docs / Compiler

Client Reachability

The compiler's answer to the client/server boundary: the needsClient check, per-block validation, and per-target stripping.

"Client reachability" is the compiler's answer to: does this component need to exist on the client at all, and which parts of its body belong to which side? It is decided at compile time by three mechanisms.

Per-component island flag

client-codegen.ts
									
										const needsClient = ir.components.some((c) => c.isClient || !isStaticComponent(c));
									
								
  • A `client` island always needs client code — it renders on both server and client.
  • A non-client component needs client code only when its body is not fully static (reactive content, on* handlers, effects, bindings).
  • A module where every component is static and non-client compiles to an empty client bundle (compileClient returns '' unless forceClient: true).

Per-block validation

`validateBlocks(compName, isClient, body)` enforces the boundary statically per component kind: a `client` component containing `{#server}` raises serverBlockInClient; a server component containing `{#client}` raises clientBlockInServer. The check recurses through StaticNode, ServerBlock and ClientBlock children, so the rules apply at any nesting depth.

Per-target stripping

Node Server codegen Client codegen
ServerBlock ({#server}) rendered dropped
ClientBlock ({#client}) dropped rendered

Client bundle

The browser bundle is built from the runtime's index-client barrel (tree-shaken to the names actually used) plus hydration entry points: hydrate, hydrateViewport, hydrateIdle, hydrateOnInteraction, needsHydration, createHydrateWalker, collectVskMarkers, reactiveProps. In code-split mode it is split into per-route `page-<name>.js` chunks.