Visual documentation for Minimact and Minimact Punch
About These Diagrams
This page contains comprehensive visual documentation for the Minimact architecture,
showing how components interact, data flows, messaging patterns, and the integration
of the Minimact Punch extension.
๐ฏ Core Architecture
System components from TSX to DOM
โก Data Flows
Request/response cycles and messaging
๐ฎ Predictions
Cache hit/miss logic and hint queues
๐ State Sync
NEW auto-sync pattern preventing stale data
๐ Lifecycle NEW
Initial SSR through SignalR connection
๐ง Learning NEW
Pattern detection and confidence scoring
๐ง Babel NEW
TSX โ C# transformation with examples
๐ Security NEW
Multi-layer authorization and validation
โก Hot Reload NEW
Template-based 3-5ms updates, 98% less memory
๐ป Developer Workflow NEW
Setup, dev loop, debugging, integrations
Minimact Core Architecture
System components showing all layers from TSX development to DOM rendering,
including the Babel compilation pipeline, ASP.NET Core runtime, Rust reconciliation engine,
and client-side SignalR integration.
Sequence diagram showing the complete request/response cycle, from initial render
through predictive pre-caching to user interactions with both cache hits and misses.
sequenceDiagram
participant User
participant DOM
participant EventDel as Event Delegation
participant SignalR as SignalR Manager
participant Hub as MinimactHub
participant Comp as Component
participant Rust as Rust Engine
participant Predictor
participant Queue as Hint Queue
participant Patcher as DOM Patcher
Note over User,Patcher: Initial Render
Comp->>Rust: Render() โ VNode
Rust->>Rust: Generate patches
Rust->>Hub: Send patches
Hub->>SignalR: ApplyPatches
SignalR->>Patcher: Apply to DOM
Patcher->>DOM: Update elements
Note over User,Patcher: Predictive Pre-Caching
Predictor->>Rust: Predict state changes
Rust->>Rust: Pre-compute patches
Rust->>Hub: Send predictions
Hub->>SignalR: QueueHint
SignalR->>Queue: Cache patches
Queue-->>Queue: Ready for instant use
Note over User,Patcher: User Interaction (Cache Hit)
User->>DOM: Click button
DOM->>EventDel: Capture event
EventDel->>Queue: Check for hint
Queue-->>Queue: ๐ข CACHE HIT!
Queue->>Patcher: Apply cached patches
Patcher->>DOM: Instant update (0ms)
EventDel->>SignalR: Notify server (background)
SignalR->>Hub: InvokeComponentMethod
Hub->>Comp: Call method
Comp->>Comp: Update state
Comp->>Rust: Re-render
Rust->>Hub: Verify patches
Hub->>SignalR: ApplyCorrection (if needed)
Note over User,Patcher: User Interaction (Cache Miss)
User->>DOM: Click button
DOM->>EventDel: Capture event
EventDel->>Queue: Check for hint
Queue-->>Queue: ๐ด CACHE MISS
EventDel->>SignalR: Invoke method
SignalR->>Hub: InvokeComponentMethod
Hub->>Comp: Call method
Comp->>Comp: Update state
Comp->>Rust: Re-render
Rust->>Rust: Compute patches
Rust->>Hub: Send patches
Hub->>SignalR: ApplyPatches
SignalR->>Patcher: Apply to DOM
Patcher->>DOM: Update (~45ms latency)
Predictive Rendering Pipeline
Flowchart showing how the prediction engine determines whether to send predictions,
the confidence threshold, and the verification/correction flow.
Critical Update: The new auto-sync pattern ensures client state changes
are immediately synchronized to the server, preventing stale data issues that could cause
the Rust reconciler to overwrite client updates.
sequenceDiagram
participant User
participant Client as Client Hook (useState)
participant SignalR as SignalR Manager
participant Hub as MinimactHub
participant Comp as MinimactComponent
participant Rust as Rust Reconciler
Note over User,Rust: NEW: Automatic State Sync
User->>Client: setCount(5)
Client->>Client: Update local state
par Check Hint Queue
Client->>Client: Check HintQueue
alt Cache Hit
Client->>Client: Apply cached patches (instant)
else Cache Miss
Client->>Client: No instant feedback
end
and Sync to Server
Client->>SignalR: updateComponentState(componentId, "count", 5)
SignalR->>Hub: UpdateComponentState
Hub->>Comp: SetStateFromClient("count", 5)
Comp->>Comp: Update internal state
Comp->>Rust: TriggerRender()
Rust->>Rust: Compute patches
Rust->>Hub: Patches ready
Hub->>SignalR: ApplyPatches (verification)
SignalR->>Client: Patches applied
end
Note over User,Rust: Result: Server always has correct state!
Minimact Punch Architecture ๐น
useDomElementState integration showing how DOM observers
feed into the reactive state system, enabling DOM-driven predictions.
End-to-end architecture showing all components from developer experience
through build-time compilation, server runtime, Rust engine, SignalR layer,
client runtime, and Minimact Punch extension.
Latency comparison showing the performance benefits of cache hits in both
traditional Minimact and Minimact Punch scenarios.
Traditional SSR
~52ms total latency (network round-trip + processing)
Minimact Cache Hit
~2ms total latency (instant patch application)
Minimact Cache Miss
~52ms total latency (falls back to server render)
Minimact Punch Hit
~2ms total latency (DOM observer + instant patch)
Component Lifecycle & Initial Load
Initial Page Load Flow
How does the initial page load work? This diagram shows the progression from
SSR โ HTML โ SignalR connection upgrade, demonstrating how the page works
without JavaScript and is progressively enhanced with JavaScript.
sequenceDiagram
participant Browser
participant Server as ASP.NET Core
participant Rust as Rust Engine
participant Component
participant SignalR as SignalR Hub
participant Client as Client Runtime
Note over Browser,Client: 1. Initial SSR (No JavaScript Yet)
Browser->>Server: HTTP GET /page
Server->>Component: Create instance
Component->>Component: OnInitializedAsync()
Component->>Rust: Render() โ VNode
Rust->>Rust: Generate HTML
Rust->>Server: HTML string
Server->>Browser: Return HTML page
Browser->>Browser: Display HTML immediately
Note over Browser,Client: 2. Progressive Enhancement (JavaScript Loads)
Browser->>Browser: Parse & execute ~5KB client.js
Browser->>Client: Initialize Minimact client
Client->>SignalR: Establish WebSocket connection
Note over Browser,Client: 3. Component Registration
SignalR->>Server: Connection established
Server->>SignalR: Assign ConnectionId
Client->>Client: Query DOM for [data-component-id]
Client->>SignalR: RegisterComponent(componentId)
SignalR->>Server: Store mapping: ConnectionId โ ComponentId
Server->>Component: Attach connection to instance
Note over Browser,Client: 4. Ready State
Component->>Rust: Generate initial predictions
Rust->>SignalR: QueueHint messages
SignalR->>Client: Cache predictions
Client->>Client: System ready - instant interactions enabled
Note over Browser,Client: Result: Page works WITHOUT JS, enhanced WITH JS
Component ID Tracking
Components are identified and tracked between server and client using GUIDs
embedded in data-component-id attributes.
graph LR
subgraph "Server-Side Rendering"
COMP[Component Instance]
ID[Generate GUID]
HTML[Rendered HTML]
end
subgraph "HTML Output"
ATTR[data-component-id attribute]
end
subgraph "Client-Side Discovery"
DOM[DOM Query]
MAP[ComponentId โ Element Map]
end
subgraph "Server Registry"
REGISTRY[Component Registry]
CONN[ConnectionId โ Component Map]
end
COMP --> ID
ID --> ATTR
ATTR --> HTML
HTML --> DOM
DOM --> MAP
MAP --> REGISTRY
REGISTRY --> CONN
style ATTR fill:#FFD700
style MAP fill:#90EE90
Prediction Engine Deep Dive
Learning & Pattern Detection
The prediction engine learns patterns through frequency analysis, sequence detection,
and conditional pattern recognition. It assigns confidence scores and only sends
predictions with โฅ70% confidence.
The Template Patch System revolutionizes predictive rendering by generating parameterized patch templates
at build time through Babel AST analysis. This provides 100% coverage of all possible state
values with zero learning phase.
At runtime, when state changes, the client fills template slots with actual values for instant updates.
No server round-trip needed!
sequenceDiagram
participant User
participant Client
participant Templates as Template Cache
participant DOM
participant SignalR
participant Server
Note over User,Server: Initial Page Load
Server->>Server: Render() โ VNode tree
Server->>Server: Extract templates from [LoopTemplate] attributes
Server->>Templates: Send all templates + initial state
Templates->>Templates: Cache templates by componentId + nodePath
Note over User,Server: User Interaction (Instant Update)
User->>Client: Click "Increment"
Client->>Client: useState: setCount(count + 1)
Client->>Templates: getTemplate(componentId, nodePath)
Templates-->>Client: { template: "Count: {0}", bindings: ["count"] }
Client->>Client: Fill slots: "Count: {0}" โ "Count: 5"
Client->>DOM: Update text node
Note right of DOM: โจ 0ms! Instant update
Client->>SignalR: updateComponentState("count", 5)
SignalR->>Server: Sync state
Server->>Server: SetStateFromClient("count", 5)
Note right of Server: Server state in sync! Prevents stale data
Note over User,Server: Complex State Change (Server Render)
User->>Client: Add new todo item
Client->>SignalR: invokeMethod("addTodo", ...)
SignalR->>Server: Execute addTodo()
Server->>Server: todos.Add(newTodo)
Server->>Server: Render() โ New VNode
Server->>Server: Rust reconciler diffs
Server->>Client: Send patches
Client->>DOM: Apply patches
Note right of DOM: Server handles complex logic
Template vs Hint Queue Comparison
graph TB
subgraph "OLD: Hint Queue System"
A1[User Action] --> B1[Server pre-computes specific predictions]
B1 --> C1[Cache patches for count=0โ1, count=1โ2, etc.]
C1 --> D1[Limited coverage N predicted values]
D1 --> E1[Learning phase required]
E1 --> F1[Cache misses possible]
end
subgraph "NEW: Template Patch System"
A2[Build Time] --> B2[Babel extracts templates from JSX AST]
B2 --> C2[Single parameterized template 'Count: {0}']
C2 --> D2[100% coverage ALL possible values]
D2 --> E2[Zero learning phase]
E2 --> F2[No cache misses ever]
end
style A1 fill:#fca5a5
style F1 fill:#fca5a5
style A2 fill:#86efac
style F2 fill:#86efac
๐ Plugin System Architecture NEW
Overview
Minimact's Plugin System enables 100% server-side plugins distributed as NuGet packages.
Plugins have zero client bundle overhead and leverage the Template Patch System
for instant client updates.
flowchart TD
subgraph "Plugin Development"
A[Create C# Class] --> B[Implement IMinimactPlugin]
B --> C[Add [MinimactPlugin] attribute]
C --> D[Define state interface/class]
D --> E[Implement Render method]
E --> F[Add embedded assets CSS, JS, images]
F --> G[Package as NuGet]
end
subgraph "Plugin Distribution"
G --> H[Publish to NuGet.org]
H --> I[Developer: dotnet add package]
end
subgraph "Plugin Discovery"
I --> J[App starts]
J --> K[PluginManager.AutoDiscover]
K --> L[Scan assemblies for [MinimactPlugin]]
L --> M[Register plugins]
M --> N[Load embedded assets]
end
subgraph "Runtime Usage"
N --> O[<Plugin name='Clock' state={...} />]
O --> P[Babel transforms to new PluginNode]
P --> Q[Server renders plugin]
Q --> R[Client applies templates]
end
style G fill:#3b82f6
style H fill:#10b981
style M fill:#8b5cf6
style R fill:#f59e0b
Plugin Rendering Flow
From JSX to rendered output, showing how plugins integrate seamlessly with Minimact's architecture.
sequenceDiagram
participant Dev as Developer
participant Babel as Babel Plugin
participant Server as ASP.NET Server
participant PluginMgr as PluginManager
participant Plugin as Clock Plugin
participant Rust as Rust Reconciler
participant Client as Browser
Note over Dev,Client: Build Time
Dev->>Babel: <Plugin name="Clock" state={time} />
Babel->>Babel: Detect Plugin JSX tag
Babel->>Babel: Transform to C# code: new PluginNode("Clock", stateObj)
Babel->>Server: Generate Component.cs
Note over Dev,Client: Runtime - Initial Render
Client->>Server: Request page
Server->>Server: Component.Render()
Server->>Server: VNode tree includes PluginNode("Clock", {...})
Server->>PluginMgr: RenderPlugin("Clock", state)
PluginMgr->>PluginMgr: Validate state schema
PluginMgr->>Plugin: Plugin.Render(state)
Plugin-->>PluginMgr: VNode tree (div > h2 + p)
PluginMgr-->>Server: VNode tree
Server->>Rust: Reconcile(oldVNode, newVNode)
Rust-->>Server: DOM patches
Server->>Client: Send patches + templates
Client->>Client: Apply patches
Client->>Client: Cache plugin templates
Note over Dev,Client: State Update
Client->>Client: time changes (1:23 โ 1:24)
Client->>Client: Template filling: "{0}:{1} {2}" with [1,24,"PM"]
Client->>Client: Update DOM instantly
Note right of Client: 0ms update! No server needed
Client->>Server: updateComponentState("time", ...)
Server->>Server: Keep state in sync
Plugin Asset Serving
Plugins can include CSS, JavaScript, images, and fonts as embedded resources,
served automatically with versioning and caching.
flowchart TD
A[Plugin has embedded assets] --> B[PluginAssetMiddleware]
B --> C{Request matches /plugin-assets/...?}
C -->|No| D[Next middleware]
C -->|Yes| E[Parse URL]
E --> F{Version specified?}
F -->|Yes| G[Get plugin by name + version]
F -->|No| H[Get latest version]
G --> I{Plugin found?}
H --> I
I -->|No| J[404 Not Found]
I -->|Yes| K[Find embedded resource]
K --> L{Resource found?}
L -->|No| J
L -->|Yes| M[Set Content-Type]
M --> N[Set Cache headers max-age=86400]
N --> O[Set ETag]
O --> P[Stream resource bytes]
style B fill:#3b82f6
style K fill:#10b981
style P fill:#8b5cf6
Plugin Example: Clock Widget
Plugin Implementation (ClockPlugin.cs):
[MinimactPlugin("Clock", "1.0.0")]
public class ClockPlugin : MinimactPlugin<ClockState>
{
public override string Name => "Clock";
public override string Version => "1.0.0";
[LoopTemplate(
template: "<div class='clock'><h2>{0}:{1}</h2><p>{2}</p></div>",
bindings: ["hours", "minutes", "period"]
)]
public override VNode Render(ClockState state)
{
return new VElement("div",
new VAttribute("class", "clock"),
new VElement("h2", $"{state.Hours}:{state.Minutes:D2}"),
new VElement("p", state.Period)
);
}
public override IEnumerable<PluginAsset> GetAssets()
{
yield return new PluginAsset(
"clock-widget.css",
PluginAssetType.Stylesheet
);
}
}
public class ClockState
{
public int Hours { get; set; }
public int Minutes { get; set; }
public string Period { get; set; } // "AM" or "PM"
}
Multiple versions of the same plugin can coexist, with semver-based compatibility checking.
graph TB
subgraph "Plugin Registry"
A[Clock@1.0.0] --> R[Registry]
B[Clock@1.2.0] --> R
C[Clock@2.0.0] --> R
end
subgraph "Component Requests"
D[<Plugin name='Clock' />] --> E{GetLatestVersion}
F[<Plugin name='Clock' version='1.x' />] --> G{GetLatestCompatible}
H[<Plugin name='Clock' version='1.2.0' />] --> I{GetExactVersion}
end
E --> C
G --> B
I --> B
C --> J[Render with Clock@2.0.0]
B --> K[Render with Clock@1.2.0]
style R fill:#3b82f6
style C fill:#10b981
style B fill:#10b981
Security & Authorization Model
Method Invocation Security
Multi-layer security ensures only authenticated, authorized users can invoke
component methods with validated parameters.
sequenceDiagram
participant Client
participant SignalR as SignalR Hub
participant Auth as Authorization
participant Registry as Component Registry
participant Comp as Component
Note over Client,Comp: Secure Method Invocation Flow
Client->>SignalR: InvokeComponentMethod(componentId, "UpdateProfile", args)
SignalR->>Auth: ValidateConnection()
Auth->>Auth: Check User.Identity
Auth->>Auth: Verify ConnectionId is authenticated
alt Not Authenticated
Auth->>Client: 401 Unauthorized
end
SignalR->>Registry: GetComponent(componentId)
alt Component Not Found
Registry->>Client: Error: Component not found
end
Registry->>Registry: Verify component belongs to this connection
alt Wrong Connection
Registry->>Client: Error: Unauthorized access
end
SignalR->>Comp: Check [Authorize] attribute on method
alt Has [Authorize(Roles="Admin")]
Comp->>Auth: Check User.IsInRole("Admin")
Auth-->>Comp: False
Comp->>Client: Error: Forbidden
end
Comp->>Comp: Validate method parameters
alt Invalid Parameters
Comp->>Client: Error: Validation failed
end
Comp->>Comp: Invoke method via reflection
Comp->>Comp: Update state
Comp->>Client: Success
State Validation Flow
All client state updates go through type checking, range validation,
sanitization, and custom validators before being applied.
Demonstrating how the security layers block malicious state updates, unauthorized
component access, and protected method invocation attempts.
sequenceDiagram
participant Attacker as Malicious Client
participant SignalR
participant Validator
participant Component
Note over Attacker,Component: Attack Attempt: Send Invalid State
Attacker->>SignalR: UpdateComponentState("count", 999999999)
SignalR->>Validator: Validate state change
Validator->>Validator: Check type: int โ
Validator->>Validator: Check range: > max allowed (1000)
Validator->>Attacker: โ Error: Value exceeds maximum (1000)
Note over Attacker,Component: Attack Attempt: Access Other User's Component
Attacker->>SignalR: InvokeComponentMethod("other-user-component", "DeleteAccount")
SignalR->>SignalR: Verify component ownership
SignalR->>SignalR: ComponentId belongs to different ConnectionId
SignalR->>Attacker: โ Error: Unauthorized access to component
Note over Attacker,Component: Attack Attempt: Call Protected Method
Attacker->>SignalR: InvokeComponentMethod("admin-panel", "DeleteAllUsers")
SignalR->>Component: Check [Authorize(Roles="Admin")]
Component->>Component: User.IsInRole("Admin") = false
Component->>Attacker: โ Error: Forbidden - Admin role required
Note over Attacker,Component: Result: All attacks blocked by security layers
Project Setup & Scaffolding NEW
Generated Project Structure
When you create a new Minimact project, this is the directory structure you get.
Understanding where files go and what they do is crucial for development.
The file-save-to-browser-update cycle happens automatically. File watchers detect
changes, trigger Babel transformation, compile C#, and update the browserโall in ~2-3 seconds.
sequenceDiagram
participant Dev as Developer
participant IDE as VS Code/Rider
participant Watch as File Watcher
participant Babel as Babel Plugin
participant DotNet as .NET Compiler
participant Server as Dev Server
participant Browser
Note over Dev,Browser: Developer Iteration Cycle
Dev->>IDE: Edit Counter.tsx
IDE->>IDE: Save file
IDE->>Watch: File change detected
Watch->>Babel: Transform Counter.tsx
Babel->>Babel: Parse TSX AST
Babel->>Babel: Generate C# code
Babel->>CS_File: Write Counter.cs
Note over Watch,Server: Automatic Build
Watch->>DotNet: Trigger incremental build
DotNet->>DotNet: Compile Counter.cs
alt Compilation Error
DotNet->>IDE: Show error in Problems panel
IDE->>Dev: Display error
else Success
DotNet->>Server: Hot reload (if supported)
alt Hot Reload Available
Server->>Browser: Inject update (no refresh)
else No Hot Reload
Dev->>Browser: Manual refresh F5
end
Browser->>Browser: Re-render component
end
Note over Dev,Browser: Changes visible in ~2-3 seconds
Hot Reload System NEW
Template-Based Hot Reload
Minimact uses a template-based hot reload system that extracts text node templates
at build time and caches them on both server and client. When you edit a TSX file, only the changed
templates are sent to the browser, achieving 3-5ms hot reload latency with
98% less memory than prediction-based approaches.
โก 3-5ms Latency
Instant visual feedback on file save
๐พ 2KB per Component
98% memory reduction vs predictions
๐ฏ 100% Coverage
All state values, not just common ones
๐ Auto State Updates
Templates automatically re-render on state change
Complete System Flow
This diagram shows the entire lifecycle: build-time template extraction, runtime initialization,
hot reload updates, and automatic state change integration.
sequenceDiagram
participant Dev as Developer
participant Babel as Babel Plugin
participant FS as File System
participant Server as C# Server
participant SignalR as SignalR Hub
participant Client as Browser Client
participant TemplateState as Template State Manager
participant DOM as DOM
Note over Dev,DOM: BUILD TIME - Template Extraction
Dev->>Babel: Write/Edit Counter.tsx <h1>Count: {count}</h1>
Babel->>Babel: Parse JSX AST
Babel->>Babel: Extract text node template template: "Count: {0}" bindings: ["count"]
Babel->>FS: Generate Counter.g.cs
Babel->>FS: Generate Counter.templates.json
Note right of FS: { "templates": { "h1[0].text[0]": { "template": "Count: {0}", "bindings": ["count"], "slots": [7] } } }
Note over Dev,DOM: RUNTIME - Component Initialization
Client->>SignalR: RegisterComponent("Counter")
SignalR->>Server: Load component
Server->>FS: Read Counter.templates.json
FS-->>Server: Return template map
Server->>SignalR: Send template-map message
SignalR->>Client: HotReload:TemplateMap
Client->>TemplateState: loadTemplateMap(componentId, templates)
TemplateState->>TemplateState: Cache templates in memory (~2KB per component)
Note right of TemplateState: Templates ready for instant hot reload!
Note over Dev,DOM: HOT RELOAD - Developer Edit
Dev->>FS: Edit Counter.tsx Change: "Count: {count}" โ "Counter: {count}"
FS->>Babel: File change detected
Babel->>Babel: Re-parse JSX
Babel->>Babel: Extract new template template: "Counter: {0}" bindings: ["count"]
Babel->>FS: Update Counter.templates.json
FS->>Server: FileSystemWatcher event
Server->>Server: TemplateHotReloadManager Load new template map
Server->>Server: Detect template change OLD: "Count: {0}" NEW: "Counter: {0}"
Server->>Server: Get current state count = 5
Server->>Server: Create template patch { template: "Counter: {0}", params: [5], bindings: ["count"] }
Server->>SignalR: Send template patch
SignalR->>Client: HotReload:TemplatePatch
Client->>TemplateState: applyTemplatePatch(patch)
TemplateState->>TemplateState: Render: "Counter: {0}" .replace("{0}", 5) = "Counter: 5"
TemplateState->>DOM: Update text node
DOM->>DOM: Flash visual feedback
Note right of DOM: ๐ INSTANT UPDATE! 3-5ms total latency
Note over Dev,DOM: STATE CHANGE - User Interaction
DOM->>Client: User clicks increment
Client->>Client: useState: setCount(6)
Client->>Client: Update local state
Client->>TemplateState: updateState("count", 6)
Client->>TemplateState: getTemplatesBoundTo("count")
TemplateState-->>Client: [template for "h1[0].text[0]"]
Client->>TemplateState: render(componentId, nodePath)
TemplateState->>TemplateState: "Counter: {0}" .replace("{0}", 6) = "Counter: 6"
TemplateState-->>Client: "Counter: 6"
Client->>DOM: findElementByPath([0, 0])
Client->>DOM: textNode.textContent = "Counter: 6"
Note right of DOM: โจ Template auto-updates with new state!
Client->>SignalR: updateComponentState("count", 6)
SignalR->>Server: Sync state to prevent stale data
Server->>Server: component.SetStateFromClient("count", 6)
Note right of Server: Server state in sync! Next render will be correct
Template Extraction (Build Time)
During the Babel transformation, templates are extracted from JSX text nodes and saved to
.templates.json files alongside the generated C# code.
flowchart TD
A[Developer writes JSX] --> B{Babel Plugin}
B --> C[Parse JSX AST]
C --> D{For each JSX element}
D --> E{Has text children?}
E -->|Yes| F{Has expressions?}
F -->|Yes| G[Extract dynamic template]
F -->|No| H[Extract static template]
E -->|No| D
G --> I[Identify bindings e.g., count]
I --> J[Calculate slots e.g., position 7]
J --> K[Build template object]
H --> K
K --> L[Add to template map]
L --> M{More elements?}
M -->|Yes| D
M -->|No| N[Generate .templates.json]
N --> O[Generate .g.cs]
style G fill:#4CAF50
style H fill:#2196F3
style N fill:#FF9800
Template Loading (Runtime Init)
When a component first registers, the server reads the .templates.json file
and sends the template map to the client, which caches it in memory (~2KB per component).
flowchart TD
A[Component registers] --> B[Server loads .templates.json]
B --> C{File exists?}
C -->|Yes| D[Parse JSON]
C -->|No| E[Skip template features]
D --> F[Cache in TemplateHotReloadManager]
F --> G[Send template-map via SignalR]
G --> H[Client receives message]
H --> I[TemplateStateManager.loadTemplateMap]
I --> J{For each template}
J --> K[Store template with key componentId:nodePath]
K --> L[Build binding index state โ templates map]
L --> M{More templates?}
M -->|Yes| J
M -->|No| N[Templates ready!]
N --> O[Memory: ~2KB]
N --> P[Coverage: 100%]
N --> Q[Ready for hot reload]
style D fill:#4CAF50
style I fill:#2196F3
style N fill:#FF9800
Hot Reload Update (Template Patch)
When you save a TSX file, the FileSystemWatcher detects the change, Babel regenerates the templates,
and the server sends only the changed templates to the browser with the current state values filled in.
flowchart TD
A[Developer edits TSX] --> B[FileSystemWatcher detects]
B --> C[Babel re-runs]
C --> D[New .templates.json generated]
D --> E[Server loads new map]
E --> F{Compare with cached map}
F --> G{Template changed?}
G -->|Yes| H[Get current component state]
G -->|No| I[Skip - no update needed]
H --> J["Fill template with params template: Counter: {0} params: [5] = Counter: 5"]
J --> K[Create TemplatePatch object]
K --> L[Send via SignalR]
L --> M[Client receives patch]
M --> N[Render template with params]
N --> O[Find DOM node by path]
O --> P{Node found?}
P -->|Yes| Q[Update textContent]
P -->|No| R[Log warning]
Q --> S[Flash visual feedback]
S --> T[Update cached template]
T --> U[Complete! 3-5ms total]
style H fill:#4CAF50
style N fill:#2196F3
style U fill:#FF9800
State Change Integration
When useState triggers a state change, the template system automatically
finds all templates bound to that state variable and re-renders them.
flowchart TD
A[User triggers state change] --> B[setState called]
B --> C[Update local state context.state.set]
C --> D[Check HintQueue]
D --> E{Hint match?}
E -->|Yes| F[Apply cached patches]
E -->|No| G[Log cache miss]
F --> H[Update template state templateState.updateState]
G --> H
H --> I[Get templates bound to state getTemplatesBoundTo]
I --> J{Templates found?}
J -->|Yes| K{For each template}
J -->|No| L[Skip template update]
K --> M[Render template template.replace placeholders]
M --> N[Find DOM node by path]
N --> O{Node type?}
O -->|Text| P[Update textContent]
O -->|Element| Q{Has attribute?}
Q -->|Yes| R[Update attribute]
Q -->|No| S[Update textContent]
P --> T{More templates?}
R --> T
S --> T
T -->|Yes| K
T -->|No| U[Sync to server updateComponentState]
U --> V[Server updates state SetStateFromClient]
V --> W[State synchronized!]
style H fill:#4CAF50
style M fill:#2196F3
style U fill:#FF9800
style W fill:#9C27B0
Memory & Performance Comparison
Template-based hot reload uses 98% less memory than prediction-based caching
while achieving 100% coverage of all possible state values.
โ Prediction-Based
Memory: ~100KB per component
Coverage: 85% (1000+ cached variations)
Scalability: Poor (linear growth)
Latency: 3-5ms (cache hit)
โ Template-Based
Memory: ~2KB per component
Coverage: 100% (parameterized)
Scalability: Excellent (constant size)
Latency: 3-5ms (same!)
Code-Behind Pattern NEW
When to Use Code-Behind
The code-behind pattern separates UI logic (TSX) from business logic (C#).
Use .codebehind.cs for database queries, API calls, and complex server-side operations.
Minimact provides debugging tools for every layer: IDE breakpoints for C# server code,
browser DevTools for client code, and the Playground Bridge for visual inspection.
๐ป IDE Debugging
Set C# breakpoints, watch variables, inspect call stacks in VS Code or Rider
๐ Browser DevTools
Console logging, SignalR traffic inspection, DOM element inspection
๐ฏ Playground Bridge
Visual hint queue viewer, patch inspector, performance metrics
๐ Error Handling
TypeScript errors in IDE, C# errors in Problems panel, runtime errors in console
Integration Points NEW
Adding Database (EF Core)
Integrating Entity Framework Core is straightforward. Install packages, create DbContext,
register in DI, and use in .codebehind.cs files.