Skip to content

Getting Started ​

Hey Minimalists! 👋

Welcome to Minimact - the server-side React framework that eliminates hydration and delivers instant interactivity. This guide will help you set up your first application with ASP.NET Core and predictive rendering.

New to Minimact?

A Minimalist is a developer who uses Minimact - someone who embraces minimal code, maximum control, and DOM domination. Check out the Glossary for all Minimact terminology.

Prerequisites ​

Quick Start ​

1. Download Minimact Swig ​

Swig is the official IDE for Minimact development. It provides a visual interface for creating projects, editing code, and running your apps.

bash
git clone https://github.com/minimact/swig
cd swig/swig
npm install
npm start

The Swig IDE will launch automatically! 🎉

2. Create a New Project ​

In the Swig IDE:

  1. Click "Create New Project"
  2. Choose a directory for your project
  3. Select a template:
    • Empty — Minimal starter
    • Todo App — Simple CRUD example
    • Electron File Manager — Desktop app with native features
  4. Click "Create"

Swig will scaffold your project with:

MyApp/
├── Program.cs              # ASP.NET Core startup
├── MyApp.csproj            # .NET project file
├── Controllers/            # ASP.NET Core MVC controllers
│   └── HomeController.cs   # Route handler
├── Pages/                  # Minimact TSX pages
│   ├── HomePage.tsx        # Homepage component
│   └── HomePage.cs         # Auto-generated C# (do not edit)
└── Components/             # Shared components
    ├── Counter.tsx
    └── Counter.cs          # Auto-generated C# (do not edit)

3. Start Development ​

In Swig:

  1. Click "Transpile" to convert TSX → C#
  2. Click "Build" to compile the project
  3. Click "Run" to start your app
  4. Click "Open in Browser" to view it

That's it! Your app is live at http://localhost:5000

4. Edit Your First Page ​

In the Swig editor, open Pages/HomePage.tsx:

tsx
import { useState } from '@minimact/core';

export default function HomePage() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <h1>Welcome to Minimact!</h1>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

Save the file (Ctrl+S) — Swig will:

  1. Auto-transpile to C# in Pages/HomePage.cs (same directory)
  2. Generate template metadata in HomePage.templates.json
  3. Hot reload your browser with the changes
  4. Preserve your component state — the counter won't reset!

Controller-Based Routing ​

Minimact uses ASP.NET Core MVC routing via controllers. Controllers handle routes and render Minimact pages.

Basic Setup ​

Controllers/HomeController.cs:

csharp
using Microsoft.AspNetCore.Mvc;
using Minimact.AspNetCore.Rendering;

[ApiController]
[Route("")]
public class HomeController : ControllerBase
{
    private readonly MinimactPageRenderer _renderer;

    public HomeController(MinimactPageRenderer renderer)
    {
        _renderer = renderer;
    }

    [HttpGet]
    public async Task<IActionResult> Index()
    {
        return await _renderer.RenderPage<HomePage>();
    }
}

Route Examples ​

csharp
// Root route (/)
[Route("")]
[HttpGet]
public async Task<IActionResult> Index()

// About page (/about)
[Route("about")]
[HttpGet]
public async Task<IActionResult> About()

// Blog post with ID (/blog/{id})
[Route("blog")]
[HttpGet("{id}")]
public async Task<IActionResult> Post(int id)

// User profile (/users/{userId}/profile)
[Route("users")]
[HttpGet("{userId}/profile")]
public async Task<IActionResult> Profile(int userId)

Using Swig CLI ​

Add pages quickly with the CLI:

bash
# Add a page at root
npx @minimact/swig add HomePage "/"

# Add a page with route parameter
npx @minimact/swig add ProductDetails "Products/{id}"

# Add a page with MVC ViewModel
npx @minimact/swig add UserProfile "Users/{userId}" --mvc

This generates the controller, page component, and ViewModel (if --mvc flag is used).


Swig IDE Features ​

Monaco Editor ​

