Skip to content

Template Patch System - Deep Dive

Status: ✅ Fully Implemented Version: 1.0 Last Updated: October 29, 2025


Overview

The Template Patch System is Minimact's revolutionary approach to predictive rendering. Unlike traditional systems that cache state-specific patches at runtime, Minimact generates parameterized templates at build time using Babel AST analysis, achieving 100% coverage with O(1) memory overhead.

This document provides a comprehensive technical deep dive into the implementation.


Architecture Components

1. Build-Time Pipeline (babel-plugin-minimact)

Location: src/babel-plugin-minimact/

The Babel plugin orchestrates four distinct template extraction phases:

TSX Source Code

   Babel AST

┌─────────────────────────────────────┐
│  Phase 1: Text Templates            │ → Hot reload for text content
│  Phase 4: Loop Templates            │ → 100% coverage for lists
│  Phase 5: Structural Templates      │ → Conditional rendering
│  Phase 6: Expression Templates      │ → Computed values
└─────────────────────────────────────┘

  C# Code Generation

[LoopTemplate] Attributes

Phase 1: Text Templates

Purpose: Instant hot reload for text content changes File: src/extractors/extractTemplates.cjs

Template Structure

javascript
{
  template: "Count: {0}",     // String with numbered slots
  bindings: ["count"],        // State variables used
  slots: [7],                 // Character positions of {0}, {1}, etc.
  path: [0, 0],              // DOM navigation path
  type: 'static' | 'dynamic'  // Template classification
}

Supported Patterns

1. Static Text

jsx
<h1>Welcome to Minimact</h1>
// → { template: "Welcome to Minimact", bindings: [], type: 'static' }

2. Mixed Text with Expressions

jsx
<p>Count: {count}</p>
// → { template: "Count: {0}", bindings: ["count"], slots: [7], type: 'dynamic' }

3. Multiple Bindings

jsx
<span>{firstName} {lastName}</span>
// → { template: "{0} {1}", bindings: ["firstName", "lastName"], slots: [0, 4] }

4. Template Literals in Attributes

jsx
<div className={`item-${id}`}>
// → Prop template with slots

Implementation Details

  • Path Keys: Templates are indexed by hierarchical DOM paths like "[0].h1[0].text[0]"
  • Slot Tracking: Records character positions where placeholders appear
  • Binding Resolution: Extracts variable names from AST identifiers
  • Escape Handling: Properly escapes JSON for C# verbatim strings

Phase 4: Loop Templates

Purpose: Parameterized templates for list rendering with O(1) memory File: src/extractors/extractLoopTemplates.cjs

Full Template Structure

javascript
{
  // Metadata for C# attribute
  stateKey: "todos",              // Which state variable (for [LoopTemplate("todos")])

  // Runtime execution details
  arrayBinding: "todos",          // Binding path to array
  itemVar: "todo",                // Callback parameter name
  indexVar: "index",              // Optional index parameter
  keyBinding: "item.id",          // React key expression

  // Nested template for each item
  itemTemplate: {
    type: 'Element',
    tag: "li",
    propsTemplates: {              // Property templates
      className: {
        template: "{0}",
        bindings: ["item.done"],
        slots: [0],
        type: 'conditional',
        conditionalTemplates: {
          true: "completed",
          false: "pending"
        }
      },
      "data-id": {
        template: "{0}",
        bindings: ["item.id"],
        slots: [0],
        type: 'binding'
      }
    },
    childrenTemplates: [            // Child element templates
      {
        type: 'Element',
        tag: "span",
        childrenTemplates: [
          {
            type: 'Text',
            template: "{0}",
            bindings: ["item.text"],
            slots: [0]
          }
        ]
      },
      {
        type: 'Element',
        tag: "button",
        propsTemplates: {
          onClick: {
            template: "toggleTodo({0})",
            bindings: ["item.id"],
            slots: [11],
            type: 'event'
          }
        },
        childrenTemplates: [
          {
            type: 'conditional',
            template: "{0}",
            bindings: ["item.done"],
            conditionalTemplates: {
              true: "✓",
              false: "○"
            }
          }
        ]
      }
    ]
  }
}

Property Template Types

1. Simple Binding

