Because reactolith resolves component tags lazily from the rendered
HTML, the browser only discovers which chunks it needs after
parsing the page. You can close that gap by emitting preload hints
for the chunks a page actually uses — either as response
headers (Link: rel=modulepreload, optionally flushed
early as 103 Early Hints) or as
<link rel="modulepreload"> tags in the document
<head>.
HTTP/2 Server Push is not the mechanism here: it has been
removed from Chrome and is effectively dead. The supported
equivalents are Link preload hints and
103 Early Hints.
Two things have to line up at request time:
With those two pieces, the backend emits one preload hint per used tag, either inline in the head:
or as a response header (which a CDN or reverse proxy can also turn
into a 103 Early Hints response):
Build with the manifest enabled:
Vite writes dist/.vite/manifest.json. Each entry maps a
source path to its hashed output file plus any CSS and shared
imports that chunk pulls in:
The backend derives the source path from a tag name by the same
convention the loader uses (e.g. ui-button →
src/components/ui-button.tsx), looks it up in the
manifest, and emits the hints — including the chunk's
transitive imports and css so a single
round-trip warms everything the component needs:
The same data can equally well be rendered as
<link rel="modulepreload"> tags into the document
head — pick whichever fits your stack. If your edge supports
it, flush the Link header as a
103 Early Hints response so the browser starts fetching
chunks while the backend is still rendering the body.
reactolith itself does not need any of this — it's a deployment-time optimization. It's documented here so backend bundles can settle on the same tag-scanning + manifest-lookup convention.
This documentation site runs the recipe above as a Vite build
plugin — view source on any page and you'll find a block like
this in the <head>, generated from the tags that
page actually uses:
The implementation lives in
docs/vite-plugin-preload-component-chunks.ts —
about 80 lines — and is wired into
docs/vite.config.ts. After vite build
writes dist/.vite/manifest.json, the plugin's
closeBundle hook walks every built HTML, scans it for
custom-element tags, resolves each one through a small
tag→source-path function that mirrors the loader's prefix
rules, and injects the resulting
<link rel="modulepreload"> tags into the
<head>. CSS sidecars and transitive shared
chunks are followed too, so a single round-trip warms everything a
component needs.