The built-in Monaco editor provides:

  • Full TypeScript/TSX syntax highlighting
  • IntelliSense and autocomplete
  • Error checking and linting
  • Multi-file editing

Real-Time Transpilation ​

As you type:

  1. Swig watches for file changes
  2. Auto-transpiles TSX → C# on save
  3. Generates template metadata
  4. Updates the generated C# code in real-time

Integrated Terminal ​

Run commands directly in Swig:

  • dotnet build to compile
  • dotnet run to start the server
  • View build output and errors

Live Component Inspector ​

While your app is running, Swig connects via SignalR to show:

  • Component Tree — Visual hierarchy of active components
  • State Inspector — Real-time state values
  • Template Viewer — See the generated templates

Creating Components ​

In Swig, create a new component:

  1. Right-click in the File Tree
  2. Select "New Component"
  3. Enter name: Counter.tsx

Components/Counter.tsx:

tsx
import { useState } from '@minimact/core';

export function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div className="counter">
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        Increment
      </button>
      <button onClick={() => setCount(count - 1)}>
        Decrement
      </button>
      <button onClick={() => setCount(0)}>
        Reset
      </button>
    </div>
  );
}

Use it in a page:

tsx
import { Counter } from '../Components/Counter';

export default function HomePage() {
  return (
    <div>
      <h1>Counter Demo</h1>
      <Counter />
    </div>
  );
}

Swig will auto-transpile both files when you save!


Project Structure ​

Minimact projects created with Swig have a clean structure:

MyApp/
├── Program.cs              # ASP.NET Core startup
├── MyApp.csproj            # .NET project file
├── Controllers/            # ASP.NET Core MVC controllers
│   ├── HomeController.cs   # Route: / (you edit this)
│   └── AboutController.cs  # Route: /about
├── Pages/                  # Minimact TSX pages
│   ├── HomePage.tsx        # Homepage component (you edit this)
│   ├── HomePage.cs         # Auto-generated C# (do not edit)
│   ├── HomePage.templates.json  # Template metadata
│   ├── AboutPage.tsx       # About page component
│   ├── AboutPage.cs        # Auto-generated C#
│   └── AboutPage.templates.json
└── Components/             # Shared components
    ├── Counter.tsx         # Reusable component (you edit this)
    ├── Counter.cs          # Auto-generated C# (do not edit)
    └── Counter.templates.json

Generated Files ​

C# files (.cs) are auto-generated in the same directory as your TSX files. Do not edit them manually — Swig overwrites them when you save the corresponding .tsx file.

Template files (.templates.json) contain the extracted template metadata used for hot reload and predictive rendering.

Swig shows generated files in a separate pane so you can see the C# output in real-time!


Configuring Routes ​

Routes are configured via ASP.NET Core MVC controllers:

Program.cs:

csharp
using Minimact.AspNetCore;

var builder = WebApplication.CreateBuilder(args);

// Add Minimact services
builder.Services.AddMinimact();

// Add MVC controllers
builder.Services.AddControllers();

var app = builder.Build();

// Serve static files (CSS, JS, images)
app.UseStaticFiles();

// Map SignalR hub for real-time communication
app.MapHub<MinimactHub>("/minimacthub");

// Map MVC controllers
app.MapControllers();

app.Run();

How it works:

  1. Swig transpiles Pages/HomePage.tsx → Pages/HomePage.cs
  2. Controllers use MinimactPageRenderer to render pages
  3. Routes are defined using standard MVC routing attributes:
    csharp
    [Route("")]           // Root: /
    [Route("about")]      // About: /about
    [Route("blog/{id}")]  // Blog post: /blog/123

Server State vs Client State ​

Server State (useState) ​

State managed on the server, synchronized to the client:

tsx
import { useState } from '@minimact/core';

export function UserProfile() {
  // Runs on server, syncs to client
  const [user, setUser] = useState({ name: 'Alice', age: 30 });

  return (
    <div>
      <h2>{user.name}</h2>
      <button onClick={() => setUser({ ...user, age: user.age + 1 })}>
        Birthday!
      </button>
    </div>
  );
}