jsx
<li data-id={todo.id}>
// → { template: "{0}", bindings: ["item.id"], type: 'binding' }

2. Conditional Expression

jsx
<li className={todo.done ? 'done' : 'pending'}>
// → {
//   template: "{0}",
//   bindings: ["item.done"],
//   type: 'conditional',
//   conditionalTemplates: { true: "done", false: "pending" }
// }

3. Template Literal

jsx
<li className={`item-${todo.id}-${todo.status}`}>
// → {
//   template: "item-{0}-{1}",
//   bindings: ["item.id", "item.status"],
//   slots: [5, 10],
//   type: 'template-literal'
// }

4. Event Handler

jsx
<button onClick={() => deleteTodo(todo.id)}>
// → {
//   template: "deleteTodo({0})",
//   bindings: ["item.id"],
//   type: 'event'
// }

Extraction Features

  • Method Chaining: Handles .filter().map() chains
  • Spread Operators: Supports [...items] patterns
  • Multiple Parameters: Extracts both item and index variables
  • Nested Loops: Supports nested .map() with item.children.map(...)
  • Key Extraction: Automatically extracts key={todo.id} expressions

C# Attribute Generation

Loop templates are serialized to JSON and embedded in C# attributes:

csharp
[LoopTemplate("todos", @"{
  ""stateKey"": ""todos"",
  ""arrayBinding"": ""todos"",
  ""itemVar"": ""todo"",
  ""indexVar"": ""index"",
  ""keyBinding"": ""item.id"",
  ""itemTemplate"": {
    ""type"": ""Element"",
    ""tag"": ""li"",
    ""propsTemplates"": {
      ""className"": {
        ""template"": ""{0}"",
        ""bindings"": [""item.done""],
        ""conditionalTemplates"": {
          ""true"": ""done"",
          ""false"": ""pending""
        }
      }
    },
    ""childrenTemplates"": [...]
  }
}")]
public partial class TodoList : MinimactComponent
{
    [State]
    private List<Todo> todos = new();
}

Key Points:

  • JSON is escaped for C# verbatim strings ("" for quotes)
  • stateKey used in attribute constructor: [LoopTemplate("todos", ...)]
  • Multiple loop templates = multiple attributes
  • Runtime: C# reflection reads attributes on component initialization

Phase 5: Structural Templates

Purpose: Conditional rendering where DOM structure changes File: src/extractors/structuralTemplates.cjs

Template Structure

javascript
{
  type: 'conditional' | 'logicalAnd',
  stateKey: "isLoggedIn",              // Root state variable
  conditionBinding: "isLoggedIn",      // Full binding expression
  branches: {
    'true': {
      type: 'Element' | 'Fragment' | 'Null',
      tag: "Dashboard",
      props: { /* static props only */ },
      children: [ /* simplified structure */ ]
    },
    'false': {
      type: 'Element',
      tag: "LoginForm",
      props: {},
      children: []
    }
  },
  path: [0, 0]  // DOM location
}

Supported Patterns

1. Ternary Conditional

jsx
{user ? <Dashboard /> : <LoginForm />}
// → Conditional template with true/false branches

2. Logical AND

jsx
{error && <ErrorMessage />}
// → {
//   type: 'logicalAnd',
//   branches: {
//     true: { type: 'Element', tag: 'ErrorMessage' },
//     false: { type: 'Null' }
//   }
// }

3. Negation

jsx
{!isLoading && <Content />}
// → Conditional with inverted binding

Branch Constraints

  • Static Props Only: Dynamic props marked for runtime evaluation
  • Simplified Structure: Branches store element type and tag, not deeply templated
  • Fragment Support: Handles <>...</> fragments
  • Null Branches: Represents absence of DOM nodes

Phase 6: Expression Templates

Purpose: Computed values with whitelisted transformations File: src/extractors/expressionTemplates.cjs

Supported Transformations

Number Formatting:

  • toFixed(n), toPrecision(n), toExponential(n)

String Operations:

  • toUpperCase(), toLowerCase(), trim()
  • substring(start, end), slice(start, end)

Array Access:

  • .length, .join(separator)

Arithmetic:

  • Binary: +, -, *, /, %
  • Unary: -value, +value

Template Types

1. Method Call

jsx
{price.toFixed(2)}
// → {
//   type: 'methodCall',
//   binding: 'price',
//   method: 'toFixed',
//   args: [2],
//   transform: { type: 'numberFormat', method: 'toFixed', args: [2] }
// }

2. Binary Expression

jsx
{count * 2 + 1}
// → {
//   type: 'binaryExpression',
//   bindings: ['count'],
//   transform: {
//     type: 'arithmetic',
//     operations: [
//       { op: '*', value: 2, side: 'right' },
//       { op: '+', value: 1, side: 'right' }
//     ]
//   }
// }

3. Member Expression

jsx
{items.length}
// → {
//   type: 'memberExpression',
//   binding: 'items.length',
//   transform: { type: 'property', property: 'length' }
// }

4. Complex Expression (Fallback)

jsx
{a * b + calculateTax(total)}
// → {
//   type: 'complexExpression',
//   bindings: ['a', 'b', 'total'],
//   expression: 'a * b + calculateTax(total)'
// }

Security Model

  • Whitelist Only: Only approved operations allowed
  • No Arbitrary Code: No eval() or function execution
  • Safe Transforms: All operations deterministic and side-effect-free

Runtime Application

Client-Side Template Application

File: src/client-runtime/src/template-applier.ts (conceptual)

javascript
function applyTemplate(element, template, state) {
  switch (template.type) {
    case 'static':
      element.textContent = template.template;
      break;

    case 'dynamic':
      const values = template.bindings.map(binding =>
        resolvePath(state, binding)
      );
      const result = fillSlots(template.template, values);
      element.textContent = result;
      break;

    case 'conditional':
      const conditionValue = resolvePath(state, template.bindings[0]);
      const branch = conditionValue ? 'true' : 'false';
      const result = template.conditionalTemplates[branch];
      element.textContent = result;
      break;

    case 'loop':
      const array = resolvePath(state, template.arrayBinding);
      const items = array.map((item, index) =>
        applyItemTemplate(template.itemTemplate, { item, index })
      );
      replaceChildren(element, items);
      break;
  }
}

Slot Filling Algorithm

javascript
function fillSlots(template, values) {
  return template.replace(/\{(\d+)\}/g, (match, index) => {
    return values[parseInt(index)] ?? '';
  });
}

// Example:
fillSlots("Hello {0}, you have {1} messages", ["Alice", 5])
// Result: "Hello Alice, you have 5 messages"

Path Resolution

javascript
function resolvePath(obj, path) {
  return path.split('.').reduce((current, key) =>
    current?.[key], obj
  );
}

// Example:
resolvePath({ user: { name: "Bob" } }, "user.name")
// Result: "Bob"

Performance Characteristics

Build Time

  • Babel Analysis: O(n) where n = JSX nodes
  • Template Generation: O(n) where n = dynamic expressions
  • One-time cost at transpilation
  • Cached between rebuilds

Runtime (Server)

  • Reflection: O(1) - Cached after first access
  • Template Lookup: O(1) - Hash table
  • Patch Generation: O(1) - Template already prepared

Runtime (Client)

  • Template Application: O(m) where m = number of bindings
  • Slot Filling: O(s) where s = number of slots
  • DOM Updates: O(k) where k = changed elements

Comparison

OperationFull RenderTemplate Patch
Server RenderingO(n)O(1)
Network Transfer~100KB HTML~2KB JSON
Client ProcessingO(n) parsingO(m) substitution
DOM UpdatesO(n) reconciliationO(k) direct updates
Overall SpeedupBaseline10x-100x

Implementation Files

Babel Plugin Structure

src/babel-plugin-minimact/
├── src/
│   ├── processComponent.cjs           # Main orchestrator
│   ├── extractors/
│   │   ├── extractTemplates.cjs      # Phase 1: Text templates
│   │   ├── extractLoopTemplates.cjs  # Phase 4: Loop templates
│   │   ├── structuralTemplates.cjs   # Phase 5: Conditionals
│   │   └── expressionTemplates.cjs   # Phase 6: Expressions
│   ├── generators/
│   │   └── component.cjs             # C# code generation
│   ├── transpilers/
│   │   └── rust.cjs                  # Rust integration
│   └── types/
│       └── templates.d.ts            # TypeScript definitions

Key Functions

processComponent.cjs:

  • extractComponentMetadata(path) - Extract props, hooks, state
  • cloneRenderBody(path) - Clone JSX AST for template extraction
  • processTemplates(component) - Orchestrate all extraction phases

extractLoopTemplates.cjs:

  • extractLoopTemplates(renderBody) - Find .map() calls
  • processMapCallback(callback) - Extract item/index parameters
  • buildItemTemplate(body) - Recursively build template structure
  • extractPropertyTemplate(value) - Classify prop template type

component.cjs:

  • generateComponentClass(component) - Generate C# partial class
  • emitLoopTemplateAttributes(templates) - Emit [LoopTemplate] attributes
  • escapeJsonForCSharp(json) - Escape JSON for verbatim strings

Advanced Features

Nested Loops

JSX:

jsx
{posts.map(post => (
  <article key={post.id}>
    <h2>{post.title}</h2>
    <div className="comments">
      {post.comments.map((comment, idx) => (
        <div key={comment.id}>
          <strong>{comment.author}</strong>
          <span>{comment.text}</span>
        </div>
      ))}
    </div>
  </article>
))}

Generated Template:

javascript
{
  stateKey: "posts",
  arrayBinding: "posts",
  itemVar: "post",
  itemTemplate: {
    childrenTemplates: [
      // ... h2, div wrapper ...
      {
        type: 'loop',              // Nested loop!
        arrayBinding: "item.comments",
        itemVar: "comment",
        indexVar: "idx",
        itemTemplate: {
          // ... comment template ...
        }
      }
    ]
  }
}

Binding Paths:

  • Outer loop: item.title, item.comments
  • Inner loop: nested.author, nested.text

External Library Detection

The plugin detects when local variables use external libraries:

jsx
import moment from 'moment';

function Component() {
  const formattedDate = moment(timestamp).format('YYYY-MM-DD');
  return <div>{formattedDate}</div>;
}

Detection:

  • moment(...) → External library call
  • formattedDate → Marked as isClientComputed: true
  • Template → Stores as __complex__ binding

Purpose: Enables client-side evaluation of library-dependent expressions


Limitations and Edge Cases

1. Complex Expressions

Templates with __complex__ bindings require server evaluation:

jsx
<div>{users.filter(u => u.active).map(u => u.name).join(', ')}</div>

Solution: Client waits for server patch instead of template application.

2. Component Boundaries

Templates don't cross component boundaries:

jsx
<div>{items.map(item => <TodoItem item={item} />)}</div>

Solution: Separate templates for parent and child components.

3. Dynamic Tag Names

JSX with dynamic element types:

jsx
<>{items.map(item => React.createElement(item.type, {}, item.content))}</>

Solution: Not supported - requires server rendering.

4. Refs and Imperative APIs

Templates work for declarative updates only:

jsx
const ref = useRef();
useEffect(() => { ref.current.focus(); }, []);

Solution: Imperative code runs separately from template system.


Future Enhancements

1. Template Composition

Allow templates to reference other templates:

json
{
  "template": "{0} - {template:userBadge}",
  "bindings": ["item.name"],
  "referencedTemplates": ["userBadge"]
}

2. Expression Functions

Support basic transformations client-side:

json
{
  "template": "{0}",
  "bindings": ["item.price"],
  "transform": "currency:USD"
}

3. Animation Templates

Specify transition parameters in templates:

json
{
  "template": "{0}",
  "transition": {
    "duration": 300,
    "easing": "ease-in-out"
  }
}

4. Template Versioning

Track template versions for cache invalidation:

json
{
  "component": "TodoList",
  "version": "2.0",
  "templateHash": "a3f2b8c1"
}

Conclusion

The Template Patch System represents a paradigm shift in predictive rendering:

  • Zero-prediction architecture - No ML, no learning phase
  • 100% state coverage - Works for any state value
  • Build-time analysis - Leverages Babel AST parsing
  • Instant feedback - Client applies templates immediately
  • Perfect accuracy - Templates extracted from source JSX
  • O(1) memory - Single template vs N cached patches

This system makes Minimact's predictive rendering deterministic, reliable, and universally applicable to any React component.


Released under the MIT License.