https://github.com/minimact/minimact
Minimact is a lightweight, server-first reactive UI framework that compiles TSX into C#, predicts DOM changes with Rust, and syncs state in real time via SignalR β all while letting you write components with familiar hooks like useState and useEffect.
https://github.com/minimact/minimact
client-side-stored-procedures dom-patching posthydrationist predictive-rendering reactive rust-reconciliation server-side-react virtual-dom
Last synced: 20 days ago
JSON representation
Minimact is a lightweight, server-first reactive UI framework that compiles TSX into C#, predicts DOM changes with Rust, and syncs state in real time via SignalR β all while letting you write components with familiar hooks like useState and useEffect.
- Host: GitHub
- URL: https://github.com/minimact/minimact
- Owner: minimact
- License: mit
- Created: 2025-10-22T00:28:26.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2025-12-10T06:52:26.000Z (about 2 months ago)
- Last Synced: 2025-12-26T06:59:13.480Z (about 1 month ago)
- Topics: client-side-stored-procedures, dom-patching, posthydrationist, predictive-rendering, reactive, rust-reconciliation, server-side-react, virtual-dom
- Language: TypeScript
- Homepage: https://docs.minimact.com
- Size: 43.7 MB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
Minimact
The Posthydrationist Framework
Server-first React with zero hydration, predictive patches, and Rust-powered performance for ASP.NET Core.
The cactus doesn't hydrate β it stores. π΅
Theyβre treading water in a sea of hydration, clinging to their VDOM life vests while Minimact is out here desert-gliding on predictive patches like some kind of reactive dune wormπ΅
Seriously thoughβclient hydration has become the default religion in web dev, and not because it's ideal. Itβs just familiar. Youβre tossing a wrench (made of Rust, no less π¦) into that belief system and saying:
βWhat if we didnβt need to hydrate anything at all because we already know whatβs going to happen?β
Minimact brings the familiar React developer experience to server-side rendering with ASP.NET Core, powered by a Rust reconciliation engine and intelligent predictive updates.
---
## π Quick Nav
π [Quick Start](#quick-start) β’
π‘ [Why Minimact?](#why-minimact) β’
π§ [Core Innovations](#core-innovations) β’
π [SPA Mode](#-single-page-application-spa-mode) β’
π³ [Lifted State](#-lifted-state-components) β’
πͺ [Custom Hooks](#-custom-hooks-hooks-as-components) β’
π [Protected State](#-useprotectedstate) β’
π¨ [Swig IDE](#-minimact-swig---desktop-ide-for-minimact) β’
ποΈ [Architecture](#architecture-overview) β’
π [Comparison](#comparison) β’
π§ͺ [Examples](#examples)
---
## What is Minimact?
**Write React. Render on the server. Update instantly with predictive patches.**
```typescript
import { useState } from '@minimact/core';
export function Counter() {
const [count, setCount] = useState(0);
return (
setCount(count + 1)}>
Count: {count}
);
}
```
**That's it.** Write familiar React code, get server-rendered HTML with 2-3ms perceived latency.
> **The cactus doesn't hydrate β it stores.** π΅
## How It Works (in 5 seconds)
```
User clicks β
[Browser checks prediction cache] β
β
Patch found β
β±οΈ 2ms DOM update β
π Server verified in background
```
**No hydration. No diffing. Just pure speed.**
---
## π§© The Minimact Principle
```
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β DECLARATIVE UI β
β β β
β Exposes structure (JSX makes tree explicit) β
β Exposes state (useState makes slots explicit) β
β β β
β DETERMINISTIC UI β
β β β
β Same state β Same output (pure function) β
β Finite states β Enumerable space β
β β β
β PREDICTIVE UI β
β β β
β Pre-compute all outputs (build-time analysis) β
β Runtime becomes lookup (0-2ms) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
```
**The progression is INEVITABLE once you recognize:**
```
Declarative β Structure
Structure β Determinism
Determinism β Pre-computability
```
React stopped at declarative. Minimact took it all the way to predictive.
---
## π§© Minimact: Problem-Solution Matrix
| β Problem | β
Minimact's Solution | π§ͺ How It Works | π§ Why Others Struggle |
|-----------|----------------------|-----------------|----------------------|
| **1. Hydration Slowness** | No hydration at all π΅ | Predictive patches render instantly | React/Next.js re-hydrates entire DOM; costly |
| **2. Prop Drilling Hell** | Lifted State Components | Auto-lifted state with full tree access | React/Redux requires boilerplate or context abuse |
| **3. Client-side Logic Leaks** | Server-first execution | All logic runs server-side unless specified | CSR frameworks must expose logic to client |
| **4. Slow First Interaction** | ~2ms latency on first click | Precomputed patches + cache | SSR/CSR require JS boot or roundtrips |
| **5. State Loss on Hot Reload** | State survives reloads π₯ | Hex Paths + Lifted State mapping | React Fast Refresh loses local state |
| **6. Global State Bloat** | Localized, queryable state | Scoped lifted state, useProtectedState | Redux/Zustand/global context leaks over time |
| **7. Razor Lock-in for .NET Devs** | Full React DX with C# backend | TSX transpiled β C# | Blazor forces Razor syntax, slow to adopt |
| **8. DX Disconnect Between Frontend/Backend** | TS β C# type safety | Shared schema + transpilation | API routes are brittle and disjointed |
| **9. Flicker from DOM Rewrites** | Targeted micro-patches | Hex path diffing + VNull nodes | HTMX, traditional SSR re-render full nodes |
| **10. Debugging React State Is Opaque** | Visual state tree in Swig IDE | SignalR-backed live state inspector | React DevTools doesn't show prop origins or server state |
| **11. Complex Interop Between MVC + React** | Drop-in MVC embedding | MVC Controllers β ViewModels β React | React + MVC usually fight each other |
| **12. Large Bundle Sizes** | 12.0 KB runtime β‘ | Minimal SignalM WebSocket client | React 45KB+, Vue 34KB+, Blazor ~300KB |
| **13. Untrackable Component Drift** | Predictive metrics in real-time | Hit rate, rollback %, false positives | Most frameworks don't measure this at all |
| **14. CSS/DOM State Blindness** | DOM as reactive source | useDomElementState() (80+ props) | React treats DOM as opaque output |
| **15. Developer Setup Time** | 2-minute setup w/ Swig | Desktop IDE w/ hot reload, TSX editor | Next.js/Blazor setups often slow and brittle |
| **16. Poor Offline Support** | Cached patches, local state | Prediction-first runtime | SSR apps break offline, HTMX needs server |
| **17. Dev/Prod Divergence** | Single runtime model | Same prediction engine used in both | Many frameworks do hydration/dev tricks |
| **18. No Secure Way to Hide Internal State** | useProtectedState() π | Cannot be accessed by parents/devtools | Most state is inspectable if global/lifted |
| **19. Inconsistent Re-rendering from Conditions** | VNull conditional representation | Structural placeholders for predictability | React reconciler shifts indices, leading to bugs |
| **20. Implicit DOM identity issues** | Stable Hex Paths | Elements never re-identified | Key/index bugs in React are common |
---
## β¨ Why Minimact?
Traditional UI frameworks like React must reconcile every state change on the client, leading to CPU overhead and slower interactions β especially on low-end devices or in high-frequency apps.
**Minimact flips the model:**
- You write UI in **TSX/JSX**
- Minimact compiles it to **C# classes**
- C# renders the HTML on the server
- A **Rust engine predicts state changes** and pre-sends patches to the client
- Client caches predicted patches **before user interaction**
- User clicks β **Client applies cached patch instantly (0ms network latency)**
- **SignalR verifies in background** and corrects only if needed
- **No diffing, no runtime VDOM, zero client reconciliation**
### For React Developers
- β
**Familiar syntax** - Write JSX/TSX like you always have
- β
**React hooks** - `useState`, `useEffect`, `useRef`, plus powerful semantic hooks
- β
**No hydration** - No client-side JavaScript frameworks to load
- β
**Instant feedback** - Hybrid client/server state for optimal UX
### For .NET Developers
- β
**ASP.NET Core integration** - Use EF Core, dependency injection, and your favorite .NET tools
- β
**Type safety** - Full TypeScript β C# type inference
- β
**Secure by default** - Business logic stays on the server
- β
**Easy deployment** - Standard ASP.NET Core hosting
- β
**Performance:** 2-3ms interactions vs 47ms traditional SSR
### For End Users
- β
**Fast initial load** - 12.0 KB client (73% smaller than React)
- β
**Instant interactions** - Predictive updates feel native
- β
**Works without JS** - Progressive enhancement built-in
- β
**Low bandwidth** - Only patches sent over the wire
### For CTOs
**Solve the "React DX + .NET backend" problem.** One stack, one deployment, full type safety from database to DOM. Rust-powered performance makes ASP.NET Core shine.
**Comparison:**
- React 18: 45 KB gzipped
- Vue 3: 34 KB gzipped
- **Minimact: 12.0 KB gzipped** (73% smaller than React)
---
## π€ Why React Critics Were Right (Without Knowing Why)
Many developers felt React was overcomplicated β they just couldn't articulate it:
> *"Re-rendering seems wasteful."*
> *"Virtual DOM feels unnecessary."*
> *"It's declarative, but... heavy."*
Here's what they were sensing:
**π§ JSX + useState = a finite state automaton.**
- Every `useState` creates a known state space.
- JSX describes a static view for each state.
That means:
- β
You can precompute all transitions.
- π« You don't need runtime diffing or reconciliation.
**But React built a ship β Virtual DOM β to navigate a path that could have been walked directly with precomputed patches.**
Minimact is that direct path:
- **No hydration**
- **No reconciliation**
- **Finite state β Predictive patches β Instant updates (2β3ms)**
**React gave you the compass. Minimact teaches you how to use it.**
---
### Better Than Blazor
Blazor requires learning Razor syntax. Minimact uses React β the syntax millions of developers already know. Lower barrier, faster adoption, bigger talent pool.
### SSR vs CSR vs Minimact
| Feature | React (CSR) | Next.js (SSR) | Minimact (Prediction) |
|---------|-------------|---------------|----------------------|
| **First Paint** | β οΈ Depends on JS | β
Fast | β
Fast |
| **Interactivity** | β
JS required | β οΈ Re-hydration | β
Instant (2-3ms) |
| **State Sync** | π Manual | π Manual | β
Auto |
| **Bundle Size** | ~45 KB | ~45 KB | **12.0 KB** |
| **Server Logic** | β None | β οΈ API routes | β
Native C# |
| **Offline Friendly** | β
Yes | β οΈ Partial | β οΈ Prediction-only |
### Key Benefits
- β‘ **2-3ms interactions** - Predictive patches cached before user clicks
- π¦ **12.0 KB bundle** - 73% smaller than React
- ποΈ **Familiar syntax** - Write JSX/TSX with React hooks
- π **Secure by default** - Business logic stays on server
- π **15Γ faster** than traditional SSR on 3G networks
---
## Quick Start
**Minimact uses the familiar ASP.NET MVC pattern as the default architecture.** Controllers pass ViewModels to React components - zero learning curve for .NET developers.
### The Standard Pattern (MVC Bridge)
**1. Controller (C#) - Familiar MVC**
```csharp
public class ProductsController : ControllerBase
{
[HttpGet("{id}")]
public async Task Details(int id)
{
var viewModel = new ProductViewModel
{
ProductName = product.Name, // Immutable (server authority)
Price = product.Price, // Immutable (server authority)
InitialQuantity = 1 // [Mutable] (client can change)
};
return await _renderer.RenderPage(viewModel);
}
}
```
**2. Component (TSX) - Mix MVC State + Client State**
```typescript
import { useMvcState, useMvcViewModel } from '@minimact/mvc';
import { useState } from '@minimact/core';
interface ProductViewModel {
productName: string;
price: number;
isAdminRole: boolean;
initialQuantity: number; // [Mutable] in C#
}
export function ProductPage() {
// From ViewModel (server-controlled)
const [productName] = useMvcState('productName'); // Immutable
const [price] = useMvcState('price'); // Immutable
const [isAdmin] = useMvcState('isAdminRole'); // Immutable
const [quantity, setQuantity] = useMvcState('initialQuantity'); // Mutable
// Pure client state (never sent to server)
const [cartTotal, setCartTotal] = useState(0);
const [showDetails, setShowDetails] = useState(false);
const handleAddToCart = () => {
setCartTotal(price * quantity); // Client-only calculation
};
return (
{productName}
${price.toFixed(2)}
setQuantity(quantity + 1)}>
Quantity: {quantity}
Add to Cart - ${cartTotal.toFixed(2)}
{/* Server-controlled visibility */}
{isAdmin && Edit Product}
{/* Client-controlled visibility */}
setShowDetails(!showDetails)}>
{showDetails ? 'Hide' : 'Show'} Details
);
}
```
**That's it!** Standard MVC Controllers + React Components + Instant Updates (2-3ms).
**Key Pattern:**
- `useMvcState` β Bound to ViewModel property (from controller)
- `useState` β Component-owned state (not from ViewModel)
- **Both sync to server** for accurate rendering and prediction
---
## π Single Page Application (SPA) Mode
**Minimact SPA combines server-side rendering with client-side navigation for instant page transitions (10-50ms) while keeping layouts mounted.**
### What is Minimact SPA?
Traditional SPAs (React Router, Next.js) re-mount the entire app on navigation. Minimact SPA keeps your shell (header, sidebar, footer) **persistent** and only swaps the page content.
**Key Features:**
- β‘ **10-50ms navigation** - Via SignalR, not HTTP
- π― **Shell persistence** - Layouts stay mounted across navigation
- π¨ **Server-driven routing** - Controllers decide which page to render
- π¦ **12.5 KB bundle** - `@minimact/spa` package
- π **Browser history support** - Back/forward buttons work seamlessly
### Quick Start with SPA Template
```bash
# Create SPA project
swig new SPA MySpaApp
# The template includes:
# - Controllers/HomeController.cs & ProductsController.cs
# - ViewModels/HomeViewModel.cs & ProductViewModel.cs
# - Shells/MainShell.tsx (persistent layout with )
# - Pages/HomePage.tsx & ProductDetailsPage.tsx
# - Auto-installed @minimact/spa module
cd MySpaApp
swig watch # Auto-transpile
swig run # Launch app
```
### How It Works
**1. Shell Component** - Persistent layout with navigation:
```tsx
import { Page, Link } from '@minimact/spa';
import { useMvcState } from '@minimact/mvc';
export default function MainShell() {
const [appName] = useMvcState('__ShellData.AppName');
const [userName] = useMvcState('__ShellData.UserName');
return (
{appName}
Welcome, {userName}!
Home
Product 1
Product 2
{/* Pages inject here */}
Β© 2025 My App
);
}
```
**2. Controller** - Returns ViewModel with shell metadata:
```csharp
[ApiController]
[Route("products")]
public class ProductsController : ControllerBase
{
[HttpGet("{id}")]
public IActionResult Details(int id)
{
var viewModel = new ProductViewModel
{
ProductId = id,
ProductName = "Widget",
Price = 99.99m,
// Shell metadata
__Shell = "Main", // Which shell to use
__ShellData = new // Data for shell
{
AppName = "My SPA App",
UserName = "Demo User"
},
__PageTitle = "Product Details"
};
return Ok(viewModel); // β¨ Auto-extracted by SPARouteHandler
}
}
```
**3. Page Component** - Rendered inside shell:
```tsx
import { useMvcState, useState } from '@minimact/mvc';
import { Link } from '@minimact/spa';
export default function ProductDetailsPage() {
const [productName] = useMvcState('ProductName');
const [price] = useMvcState('Price');
const [quantity, setQuantity] = useState(1);
return (
β Back to Home
{productName}
${price}
setQuantity(quantity + 1)}>
Quantity: {quantity}
Next Product
);
}
```
### The Navigation Flow
```
User clicks
β
Client: SignalR.invoke('NavigateTo', '/products/2')
β
Server:
- Routes to ProductsController.Details(2)
- Extracts ViewModel
- Same shell? β Render page only
- Different shell? β Render shell + page
- Rust reconciler computes patches
β
Client: Apply patches (10-50ms!)
β
Browser: URL updated via history.pushState
β
Done! Shell stayed mounted β¨
```
### Performance Comparison
| Scenario | Traditional SPA | Minimact SPA |
|----------|----------------|--------------|
| **Same Layout Navigation** | 100-200ms (re-mount) | **10-50ms** (shell persists) β
|
| **Different Layout** | 100-200ms (re-mount) | 20-100ms (still faster) |
| **Full Page Reload** | 200-500ms | 200-500ms (same) |
| **Initial Load** | 200-500ms | 200-500ms (same) |
**Why so fast?**
- β
No JavaScript bundle parsing (already loaded)
- β
No React reconciliation (Rust does it server-side)
- β
Only DOM patches sent (not full HTML)
- β
**Shell stays mounted** (no layout re-render)
- β
SignalR WebSocket (no HTTP overhead)
### Setup (Program.cs)
```csharp
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddMinimact();
builder.Services.AddMinimactMvcBridge();
builder.Services.AddMinimactSPA(); // β¨ Enable SPA support
builder.Services.AddControllersWithViews();
builder.Services.AddSignalR();
var app = builder.Build();
app.UseStaticFiles();
// Serve mact_modules for @minimact/spa
app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(app.Environment.ContentRootPath, "mact_modules")),
RequestPath = "/mact_modules"
});
app.UseMinimact(); // Auto-discovers shells and pages
app.MapControllers();
app.MapHub("/minimact");
app.Run();
```
### Advanced Features
**Multiple Shells:**
```csharp
// Admin pages use AdminShell
if (User.IsInRole("Admin"))
viewModel.__Shell = "Admin";
// Public pages use PublicShell
else
viewModel.__Shell = "Public";
```
**No Shell (Page-Only):**
```csharp
// Landing pages, login screens
viewModel.__Shell = null; // No shell layout
```
**Conditional Rendering Based on Shell:**
```tsx
export default function ProductPage() {
const [shell] = useMvcState('__Shell');
if (shell === 'Admin') {
return ;
}
return ;
}
```
**Prefetching (Optimize for Predicted Navigation):**
```tsx
View Product
```
### Benefits
β
**10-50ms navigation** - Feels like a native app
β
**Shell persistence** - Sidebars, headers stay mounted
β
**Server-driven routing** - Controllers decide pages
β
**Auto-discovery** - Shells and pages automatically registered
β
**Browser history** - Back/forward buttons work
β
**Type-safe** - TypeScript β C# type inference
β
**Zero configuration** - Just `return Ok(viewModel)`
**[π± Complete SPA Guide β](./docs/SPA_IMPLEMENTATION_COMPLETE.md)**
### Using Minimact Swig IDE
```bash
# Clone and run Swig - the official Minimact IDE
git clone https://github.com/minimact/swig
cd swig
npm install
npm start
```
Once Swig launches:
1. **Create Project** - Click "New Project" and choose a directory
2. **Edit Components** - Write TSX in Monaco editor (auto-transpiles to C#)
3. **Build** - Click "Build" to compile your app
4. **Run** - Click "Run" and open in browser
From zero to running app in under 2 minutes.
**Modular runtime architecture:**
- `@minimact/core` β **12.0 KB gzipped** (Core runtime with SignalM WebSocket)
- `@minimact/core/r` β **23.94 KB gzipped** (Core runtime with full SignalR + fallbacks)
- `@minimact/core/hot-reload` β **+5.15 KB** (Hot reload for development)
- `@minimact/core/playground` β **+376 B** (Swig IDE integration)
- `@minimact/core/power` β **+5.37 KB** (Advanced features: useServerTask, useComputed, usePaginatedServerTask, etc.)
**π¦ Real-world examples:**
- [β
TodoMVC](./examples/todo) - Classic todo app
- [π Dashboard](./examples/dashboard) - Admin dashboard with templates
- [π Blog](./examples/blog) - Markdown blog with EF Core
- [π Forms](./examples/forms) - Validation and semantic hooks
**[π Full Getting Started Guide β](./docs/getting-started.md)**
---
## Why You'll Love Minimact
π§ **React syntax, C# backend** β No Razor needed
β‘ **Instant interactions** β <3ms click-to-DOM
π΅ **No hydration** β Predictive updates instead
π οΈ **Desktop IDE** with live state + TSX editing
𧬠**Full state tree visibility** β Perfect prediction
π **Secure by default** β Logic runs server-side
π¦ **73% smaller** than React (12.0 KB vs 45 KB)
π **Modular architecture** β Import only what you need
π₯ **Plugin system** via NuGet packages
---
## π¦ Modular Architecture - Import Only What You Need
Minimact's modular design means you only ship code your app actually uses:
### **Core Package (12.0 KB)**
```typescript
import { Minimact, useState, useEffect, useRef } from '@minimact/core';
```
Essential hooks and runtime - perfect for most apps.
### **Power Features (+5.37 KB)**
```typescript
import { useServerTask, useComputed, usePaginatedServerTask } from '@minimact/core/power';
```
Advanced features for complex apps:
- `useServerTask` - Execute async tasks on server
- `useServerReducer` - Redux-like state management
- `usePaginatedServerTask` - Built-in pagination
- `useComputed` - Client-side computation with browser APIs
- `usePub`, `useSub` - Pub/Sub messaging
- `useSignalR` - Direct SignalR access
- `useContext` - Context API for shared state
- `useMarkdown` - Render markdown
- Task scheduling hooks
### **Development Tools**
```typescript
// Enable hot reload in development
import { enableHotReload } from '@minimact/core/hot-reload'; // +5.15 KB
if (import.meta.env.DEV) {
enableHotReload();
}
// Playground bridge for Swig IDE
import { PlaygroundBridge } from '@minimact/core/playground'; // +376 B
```
Auto tree-shaken in production builds via bundler!
### **Example: Simple App**
```typescript
// Just the essentials - 12.0 KB
import { useState } from '@minimact/core';
export function Counter() {
const [count, setCount] = useState(0);
return setCount(count + 1)}>Count: {count};
}
```
### **Example: Complex App with Pagination**
```typescript
// Core + power features - 17.4 KB total
import { useState, useEffect } from '@minimact/core';
import { usePaginatedServerTask, useComputed } from '@minimact/core/power';
export function DataGrid() {
const [page, setPage] = useState(1);
const data = usePaginatedServerTask('/api/data', { page });
return
{/* Render paginated data */};
}
```
**Why This Matters:**
- β
Most apps use **12.0 KB** (just core)
- β
Complex apps add **+5.37 KB** (still smaller than competitors)
- β
Dev tools **auto tree-shake** in production
- β
No bundle bloat from unused features
---
## π¦ Zero-Config Module Management with Swig CLI
Minimact includes a **zero-config module system** for managing client-side dependencies. No CDN links, no manual script tags, no build configuration - just simple CLI commands.
### Quick Start
```bash
# Initialize modules with interactive selection
swig init
# Or install specific modules
swig import lodash
swig import @minimact/power
# List installed modules
swig list
# Update modules
swig update --all
# Remove a module
swig uninstall lodash
```
### How It Works
1. **Global Cache** - Modules download to AppData (like Swig GUI installation)
2. **Project Copy** - Copies from cache to your project's `mact_modules/`
3. **Auto-Serve** - ASP.NET Core automatically serves and includes modules
4. **Smart Control** - Use `[ModuleInfo]` attribute to optimize per-component
**Example - All modules auto-included:**
```csharp
public class MyDashboard : MinimactComponent
{
// All mact_modules/ automatically included
}
```
**Example - Opt-out for performance:**
```csharp
[ModuleInfo(OptOut = true)]
public class LandingPage : MinimactComponent
{
// Core only (12 KB), no extra modules
}
```
**Example - Selective inclusion:**
```csharp
[ModuleInfo(Include = new[] { "@minimact/power", "lodash" })]
public class DataProcessorPage : MinimactComponent
{
// Only power and lodash included
}
```
### Available Modules
**Minimact Modules:**
- `@minimact/power` - Advanced features (useServerTask, useComputed, etc.)
- `@minimact/mvc` - MVC Bridge (useMvcState, useMvcViewModel)
- `@minimact/spa` - Single Page Application (instant navigation, shell persistence)
- `@minimact/punch` - DOM state tracking (useDomElementState)
- `@minimact/md` - Markdown rendering (useMarkdown)
**External Libraries:**
- `lodash` - Utility library (24 KB)
- `moment` / `dayjs` - Date manipulation
- `axios` - HTTP client
- `chart.js` - Charting library
- ...and any npm package with a browser bundle!
### Benefits
β
**Zero Configuration** - No webpack, no bundlers, pure simplicity
β
**Offline-First** - Global cache means fast installs after first download
β
**Version Control Friendly** - Check in `mact_modules/` to Git
β
**NPM-Powered** - Uses `npm install` under the hood
β
**Automatic Integration** - ASP.NET Core auto-scans and serves modules
**[π¦ Complete Module Management Guide β](./docs/SWIG_CLI_MODULES_GUIDE.md)**
---
## Core Innovations
### π― Template Prediction System
Pre-computed parameterized patches for 100% state coverage:
```typescript
// First interaction: Extracts template "Count: {0}"
// All future clicks: Instant update with any value
Count: {count}
```
**Benefits:**
- β
100% coverage from first render (zero cold start)
- β
98% memory reduction vs cached predictions
- β
Babel extracts templates at build time
- β
Works with loops, conditionals, expressions
**[π Template System Details β](./docs/TEMPLATE_PATCH_SYSTEM.md)**
---
### π³ Lifted State Components
All child state automatically lives in parent. Zero prop drilling:
```
Dashboard
βββ UserProfile (Component)
βββ isEditing (lifted β
visible)
βββ username (lifted β
visible)
βββ cache (lifted π protected)
Access: state["UserProfile.isEditing"]
```
```typescript
// Parent sees ALL child state
function Dashboard() {
const isEditing = state["UserProfile.isEditing"]; // Just read it!
return (
);
}
// Child accesses seamlessly
function UserProfile() {
const isEditing = state.isEditing; // Auto-prefixed
setState('isEditing', true); // Updates parent!
}
```
**Benefits:**
- β
Zero prop drilling, no callbacks
- β
Parent can observe/control any child state
- β
Perfect prediction (full state tree visible)
- β
Hot reload preserves state
**[π³ Lifted State Guide β](./docs/LIFTED_STATE_COMPONENT_SYSTEM.md)**
---
### πͺ Custom Hooks (Hooks as Components)
Create reusable stateful logic with UI - hooks return both values AND JSX:
```typescript
// Define a hook (note the required namespace parameter!)
function useCounter(namespace: string, start: number = 0) {
const [count, setCount] = useState(start);
const increment = () => setCount(count + 1);
const decrement = () => setCount(count - 1);
const reset = () => setCount(start);
// Hooks can return JSX! (unlike React)
const ui = (
-
{count}
+
Reset
);
return [count, increment, decrement, reset, ui];
}
// Use the hook with multiple independent instances
function Dashboard() {
const [count1, increment1, , , counterUI1] = useCounter('counter1', 0);
const [count2, increment2, , , counterUI2] = useCounter('counter2', 10);
return (
Counter 1: {count1}
External +1
{counterUI1}
Counter 2: {count2}
External +1
{counterUI2}
);
}
```
**Under the hood:** Custom hooks are **child components with syntactic sugar**. The `useCounter('counter1', 0)` call compiles to a `VComponentWrapper` - reusing the Lifted State infrastructure with zero runtime overhead!
**Benefits:**
- β
Multiple independent instances with unique namespaces
- β
Can return JSX UI (unlike React hooks)
- β
Reuses VComponentWrapper + Lifted State Pattern
- β
Parent can observe child hook state: `State["counter1.count"]`
- β
Type-safe C# classes with `[Hook]` attribute
- β
Hot reload preserves hook state
**[πͺ Custom Hooks Guide β](./docs/CUSTOM_HOOKS_IMPLEMENTATION.md)**
---
### π useProtectedState
Lifted state with access control:
```typescript
function Counter() {
const [count, setCount] = useState(0); // Public
const [animationQueue, setQueue] = useProtectedState([]); // Protected
}
// Parent can't touch protected state!
const queue = state["Counter.animationQueue"]; // β Runtime error
```
**[π Protected State Details β](./docs/USE_PROTECTED_STATE.md)**
---
### π¨ Minimact Swig IDE
Desktop development environment with real-time component inspection:
**Features:**
- Monaco editor with full TSX support
- Auto-transpilation watch mode
- Live component state inspector
- Visual prediction analytics
- Integrated terminal and file tree
**Quick Start:**
```bash
git clone https://github.com/minimact/swig
cd swig
npm install
npm start
```
**What Makes Swig Special:**
- **Hot reload preserves state** - State lives in parent, survives reloads
- **Real-time component inspector** - See state changes as they happen via SignalR
- **Visual prediction analytics** - Green/red cache hit overlay shows prediction accuracy
- **Performance dashboard** - Hit rate, latencies, and metrics in real-time
- **Zero configuration** - From clone to running app in 2 minutes
**[π¨ Swig IDE Guide β](./docs/MINIMACT_SWIG_ELECTRON_PLAN.md)**
---
### πΉ Minimact Punch
DOM as a reactive data source - 80+ properties as state:
```typescript
const box = useDomElementState('.container');
{box.childrenCount > 5 && }
{box.isIntersecting && }
{box.vals.avg() > 100 && }
```
**[πΉ Minimact Punch Details β](./docs/USEDOMELEMENTSTATE_IMPLEMENTATION_PLAN.md)**
---
## Official Extensions
**The Minimact Quantum Stack** - Transform the DOM into a queryable, reactive, distributed database.
| Extension | Description | Key Feature |
|-----------|-------------|-------------|
| **π₯ minimact-punch** | DOM as reactive data source (80+ properties) | `useDomElementState('.card')` |
| **ποΈ minimact-query** | SQL for the DOM with full query syntax | `.from('.card').where().orderBy()` |
| **π minimact-quantum** | Multi-client DOM identity sync (7-17ms) | Mutation vectors, not data sync |
| **π― minimact-bundle** | Declarative control without wrappers | Apply styles/classes to any selector |
| **π minimact-spatial** | Viewport regions as 2D database | Query spatial areas reactively |
| **π³ minimact-trees** | Universal decision trees (0-1ms predicted) | XState but declarative & minimal |
**Philosophy:** *"The DOM is no longer a view layer - it's a queryable, reactive, distributed database."*
**[π Explore Extensions β](./extensions)**
---
## Advanced Features
### π Plugin System - Extend via NuGet
**Distribute reusable UI components as NuGet packages. Auto-discovered. Type-safe. Zero config.**
```bash
# Install plugin
dotnet add package Minimact.Plugin.Clock
# Use in TSX
```
**Benefits:**
- β
Auto-discovery via `[MinimactPlugin]` attribute
- β
Type-safe state contracts (JSON Schema validation)
- β
Versioned assets with cache optimization
- β
Works with template prediction system
- β
Server-rendered, no client JavaScript required
**[π Plugin System Guide β](./docs/PLUGIN_SYSTEM_PHASE2_COMPLETE.md)**
### More Advanced Features
| Feature | Description | Learn More |
|---------|-------------|------------|
| **useServerTask** | TypeScript β C#/Rust transpilation for async tasks | [π Docs](./docs/server-tasks.md) |
| **useContext** | Redis-like server-side cache (session/request/url scoped) | [π Docs](./docs/use-context.md) |
| **useComputed** | Client-side computation with server rendering | [π Docs](./docs/use-computed.md) |
| **MVC Bridge** | Integrate with traditional ASP.NET MVC | [π― MVC Bridge](./docs/MVC_BRIDGE_IMPLEMENTATION_PLAN.md) |
| **Semantic Hooks** | High-level abstractions (useModal, useDropdown, etc.) | [π― Hooks API](./docs/api-reference.md) |
---
## Architecture Overview
```
βββββββββββββββββββββββββββββββββββββββββββ
β Developer writes TSX with React hooks β
β β β
β Babel: TSX β C# + Extract templates β
β β β
β ASP.NET Core renders HTML β
β β β
β Rust: Predicts patches, sends to clientβ
β β β
β [Client has patches cached] β
β β β
β User clicks β 0ms (cache hit!) β
β β β
β Server verifies in background β
βββββββββββββββββββββββββββββββββββββββββββ
```
**7 Main Components:**
1. **Babel Plugin** - TSX β C# transformation
2. **C# Runtime** - ASP.NET Core integration
3. **Rust Engine** - High-performance reconciliation
4. **Client Library** - 12.0 KB modular runtime
5. **Lifted State** - Automatic state architecture
6. **Minimact Punch** - DOM state extensions
7. **Minimact Swig** - Desktop IDE
### Technical Innovations
**SignalM - Minimal WebSocket Protocol**
- Custom lightweight protocol (vs full SignalR)
- **12.0 KB** core runtime with SignalM
- **23.94 KB** core runtime with full SignalR (fallbacks for older browsers)
- Real-time bidirectional communication
- Optimized for patch delivery
**Modular Architecture**
- **Core** (12.0 KB) - Essential runtime only
- **Hot Reload** (+5.15 KB) - Development tools (auto tree-shaken in production)
- **Playground** (+376 B) - Swig IDE integration
- **Power Features** (+5.37 KB) - Advanced hooks (useServerTask, useComputed, usePaginatedServerTask, etc.)
**Hex Paths - Stable Element Identifiers**
- Hexadecimal gap-based allocation (0x10000000, 0x20000000, 0x30000000...)
- Example: "10000000.30000000.20000000" (stable IDs, not DOM indices)
- Elements keep stable IDs across insertions/deletions
- Enables precise DOM targeting without re-indexing
- PathConverter translates hex paths β DOM indices (accounts for VNull)
**VNull Nodes - Explicit Conditionals**
- Represents `{condition && }` explicitly in VNode tree
- Rust reconciler understands conditional rendering
- Patches target exact positions, not shifting indices
- Eliminates "DOM shift" bugs from traditional reconcilers
**[π Complete Architecture β](./docs/MINIMACT_COMPLETE_ARCHITECTURE.md)**
**[π Architecture Deep Dive (Substack) β](https://ameritusweb.substack.com/p/reverse-scalable-systems-achieving)** - A comprehensive analysis of Minimact's reverse scalability principles and architectural innovations.
---
## Core Technical Innovations
### Hex Paths - Stable Element Identity
Every element gets a persistent hex path ID (e.g., `1.2.F.3`). Unlike DOM indices that shift when elements are added/removed, hex paths use **gap-based allocation** to maintain stability.
**Why it matters:**
- Patches target exact elements, even as DOM changes
- Hot reload works perfectly (paths don't shift)
- Prediction system can reference elements precisely
### VNull Nodes - Explicit Conditional Rendering
Minimact represents `{condition && }` with explicit `VNull` nodes instead of omitting them from the tree.
**Why it matters:**
- PathConverter can accurately map hex paths β DOM indices
- Accounts for conditionally rendered elements
- Makes reconciliation deterministic and predictable
**[π§ Technical Deep Dive β](./docs/HEX_PATH_SYSTEM.md)**
---
## Runtime Comparison: SignalM vs SignalR
Minimact offers two runtime versions optimized for different scenarios:
| Feature | `@minimact/core` (SignalM) | `@minimact/core/r` (SignalR) |
|---------|---------------------------|------------------------------|
| **Size** | **12.0 KB gzipped** | **23.94 KB gzipped** |
| **Protocol** | Custom WebSocket | Full SignalR with fallbacks |
| **Use Case** | Modern browsers, micro-patches | IE11+, corporate networks |
| **Fallbacks** | None | Long polling, SSE, forever frame |
| **Latency** | ~2ms | ~3-5ms |
**SignalM** is a custom WebSocket protocol optimized specifically for Minimact's patch delivery system. It's 50% smaller than SignalR and designed for predictive micro-patch streaming.
**When to use SignalM:**
- Modern browser support (Chrome 90+, Firefox 88+, Safari 14+)
- Public-facing apps with controlled environments
- Maximum performance is critical
**When to use SignalR:**
- Enterprise environments with older browsers
- Corporate networks with WebSocket restrictions
- Need guaranteed compatibility
---
## Performance
| Metric | Value |
|--------|-------|
| **Initial Load** | **12.0 KB** (73% smaller than React) |
| **Time to Interactive** | < 100ms |
| **Interaction Latency** | ~2-5ms (with prediction) |
| **Cache Hit Rate** | 95-98% (after warmup) |
| **Memory vs Caching** | 98% reduction (templates vs concrete patches) |
**[π Benchmarks β](./docs/benchmarks.md)**
---
## Comparison
| Feature | Minimact | Next.js | Blazor Server | HTMX |
|---------|----------|---------|---------------|------|
| **Bundle Size** | **12.0 KB** | ~45 KB | ~300 KB | ~14 KB |
| **Syntax** | React JSX | React JSX | Razor C# | HTML attrs |
| **Hydration** | None | Required | None | None |
| **Update Model** | **Predictive (client)** | Reactive (client) | Reactive (server) | **Triggered (server)** |
| **Prediction** | β
Rust | β | β | β |
| **Hybrid State** | β
| β | β | Manual |
| **Type Safety** | β
TSβC# | β
TS | β
C# | β |
| **Client Interactivity** | β
Full | β
Full | β
Full | β Server-only |
| **Modular** | β
Opt-in features | β All-in-one | β All-in-one | β
Minimal |
### Why Minimact > HTMX
**HTMX is server-triggered** (user clicks β server responds β HTML swaps)
**Minimact is predictively client-updated** (patches pre-cached β instant apply)
**Key differences:**
- β
**No flicker** - Patches already cached, apply instantly
- β
**No hydration** - But full client interactivity (unlike HTMX)
- β
**Optimistic updates** - UI responds before server confirms
- β
**95-98% instant** - Most interactions hit cache, no network wait
- β
**React DX** - Familiar hooks and component model
**HTMX use case:** Simple server-driven apps, minimal JS
**Minimact use case:** Complex UIs with instant feedback and React DX
---
## Project Status
**Current Phase:** Production-Ready Core + Advanced Features β
### Recently Completed (2025)
- β
Template Prediction System (Phases 1-9)
- β
Lifted State Component System
- β
useProtectedState Hook
- β
Custom Hooks (Hooks as Child Components with UI return)
- β
Minimact Swig IDE
- β
Minimact Punch (Base Features)
- β
State Synchronization (client β server)
- β
Mact Modules System (Zero-config module management with Swig CLI)
- β
SPA Mode (Single Page Application with shell persistence and 10-50ms navigation)
### In Progress
- π§ Minimact Punch Advanced Features (Parts 2-5)
- π§ Semantic Hooks Library
**[π Full Status & Roadmap β](./docs/roadmap.md)**
---
## Examples
- **[Todo App](./examples/todo)** - Classic TodoMVC
- **[Blog](./examples/blog)** - Markdown blog with EF Core
- **[Dashboard](./examples/dashboard)** - Admin dashboard with templates
- **[Forms](./examples/forms)** - Validation and semantic hooks
---
## Documentation
π **[docs.minimact.com](https://docs.minimact.com)** - Complete guides and API reference
### Quick Links
- [Getting Started](./docs/getting-started.md)
- [Architecture Overview](./docs/MINIMACT_COMPLETE_ARCHITECTURE.md)
- [API Reference](./docs/api-reference.md)
- [Babel Plugin Guide](./docs/babel-plugin.md)
- [Deployment Guide](./docs/deployment.md)
---
## π΅ The Mactic Experience
```
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β π PRESENTING π β
β β
β ββββ ββββ ββββββ βββββββββββββββββββ βββββββ β
β βββββ βββββββββββββββββββββββββββββββββββββββββ β
β ββββββββββββββββββββββ βββ ββββββ β
β ββββββββββββββββββββββ βββ ββββββ β
β βββ βββ ββββββ βββββββββββ βββ βββββββββββ β
β βββ ββββββ βββ βββββββ βββ βββ βββββββ β
β β
β THE MUSICAL β
β β
β "A WHOLE NEW WORLD OF RENDERING" β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
π΅ A WHOLE NEW WORLD π΅
(Minimact Edition)
I can show you the world
Shining, shimmering, mactic
Tell me, developer, now when did
You last let hydration go?
I can open your eyes
Take you render by render
Over, sideways and under
On a mactic carpet ride
A whole new world
A new fantastic point of view
No one to tell us "no"
Or where to go
Or say we're only hydrating
A whole new world
A dazzling place I never knew
But when I'm way up here
It's crystal clear
That now I'm in a whole new world with you
(Now I'm in a whole new world with Minimact)
Unbelievable sights
Indescribable feeling
Soaring, flying, wheeling
Through an endless server-side sky
A whole new world
(Don't you dare close your eyes)
A hundred thousand things to see
(Hold your breath, it gets better)
I'm like a shooting star
I've come so far
I can't go back to where I used to be
A whole new world
With new horizons to pursue
I'll chase them anywhere
There's time to spare
Let me share this whole new world with you
π΅β¨ Minimact: Where every navigation is a mactic carpet ride β¨π΅
```
**The Mactic Lexicon:**
- **Mactnificent** (adj.) - Magnificent, but for Minimact
- **Mactical** (adj.) - So magical it feels like server-side sorcery
- **Mactic** (adj.) - When patches predict your clicks before you make them
*It's not magic. It's mactic.* πͺ
**[π Experience the Full Musical β](./docs/MUSICAL.md)**
**The Complete Setlist:**
1. π΅ "Part of Your World" - The Developer's Lament
2. π΅ "Under the Server" - Life below the client
3. π΅ "Let It Go (The Hydration)" - Embracing server-side rendering
4. π΅ "How Far I'll Go" - Avoiding client-side rendering
5. π΅ "You're Welcome" - From SignalR
6. π΅ "Circle of Life" - The Component Lifecycle
7. π΅ **"A Whole New World"** - The Mactic Experience
8. π΅ "One Day More" - Until Production
*Plus: The Minimact Cinematic Universe, MinimactLand Theme Park, and more!* πβ¨
---
## Why the Name Minimact?
**Minimact** stands for **MINIMal Anticipatory Client Technology**.
- **Minimal** β Tiny 12.0 KB runtime, minimal client logic
- **Anticipatory** β Predictive patches pre-sent before user interaction
- **Client Technology** β Smart client that applies cached patches instantly
And yes β the cactus π΅ doesn't hydrate. It stores.
---
## Contributing
We welcome contributions! See [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines.
**Join the discussion:**
- [GitHub Discussions](https://github.com/minimact/minimact/discussions)
- [Discord Server](https://discord.gg/EKPDh6v7)
---
## License
MIT License - see [LICENSE](./LICENSE) for details
---
## Acknowledgments
Inspired by **React**, **Blazor**, **HTMX**, **Vue**, and **SolidJS**.
Built with **Rust**, **ASP.NET Core**, **Babel**, and **TypeScript**.
---
Built with β€οΈ for the .NET and React communities
β Star this repo if you're interested in server-side React for .NET!