When to use useState:

  • All interactive state (buttons, inputs, toggles)
  • Data from server/database
  • State that needs to persist across renders
  • Any state that affects rendering logic

Note: All state in Minimact is server-managed and synced to the client. This ensures consistent rendering and prevents stale data issues.


Server Tasks ​

Run long-running operations on the server with progress updates:

tsx
import { useServerTask } from '@minimact/core';

export function DataProcessor() {
  const [task, startTask] = useServerTask(async (updateProgress) => {
    for (let i = 0; i <= 100; i += 10) {
      await delay(500);
      updateProgress(i);
    }
    return 'Processing complete!';
  });

  return (
    <div>
      <button onClick={startTask} disabled={task.isRunning}>
        Start Processing
      </button>

      {task.isRunning && <p>Progress: {task.progress}%</p>}
      {task.isComplete && <p>Result: {task.result}</p>}
    </div>
  );
}

Template-Based Hot Reload ​

Minimact's hot reload is faster and more efficient than React Fast Refresh because it uses the template system.

How It Works ​

1. Build Time — Template Extraction

When you save a TSX file, Babel extracts templates:

tsx
// You write:
<h1>Count: {count}</h1>

// Babel generates Counter.templates.json:
{
  "component": "Counter",
  "templates": {
    "h1[0].text[0]": {
      "template": "Count: {0}",
      "bindings": ["count"],
      "slots": [7],
      "path": [0, 0]
    }
  }
}

2. Development — File Watching

You edit Counter.tsx
    ↓
CLI transpiles TSX → C#
    ↓
Babel regenerates Counter.templates.json
    ↓
FileSystemWatcher detects change
    ↓
TemplateHotReloadManager compares old vs new templates
    ↓
Detects: "Count: {0}" changed to "Total: {0}"

3. Runtime — Template Patching

Server sends template patch via SignalR:
{
  "type": "template-patch",
  "componentId": "counter-123",
  "path": [0, 0],
  "template": "Total: {0}",
  "params": [5],  // Current count value
  "bindings": ["count"]
}
    ↓
Client receives patch
    ↓
Client applies template with current state
    ↓
DOM updates instantly: "Total: 5"
    ↓
Component state preserved!

Benefits Over React Fast Refresh ​

FeatureReact Fast RefreshMinimact Hot Reload
SizeFull component + VDOM (~100KB)Template only (~2KB)
SpeedRe-render + reconcile (~16ms)Template application (~1ms)
State✅ Preserved (usually)✅ Always preserved
Coverage70-80% (some changes require reload)100% (all text/attribute changes)
NetworkFull component codeJust template diff

What Gets Hot Reloaded ​

✅ All text changes:

tsx
// Change "Hello" to "Hi" → Hot reloaded
<h1>Hello World</h1>
<h1>Hi World</h1>

✅ All attribute changes:

tsx
// Change className → Hot reloaded
<div className="blue">Content</div>
<div className="red">Content</div>

✅ Template structure:

tsx
// Add/remove placeholders → Hot reloaded
<p>Count: {count}</p>
<p>Count: {count} / Max: {max}</p>

⚠️ Component structure changes require reload:

tsx
// Adding new elements requires full transpile
<div>Old</div>
<div><span>New child element</span></div>

Watching Template Files ​

The hot reload manager watches for .templates.json changes:

Generated/
├── pages/
│   ├── Index.cs
│   └── Index.templates.json     ← Watched
├── components/
│   ├── Counter.cs
│   └── Counter.templates.json   ← Watched
└── routes.json

File watcher configuration:

  • Path: Generated/ folder
  • Filter: *.templates.json
  • Debounce: 50ms (prevents duplicate triggers)

State Preservation ​

Unlike React Fast Refresh, Minimact never loses state because:

  1. Templates are parameterized — Component state fills the template
  2. No re-rendering — Client just updates text/attributes
  3. Component instance stays alive — Server maintains state

Example:

tsx
export function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>Count: {count}</p>  {/* Change this text */}
      <button onClick={() => setCount(count + 1)}>+</button>
    </div>
  );
}

