From 8e2b407888360f791c5d2da2d2edb123e005cd19 Mon Sep 17 00:00:00 2001 From: Vivek Date: Mon, 1 Jun 2026 03:18:07 +0530 Subject: [PATCH] docs: note the preload walk stops at the .server boundary The no-build page's modulepreload section said preloads cover "transitive dependencies" and only carved out .server.* files themselves, which read as the pre-#161 behavior (a plain util imported through a server action would be preloaded). #161 made the walk stop at the .server boundary, so those transitive server-only deps are excluded too. Document that the preload set is exactly the set the page fetches. --- docs/app/docs/no-build/page.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/app/docs/no-build/page.ts b/docs/app/docs/no-build/page.ts index ed64082e2..61440f68c 100644 --- a/docs/app/docs/no-build/page.ts +++ b/docs/app/docs/no-build/page.ts @@ -69,7 +69,7 @@ export default function NoBuild() { <link rel="modulepreload" href="/components/avatar.ts"> <link rel="modulepreload" href="/lib/format-date.ts">

This converts a sequential import waterfall into a parallel fetch. The browser fires every request as soon as the HTML head is parsed, well before <script type="module"> at the bottom would have discovered them.

-

Server-only modules (filename matches .server.{js,ts} or content has a 'use server' directive) are excluded from preload hints. They never reach the browser as source. Lazy components (static lazy = true) are also excluded, since they load on viewport entry via IntersectionObserver, not page load.

+

Server-only modules (filename matches .server.{js,ts} or content has a 'use server' directive) are excluded from preload hints, and the dependency walk stops at them: a plain module reached only through a server file (a util that a server action imports) is excluded too. The browser only ever fetches the action's RPC stub, never the server file's own imports, so the preload set is exactly the set the page actually fetches. None of it reaches the browser as source. Lazy components (static lazy = true) are also excluded, since they load on viewport entry via IntersectionObserver, not page load.

Display-only components are excluded too, and not just from preloads. A component whose render() is a pure function of its inputs (no @event handler, no non-state reactive property, no overridden lifecycle hook, no signal / Task / streaming directive, no <slot>) does no client-side work, so its SSR'd HTML is the complete output. The server detects this statically and strips its side-effect import from the served page source, so the browser never downloads the module at all, and any npm package imported only by display-only components drops out of the importmap. This is the no-build equivalent of React Server Components' dead-JS elimination, with no bundler and no server/client directive. The analysis is conservative: anything it cannot prove inert keeps shipping. See Progressive Enhancement.

The module graph is also the authorisation gate