July 2026 capability update

Preact-shaped code.
Direct DOM output.

The lab now covers realistic hooks and context, server rendering without a DOM, compiled islands inside ordinary Preact trees, and hundreds of independently compiled upstream cases.

Server HTML ready · hydration pending
opt-in runtime zero bytes in default Preact one render and hydration shape
518/637upstream casesindependently compiled
93/99attributed behaviorsexact pinned titles
24,987 Bbrowser bundleBrotli compressed
0recoverable gapsfrom the pinned audit
What landed

A much wider credible application surface

These are compiler and runtime paths with focused tests—not a compatibility promise for arbitrary Preact applications.

01 / Authoring

Components can look like real application code.

Guarded returns, reactive context, template values, renderable arrays, component refs, and package-native hooks all lower into owned direct-DOM work.

  • useMemo and post-commit useEffect
  • createContext, Provider, Consumer, and useContext
  • JSX stored in state or Signals as reusable template handles
  • Guarded component returns and nested renderable arrays
02 / Server

SSR no longer needs a browser-shaped document.

A plain-object host runs the same generated create path in Workers and Node, then emits one canonical hydration wire format.

DOM-independentboundary optionsbounded retries
03 / Adoption

Compiled islands fit inside existing Preact trees.

The ABI v3 island wrapper keeps Preact out of the compiled subtree while reactive props update bindings in place—one subtree at a time, with no core cost.

VDOM hostlive propsstable DOM
04 / Reactivity

Signals now carry application-level behavior.

Models and actions auto-batch updates, effect ownership disposes deterministically, and dropped computed dependencies stop doing unnecessary work.

createModelactiongraph pruning
05 / Fidelity

The DOM edge cases are part of the contract.

Root swaps, unkeyed reorders, Fragment movement, inner HTML, comments, form defaults, iframe and document hosts, MathML, and Preact v11 SVG attributes now have executable coverage.

  • Preact-aligned text and object omission
  • Reserved JSX metadata never reaches the host
  • Shared conditional edges preserve host state
  • Templates retain identity across positions
Measured evidence

Less update work. Competitive browser paths.

Correctness-gated results from the checked benchmark fixtures. Lower is better for timings; local browser numbers are directional, machine-sensitive evidence—not CI performance budgets.

Scalar update work
1compiled component run
vs
101reference component runs

Across 100 state updates, each path performs 100 text mutations and 0 child-list mutations. The compiled binding does the same DOM work without re-evaluating the component.

Keyed compiled frontier1.03–1.12×

Geometric mean against the fastest compiled reference across nine keyed operations and two consecutive full runs.

Standalone runtime9,078 B

Brotli, rebuilt with this page. Budget 9,100 B; 22 B headroom.

Keyed browser fixture

Retained row operations

Lab time
OperationTwo-run range
Create 1,0003.05–3.15 ms
Replace 1,0005.30–5.40 ms
Append 1,0002.75–2.90 ms
Create 10,00026.60–26.85 ms
Hydration browser fixture

Synchronous keyed claim

Lab / Preact
RowsLabPreactRatio
1,000 rows6.40–6.65 ms5.05 ms1.27–1.32×
10,000 rows69.60–70.35 ms162.05–164.25 ms0.42–0.43×
Client payload25,107 B lab · 12,110 B Preact10k HTML1,224,104 B lab · 916,740 B Preact

Measured 2026-07-26 on arm64 macOS, Node 22.22.0, Chromium 149.0.7827.55. The hydration fixture covers synchronous keyed hydration only—not streaming, Suspense, or resume.

Update work ↗Keyed methodology ↗Hydration methodology ↗
Live compiled component

Keyed rows, direct updates

3 rows
01ParseOxc TSX AST
02Proveeligible shapes
03Lowerowned template IR
04Emitdirect DOM + SSR
Compiler x-ray

Authored TSX becomes direct DOM work

Both panes come from this build. The right side is the Rust compiler's emitted JavaScript—not a hand-written approximation.

Inputdemo/src/App.tsx
569 lines
import { signal, useComputed } from "@preact/signals";
import { useEffect, useMemo, useState } from "preact/hooks";
import { StatCard } from "./StatCard.tsx";

interface DemoRow {
  id: number;
  label: string;
}