User clicks button 5 times → count = 5

You edit: <p>Count: {count}</p> → <p>Total: {count}</p>

Result: Browser shows "Total: 5" — count is still 5!

Memory Efficiency ​

Traditional hot reload:

Component with 100 possible states:
100 states × 1KB each = 100KB in memory

Minimact template hot reload:

Component with 100 possible states:
1 template × 200 bytes = 200 bytes in memory
→ 500x memory reduction

Debugging Hot Reload ​

Enable logging to see what's happening:

csharp
// Program.cs
builder.Services.AddMinimact(options =>
{
    options.EnableHotReload = true;
    options.HotReloadLogging = true;  // Enable debug logs
});

Console output:

[Minimact Templates] 📦 Watching Generated/ for *.templates.json changes
[Minimact Templates] 📝 Template file changed: Counter.templates.json
[Minimact Templates] 🚀 Sent template patch for counter-123: "Count: {0}" → "Total: {0}"

Browser console:

[Minimact HotReload] Received template patch for counter-123
[Minimact HotReload] Applied patch to path [0, 0]: "Total: 5"

Next Steps ​

Now that you have Minimact set up:


Troubleshooting ​

Swig won't start ​

If Swig fails to launch:

  1. Ensure Node.js 18+ is installed: node --version
  2. Reinstall dependencies:
    bash
    cd swig/swig
    rm -rf node_modules
    npm install
    npm start

Routes not found ​

If routes aren't working:

  1. In Swig, click "Transpile" to regenerate routes
  2. Check that .cs files exist next to your .tsx files
  3. Ensure MapMinimactPages() is called in Program.cs

Transpiler not working ​

If file changes aren't detected in Swig:

  1. Check that files have .tsx extension
  2. Look for errors in the Swig terminal output
  3. Try clicking "Transpile" manually

Port already in use ​

If port 5000 is taken:

  1. In Swig terminal, run:
    bash
    dotnet run --urls "http://localhost:5001"
  2. Or stop the other process using port 5000

Bundle Sizes ​

Minimact is available in two versions:

VersionTransportBundle Size (gzipped)Browser Support
minimactSignalM (WebSocket + JSON)12.01 KBModern browsers (95%+ users)
minimact-rSignalR (Full client)25.03 KBAll browsers + legacy support

Comparison to Other Frameworks ​

FrameworkSize (gzipped)vs Minimact
React 1845 KB71% larger
Vue 334 KB155% larger
Minimact12.01 KB✅ Default
Minimact-R25.03 KB88% larger (still 44% smaller than React)

When to Use Each Version ​

Use minimact (default) if:

  • ✅ You're building for modern browsers (Chrome 51+, Firefox 54+, Safari 10+, Edge 15+)
  • ✅ You want the smallest possible bundle
  • ✅ You only need WebSocket transport
  • ✅ 95%+ of users have WebSocket support

Use minimact-r if:

  • ✅ You need enterprise compatibility (old proxies, firewalls)
  • ✅ You need transport fallback (SSE, Long Polling)
  • ✅ You want to use the useSignalR hook for custom hubs
  • ✅ You need MessagePack protocol support

Migration is seamless — just swap the package name. No code changes required!


Advanced Features in Swig ​

Template Inspector ​

View the extracted templates from your components:

  1. Click "Inspector" tab
  2. Select a component from the tree
  3. See all extracted templates with bindings
  4. Filter by type: static, dynamic, conditional, loop

File Watcher ​

Swig automatically watches for file changes and rebuilds:

  • TSX files → Transpiles to C#
  • Template changes → Hot reloads browser
  • C# files → Triggers dotnet build

Project Templates ​

Swig includes several starter templates:

  • Empty — Minimal Minimact project
  • Todo App — CRUD operations example
  • Electron File Manager — Desktop app with native APIs
  • Dashboard — Admin panel with charts

🌵 You're ready to build with Minimact and Swig! 🌵

For advanced CLI usage (optional), see the CLI Reference.

Released under the MIT License.