Troubleshooting
AFW components run once at connect (init-once), not on every state change. Most "my UI didn't update" reports trace back to that model, signal reassignment rules, or server/client render drift. This page collects the frequent cases and where to read more.
The UI does not update when state changes
Put conditionals inside JSX holes
if (cond) return <A /> and early-return guards are evaluated once at connect. They are not reactive regions — flipping a $state later does not swap the branch.
// ❌ frozen at first connect if (!user) return <Login />; return <Main user={user} />; // ✅ reactive — inline hole or single return with JSX branches return user ? <Main user={user} /> : <Login />; return <div>{user ? <Main user={user} /> : <Login />}</div>;
Full walkthrough: Templating → Init-once and JSX → Unsupported patterns.
Do not cache JSX in a local variable
JSX assigned to const view = … is built once. Embedding {view} later is a plain value hole, not a reactive switch:
// ❌ branch fixed at connect const panel = user ? <Main /> : <Login />; return <div>{panel}</div>; // ✅ conditional inside the returned JSX return <div>{user ? <Main /> : <Login />}</div>;
Reassign $state, do not mutate it
Signals notify on reassignment, not in-place mutation. The compiler rejects common mutators (push, splice, property assignment, nested ++) on $state values with an error that points at the immutable rewrite.
// ✅ todos = [...todos, item]; todos = todos.filter((t) => t.id !== id); // ❌ compile error or silent no-op todos.push(item); todos[0].done = true;
When nested objects need direct mutation, use reactive() instead — see Reactivity.
Compile errors
| Error / pattern | Fix |
|---|---|
ref={(el) => …} callback ref | Use $ref() + onMount / $effect / $expose |
<Foo.Bar /> member component | Import Bar and render <Bar /> |
$state mutation (push, x.y = …, nested ++) | Reassign a new value, or switch to reactive() |
{ [key]: val } computed prop keys | Use static identifier keys in JSX attributes |
Lifecycle hook inside a helper / if / after await | Move onMount, onCleanup, etc. to top-level statements in the component body |
Callback refs and member components are called out in the JSX reference.
Hydration mismatches
With SSG or SSR, the client adopts the server HTML on first paint. If structure diverges, the runtime reports a hydration mismatch through the runtime error API and rebuilds that component via CSR. The rest of the page stays adopted.
Common causes:
Non-deterministic render —
Date.now(),Math.random(), or readingwindow/localStorageduring render. Gate client-only UI withonMountor a post-hydration signal.Loader vs client fetch — pre-rendered HTML should match what the client paints before its own fetch completes. Use route loaders so SSG/SSR inline the same data the client reads on hydration.
Different branches server vs client — avoid
user ? <Dashboard /> : <Login />whenuseris only known in the browser unless the server emits the same branch the client will adopt first.
Details: Hydration.
Multi-root fragment outputs (a page that returns <>…</> without a wrapper) are a known gap on the adopt path — the subtree may fall back to CSR. Wrap the page in a single element when you need reliable hydration.
Lifecycle hooks on pages
onResize and onVisibilityChange observe the host element. A page using them needs a single element root — not a bare fragment. The compiler warns when the root is a fragment.
onMediaQuery has no element constraint. All lifecycle macros must be top-level statements. Calls inside helpers or conditions are unsupported; component bodies must remain synchronous. During SSG/SSR they do not run at all — only in the client bundle.
See Lifecycle.
Compiler diagnostics (esdev start)
The compiler plugin returns file, line and column diagnostics to esdev. With the default project plugin, compile errors fail the build. Fix the reported location and wait for a successful rebuild.
The compiler emits source maps for JSX/TSX transforms, which esdev can compose with the bundle's map. Enable build source maps when you need to debug release output. Markdown and MDX first become generated JSX, so their diagnostics and maps can still point into that generated source rather than the original prose.
Runtime errors go through the framework's error-reporting API, including the afw:error event and console logging. <ErrorBoundary> can show a fallback while still reporting the error. The native compiler plugin does not supply the retired CLI's /__route stack rewriting or error-overlay controls.
See Error boundaries.
SEO and metadata
Per-route titles and share cards need SSG or SSR — each URL must ship its own
<title>,canonical, andgenerateMetadataoutput. A plain CSR build serves one sharedindex.htmlfor every route, with the head provided by that HTML entry. See Metadata & SEO.SPA navigation never updates
<head>— after first paint,<Link>navigations do not re-run metadata. Crawlers and social previews use the document for the URL they fetched, not the client-rendered view.Missing absolute URLs — set
site.urlinafw.config.jsfor your prerender script so canonicals,og:image, and the sitemap are absolute.Dynamic routes not in the sitemap — export
getStaticPathswhen pre-rendering with a configured prerender target; otherwise those paths have no static HTML (but a request renderer / SSR still resolvesgenerateMetadataper request).
Quick decision tree
UI stuck after setState? ├─ conditional outside {…} in JSX? → move into a JSX hole ├─ JSX stored in const/let? → inline the conditional └─ mutating array/object in place? → reassign or use reactive() Compile failed? ├─ callback ref / <Foo.Bar /> → $ref / direct import └─ $state.push or x.y = … → immutable reassignment Flash or hydration warning on load? ├─ Date.now / window during render? → onMount or loader data └─ fragment page root + observers? → wrap in one element Wrong title in Slack/Google? ├─ plain CSR only? → configure prerendering or a request renderer for per-route <head> └─ deep link works but in-app nav wrong? → expected: <head> is not updated on <Link>
Related guides
JSX — supported patterns and pitfalls table
Templating — reactive conditionals and lists
Reactivity —
$state,reactive(),$effectHydration — adopt path and mismatch recovery
Metadata & SEO — titles, OG tags,
site.urlData Fetching — loaders aligned with pre-rendered HTML