interface DemoProps {
  rows: DemoRow[];
  compiled: {
    excerpt: string;
    full: string;
    lines: number;
    bytes: string;
  };
  stats: {
    upstream: string;
    compatibility: string;
    bundle: string;
    recovery: string;
    targets: string;
  };
  benchmarks: {
    measuredAt: string;
    environment: {
      platform: string;
      node: string;
      chromium: string;
    };
    updateWork: {
      updates: number;
      compiledEvaluations: number;
      referenceEvaluations: number;
      characterDataMutations: number;
      childListMutations: number;
    };
    keyed: {
      frontierMean: string;
      operations: Array<{
        label: string;
        lab: string;
      }>;
    };
    hydration: {
      rows: Array<{
        label: string;
        lab: string;
        preact: string;
        ratio: string;
      }>;
      clientSize: string;
      htmlSize10k: string;
    };
    size: {
      runtimeBrotli: string;
      runtimeBudget: string;
      headroom: string;
    };
  };
  source: string;
  sourceLines: number;
  nextId(): number;
  onCommit(rows: number): void;
}

export function App({
  rows: initialRows,
  compiled,
  stats,
  benchmarks,
  source,
  sourceLines,
  nextId,
  onCommit
}: DemoProps) {
  const [count, setCount] = useState(0);
  const rows = signal(initialRows);
  const selected = signal(initialRows[0].id);
  const showFullOutput = signal(false);
  const rowCount = useMemo(() => rows.value.length, [rows.value.length]);
  const compiledOutput = useComputed(() =>
    showFullOutput.value ? compiled.full : compiled.excerpt
  );
  const outputLabel = useComputed(() =>
    showFullOutput.value ? "Show focused excerpts" : "Show full emitted module"
  );

  useEffect(() => {
    onCommit(rowCount);
  }, [rowCount]);

  return (
    <main className="showcase">
      <section className="hero">
        <div className="eyebrow">July 2026 capability update</div>
        <h1>Preact-shaped code.<br />Direct DOM output.</h1>
        <p className="lede">
          The lab now covers realistic hooks and context, server rendering
          without a DOM, compiled islands inside ordinary Preact trees, and
          hundreds of independently compiled upstream cases.
        </p>
        <div className="hero-actions">
          <button id="increment" className="primary" onClick={() => setCount(count + 1)}>
            Hook updates: {count}
          </button>
          <span id="hydration-result" className="runtime-status">
            Server HTML ready · hydration pending
          </span>
        </div>
        <div className="hero-proof" aria-label="Compiler guarantees">
          <span><i></i> opt-in runtime</span>
          <span><i></i> zero bytes in default Preact</span>
          <span><i></i> one render and hydration shape</span>
        </div>
      </section>

      <section className="stats" aria-label="Verified project statistics">
        <StatCard
          value={stats.upstream}
          label="upstream cases"
          detail="independently compiled"
        />
        <StatCard
          value={stats.compatibility}
          label="attributed behaviors"
          detail="exact pinned titles"
        />
        <StatCard
          value={stats.bundle}
          label="browser bundle"
          detail="Brotli compressed"
        />
        <StatCard
          value={stats.recovery}
          label="recoverable gaps"
          detail="from the pinned audit"
        />
      </section>

      <section id="capabilities" className="release-section">
        <div className="release-heading">
          <div>
            <span className="section-kicker">What landed</span>
            <h2>A much wider credible application surface</h2>
          </div>
          <p>
            These are compiler and runtime paths with focused tests—not a
            compatibility promise for arbitrary Preact applications.
          </p>
        </div>

        <div className="capability-grid">
          <article className="capability-card capability-card-wide">
            <div className="capability-index">01 / Authoring</div>
            <h3>Components can look like real application code.</h3>
            <p>
              Guarded returns, reactive context, template values, renderable
              arrays, component refs, and package-native hooks all lower into
              owned direct-DOM work.
            </p>
            <ul className="feature-list">
              <li><code>useMemo</code> and post-commit <code>useEffect</code></li>
              <li><code>createContext</code>, Provider, Consumer, and <code>useContext</code></li>
              <li>JSX stored in state or Signals as reusable template handles</li>
              <li>Guarded component returns and nested renderable arrays</li>
            </ul>
          </article>

          <article className="capability-card">
            <div className="capability-index">02 / Server</div>
            <h3>SSR no longer needs a browser-shaped document.</h3>
            <p>
              A plain-object host runs the same generated create path in Workers
              and Node, then emits one canonical hydration wire format.
            </p>
            <div className="capability-tags">
              <span>DOM-independent</span>
              <span>boundary options</span>
              <span>bounded retries</span>
            </div>
          </article>

          <article className="capability-card">
            <div className="capability-index">03 / Adoption</div>
            <h3>Compiled islands fit inside existing Preact trees.</h3>
            <p>
              The ABI v3 island wrapper keeps Preact out of the compiled subtree
              while reactive props update bindings in place—one subtree at a
              time, with no core cost.
            </p>
            <div className="capability-tags">
              <span>VDOM host</span>
              <span>live props</span>
              <span>stable DOM</span>
            </div>
          </article>

          <article className="capability-card">
            <div className="capability-index">04 / Reactivity</div>
            <h3>Signals now carry application-level behavior.</h3>
            <p>
              Models and actions auto-batch updates, effect ownership disposes
              deterministically, and dropped computed dependencies stop doing
              unnecessary work.
            </p>
            <div className="capability-tags">
              <span>createModel</span>
              <span>action</span>
              <span>graph pruning</span>
            </div>
          </article>

          <article className="capability-card capability-card-wide">
            <div className="capability-index">05 / Fidelity</div>
            <h3>The DOM edge cases are part of the contract.</h3>
            <p>
              Root swaps, unkeyed reorders, Fragment movement, inner HTML,
              comments, form defaults, iframe and document hosts, MathML, and
              Preact v11 SVG attributes now have executable coverage.
            </p>
            <ul className="feature-list feature-list-columns">
              <li>Preact-aligned text and object omission</li>
              <li>Reserved JSX metadata never reaches the host</li>
              <li>Shared conditional edges preserve host state</li>
              <li>Templates retain identity across positions</li>
            </ul>
          </article>
        </div>
      </section>

      <section id="benchmarks" className="benchmark-section">
        <div className="release-heading">
          <div>
            <span className="section-kicker">Measured evidence</span>
            <h2>Less update work. Competitive browser paths.</h2>
          </div>
          <p>
            Correctness-gated results from the checked benchmark fixtures.
            Lower is better for timings; local browser numbers are directional,
            machine-sensitive evidence—not CI performance budgets.
          </p>
        </div>

        <div className="benchmark-grid">
          <article className="benchmark-card benchmark-card-wide">
            <span className="benchmark-label">Scalar update work</span>
            <div className="benchmark-comparison">
              <div>
                <strong>{benchmarks.updateWork.compiledEvaluations}</strong>
                <span>compiled component run</span>
              </div>
              <i>vs</i>
              <div>
                <strong>{benchmarks.updateWork.referenceEvaluations}</strong>
                <span>reference component runs</span>
              </div>
            </div>
            <p>
              Across {benchmarks.updateWork.updates} state updates, each path
              performs {benchmarks.updateWork.characterDataMutations} text
              mutations and {benchmarks.updateWork.childListMutations} child-list
              mutations. The compiled binding does the same DOM work without
              re-evaluating the component.
            </p>
          </article>

          <article className="benchmark-card">
            <span className="benchmark-label">Keyed compiled frontier</span>
            <strong className="benchmark-metric">{benchmarks.keyed.frontierMean}</strong>
            <p>
              Geometric mean against the fastest compiled reference across nine
              keyed operations and two consecutive full runs.
            </p>
          </article>

          <article className="benchmark-card">
            <span className="benchmark-label">Standalone runtime</span>
            <strong className="benchmark-metric">{benchmarks.size.runtimeBrotli}</strong>
            <p>
              Brotli, rebuilt with this page. Budget{" "}
              {benchmarks.size.runtimeBudget}; {benchmarks.size.headroom} headroom.
            </p>
          </article>
        </div>

        <div className="benchmark-detail-grid">
          <article className="benchmark-table-card">
            <header>
              <div>
                <span className="benchmark-label">Keyed browser fixture</span>
                <h3>Retained row operations</h3>
              </div>
              <small>Lab time</small>
            </header>
            <div className="benchmark-table-scroll">
              <table>
                <thead>
                  <tr>
                    <th>Operation</th>
                    <th>Two-run range</th>
                  </tr>
                </thead>
                <tbody>
                  {benchmarks.keyed.operations.map(operation => (
                    <tr key={operation.label}>
                      <td>{operation.label}</td>
                      <td>{operation.lab}</td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          </article>

          <article className="benchmark-table-card">
            <header>
              <div>
                <span className="benchmark-label">Hydration browser fixture</span>
                <h3>Synchronous keyed claim</h3>
              </div>
              <small>Lab / Preact</small>
            </header>
            <div className="benchmark-table-scroll">
              <table>
                <thead>
                  <tr>
                    <th>Rows</th>
                    <th>Lab</th>
                    <th>Preact</th>
                    <th>Ratio</th>
                  </tr>
                </thead>
                <tbody>
                  {benchmarks.hydration.rows.map(row => (
                    <tr key={row.label}>
                      <td>{row.label}</td>
                      <td>{row.lab}</td>
                      <td>{row.preact}</td>
                      <td>{row.ratio}</td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
            <div className="benchmark-payload">
              <span><strong>Client payload</strong>{benchmarks.hydration.clientSize}</span>
              <span><strong>10k HTML</strong>{benchmarks.hydration.htmlSize10k}</span>
            </div>
          </article>
        </div>

        <div className="benchmark-note">
          <p>
            Measured {benchmarks.measuredAt} on {benchmarks.environment.platform},
            Node {benchmarks.environment.node}, Chromium{" "}
            {benchmarks.environment.chromium}. The hydration fixture covers
            synchronous keyed hydration only—not streaming, Suspense, or resume.
          </p>
          <div>
            <a href="https://github.com/JoviDeCroock/preact-compiler-lab/blob/main/docs/work-benchmark.md">Update work ↗</a>
            <a href="https://github.com/JoviDeCroock/preact-compiler-lab/blob/main/docs/browser-keyed-benchmark.md">Keyed methodology ↗</a>
            <a href="https://github.com/JoviDeCroock/preact-compiler-lab/blob/main/docs/browser-hydration-benchmark.md">Hydration methodology ↗</a>
          </div>
        </div>
      </section>

      <section id="proof" className="demo-grid">
        <article className="interactive-panel">
          <div className="section-heading">
            <div>
              <span className="section-kicker">Live compiled component</span>
              <h2>Keyed rows, direct updates</h2>
            </div>
            <strong className="row-count">{rowCount} rows</strong>
          </div>

          <div className="toolbar">
            <button
              id="add-row"
              onClick={() =>
                rows.value = rows.value.concat({
                  id: nextId(),
                  label: `Compiled row ${rows.value.length + 1}`
                })
              }
            >
              Add row
            </button>
            <button
              id="rotate-rows"
              onClick={() =>
                rows.value = rows.value.length > 1
                  ? rows.value.slice(1).concat(rows.value[0])
                  : rows.value
              }
            >
              Rotate identity
            </button>
          </div>

          <ul id="compiled-rows" className="compiled-rows">
            {rows.value.map(row => (
              <li
                key={row.id}
                data-id={row.id}
                className={selected.value === row.id ? "selected" : ""}
              >
                <button
                  className="row-select"
                  onClick={() => selected.value = row.id}
                >
                  <span>{row.label}</span>
                  <small>key {row.id}</small>
                </button>
                <button
                  className="row-remove"
                  aria-label={`Remove ${row.label}`}
                  onClick={() =>
                    rows.value = rows.value.filter(item => item.id !== row.id)
                  }
                >
                  ×
                </button>
              </li>
            ))}
          </ul>
        </article>

        <aside className="proof-panel">
          <span className="section-kicker">What this page proves</span>
          <h2>One component, four real paths</h2>
          <ol>
            <li><strong>Compile</strong><span>Oxc lowers TSX into direct DOM instructions.</span></li>
            <li><strong>Render</strong><span>The server host emits the exact HTML you loaded.</span></li>
            <li><strong>Hydrate</strong><span>Existing nodes are claimed before exact bindings attach.</span></li>
            <li><strong>Update</strong><span>Hooks and Signals mutate only their owned bindings.</span></li>
          </ol>
          <div className="target-row">
            <span>Targets</span>
            <code>{stats.targets}</code>
          </div>
        </aside>
      </section>

      <section className="architecture-strip" aria-label="Compiler architecture">
        <div>
          <span>01</span>
          <strong>Parse</strong>
          <small>Oxc TSX AST</small>
        </div>
        <i>→</i>
        <div>
          <span>02</span>
          <strong>Prove</strong>
          <small>eligible shapes</small>
        </div>
        <i>→</i>
        <div>
          <span>03</span>
          <strong>Lower</strong>
          <small>owned template IR</small>
        </div>
        <i>→</i>
        <div>
          <span>04</span>
          <strong>Emit</strong>
          <small>direct DOM + SSR</small>
        </div>
      </section>

      <section id="compiler" className="compiler-panel">
        <div className="compiler-heading">
          <div>
            <span className="section-kicker">Compiler x-ray</span>
            <h2>Authored TSX becomes direct DOM work</h2>
          </div>
          <p>
            Both panes come from this build. The right side is the Rust compiler's
            emitted JavaScript—not a hand-written approximation.
          </p>
        </div>

        <div className="compiler-flow">
          <article className="code-card">
            <header>
              <div>
                <span>Input</span>
                <strong>demo/src/App.tsx</strong>
              </div>
              <small>{sourceLines} lines</small>
            </header>
            <pre><code>{source}</code></pre>
          </article>

          <div className="compile-arrow" aria-hidden="true">→</div>

          <article className="code-card emitted-code">
            <header>
              <div>
                <span>Rust / Oxc output</span>
                <strong>App.compiled.mjs</strong>
              </div>
              <small>{compiled.lines} lines · {compiled.bytes}</small>
            </header>
            <pre id="compiled-output"><code>{compiledOutput.value}</code></pre>
            <button
              id="toggle-compiled-output"
              className="output-toggle"
              onClick={() => showFullOutput.value = !showFullOutput.value}
            >
              {outputLabel.value}
            </button>
          </article>
        </div>
      </section>

      <section id="boundaries" className="boundary-panel">
        <div>
          <span className="section-kicker">Honest boundaries</span>
          <h2>Useful now. Still a compiler lab.</h2>
        </div>
        <p>
          The remaining boundary is architectural, not hidden test debt:
          portals, streaming SSR, <code>preact/compat</code>, arbitrary
          VDOM-valued expressions, and runtime Suspense/error boundaries are
          not implemented. The compiler stays separate and opt-in until real
          application evidence justifies a stronger compatibility claim.
        </p>
      </section>

      <section id="docs" className="docs-section">
        <div className="release-heading">
          <div>
            <span className="section-kicker">Read the evidence</span>
            <h2>Design notes, limits, and reproducible proof</h2>
          </div>
          <p>
            The repository documents how each claim is measured and where the
            compiler deliberately stops.
          </p>
        </div>
        <div className="docs-grid">
          <a href="https://github.com/JoviDeCroock/preact-compiler-lab/blob/main/docs/supported-syntax.md">
            <span>Syntax</span>
            <strong>Supported authoring surface</strong>
            <small>hooks, context, templates, JSX, and diagnostics →</small>
          </a>
          <a href="https://github.com/JoviDeCroock/preact-compiler-lab/blob/main/docs/external-modules-and-interop.md">
            <span>Interop</span>
            <strong>Migration and island design</strong>
            <small>how compiled and ordinary Preact meet →</small>
          </a>
          <a href="https://github.com/JoviDeCroock/preact-compiler-lab/blob/main/docs/upstream-suites.md">
            <span>Evidence</span>
            <strong>Upstream compatibility suites</strong>
            <small>exact pins, mappings, and exclusions →</small>
          </a>
          <a href="https://github.com/JoviDeCroock/preact-compiler-lab/blob/main/docs/architecture.md">
            <span>Internals</span>
            <strong>Compiler and runtime architecture</strong>
            <small>pipeline, ownership, and generated ABI →</small>
          </a>
        </div>
      </section>
    </main>
  );
}
Rust / Oxc outputApp.compiled.mjs
3058 lines · 144,217 B
// Generated by preact_compiler_lab. Do not edit by hand.
import { createRuntime, own as $own } from "preact-compiler-lab:runtime";
import { bindKeyedAttribute as $bindKeyedAttribute } from "preact-compiler-lab:runtime/keyed-attributes";
import { createComponent as $factory_StatCard, componentMetadata as $metadata_StatCard } from "./StatCard.compiled.mjs";

// … source-location table omitted from this focused view …

  function $createRoot($ctx, $componentProps, $n = 0) {
    return $ctx.renderPhase(() => {
      let initialRows, compiled, stats, benchmarks, source, sourceLines, nextId, onCommit;
      const $propsVersion0 = $ctx.signal(0);
      $ctx.observe(() => { ({
  rows: initialRows,
  compiled,
  stats,
  benchmarks,
  source,
  sourceLines,
  nextId,
  onCommit
} = $componentProps); $propsVersion0.value = $propsVersion0.peek() + 1; });
      const {useState,signal,computed,effect,batch,useComputed,useMemo,useEffect}=$ctx;
      const $state_count = useState(0);
      let count = $state_count.value;
      const setCount = $arg_count => { const $next_count = typeof $arg_count === "function" ? $arg_count($state_count.value) : $arg_count; $ctx.setState($state_count, $next_count, $sync_count=>count=$sync_count); };
      const rows = signal(initialRows);
      const selected = signal(initialRows[0].id);
      const showFullOutput = signal(false);
      const $memo_rowCount = useMemo(() => rows.value.length, () => ([rows.value.length]));
      let rowCount = $memo_rowCount.value;
      const compiledOutput = useComputed(() =>
    showFullOutput.value ? compiled.full : compiled.excerpt
  );
      const outputLabel = useComputed(() =>
    showFullOutput.value ? "Show focused excerpts" : "Show full emitted module"
  );
      useEffect(() => {
    onCommit(rowCount);
  }, () => ((rowCount = $memo_rowCount.value, [rowCount])));
      return () => {
        const $el1 = $rt.element("main", $n);
        $el1.setAttribute("class", "showcase");
        const $el2 = $rt.element("section", $n);
        $el2.setAttribute("class", "hero");
        const $el3 = $rt.element("div", $n);
        $el3.setAttribute("class", "eyebrow");
        const $text4 = $rt.text("July 2026 capability update");
        $el3.appendChild($text4);
        $el2.appendChild($el3);
        const $el5 = $rt.element("h1", $n);
        // … direct instructions for the remaining static sections …

        const $map204 = $rt.map($ctx, () => (($propsVersion0.value, benchmarks.keyed.operations)), (operation, $index, $ctx, $item205) => {
          const $el206 = $rt.element("tr", $n);
          const $el207 = $rt.element("td", $n);
          const $text208 = $rt.text("");
          $el207.appendChild($text208);
          $el206.appendChild($el207);
          const $el209 = $rt.element("td", $n);
          const $text210 = $rt.text("");
          $el209.appendChild($text210);
          $el206.appendChild($el209);
          let $value211, $value212;
          $item205.refresh = $ctx.observe(() => {
            let $error, $next;
            if ($value211 !== $ctx) try {
              $next = $rt.textValue($item205.value.label);
              if ($next !== $value211) { $value211 = $next; if ($text208.data !== $next) $text208.data = $next; }
            } catch ($caught) { $value211 = $ctx; $error ??= $caught; }
            if ($value212 !== $ctx) try {
              $next = $rt.textValue($item205.value.lab);
              if ($next !== $value212) { $value212 = $next; if ($text210.data !== $next) $text210.data = $next; }
            } catch ($caught) { $value212 = $ctx; $error ??= $caught; }
            if ($error) throw $error;
          }, true, true).refresh;
          return $el206;
        }, (operation) => (operation.label), false);
        $el203.appendChild($map204);
        $el196.appendChild($el203);
        $el195.appendChild($el196);
        $el186.appendChild($el195);
        $el185.appendChild($el186);
        const $el213 = $rt.element("article", $n);
        $el213.setAttribute("class", "benchmark-table-card");
        const $el214 = $rt.element("header", $n);
        const $el215 = $rt.element("div", $n);
        const $el216 = $rt.element("span", $n);
        // … remaining keyed-row bindings omitted …

export function createApp(host = document, { hydratable = true } = {}) {
  const $module = $createModule(host);
  const $rt = $module.runtime;
  return {
    render(container, props = {}) {
      const $ctx = $rt.createContext(props, true, hydratable);
      const $value = $own($ctx, () => $module.create($ctx, props, $rt.ns(container)));
      container.textContent = "";
      const $root = $rt.mount(container, $value);
      $own($ctx, () => $ctx.commit());
      return { root: $root, dispose: () => $ctx.dispose() };
    },
    renderToString(props = {}, { hydratable: $boundaries = true } = {}) {
      const $container = host.createElement("div");
      const $ctx = $rt.createContext(props, false, $boundaries);
      try {
        const $value = $module.create($ctx, props);
        $rt.mount($container, $value);
  // … renderToString() and hydrate() share the generated template shape …
Honest boundaries

Useful now. Still a compiler lab.

The remaining boundary is architectural, not hidden test debt: portals, streaming SSR, preact/compat, arbitrary VDOM-valued expressions, and runtime Suspense/error boundaries are not implemented. The compiler stays separate and opt-in until real application evidence justifies a stronger compatibility claim.

Read the evidence

Design notes, limits, and reproducible proof

The repository documents how each claim is measured and where the compiler deliberately stops.