mountApplication
Mount a Preact route application or hydrate an existing server-rendered document.
Compatibility
Import from @kasperrt/smolter/react/client @kasperrt/smolter/preact/client
Signature
function mountApplication(
routes: AppRoute[],
rootModule?: RootModule,
options: boolean | MountApplicationOptions = false,
): Promise<Router>;Parameters
Pass routes, an optional browser-safe rootModule, and an optional options object. options.hash enables hash history. options.deployment accepts the virtual deployment metadata and protects both the initial lazy import and later navigation. A boolean third argument remains supported for hash routing.
Deferred timeout compatibility in 0.4.5
In 0.4.5, the default initial deferred-data budget changes from 30 seconds to 8 seconds for both React and Preact. It starts when the hydration payload is revived; individual settlements do not restart it. For intentionally slower initial data, set deferredTimeout explicitly. This bounds silent document interruptions, not immediate disconnect detection, and does not retry loaders or mutations.
// Use the matching react/client or preact/client entry.
await mountApplication(routes, root, {
deferredTimeout: 30_000,
});Returns
Returns a Promise<Router>. Mounting waits for the initially matched route module to preload. The returned router exposes navigation, revalidation, fetchers, and disposal. Client-only mounting starts loaders through RouterProvider; the returned promise does not mean every loader has completed.
Client-only application
Provide a root element in the HTML document. Manually registered routes work without a file-router plugin. Keep route components and client handlers in their normal route modules.
import { defineRoute } from "@kasperrt/smolter/preact";
import { mountApplication } from "@kasperrt/smolter/preact/client";
import * as home from "./routes/_index/route";
import * as tasks from "./routes/tasks/route";
const routes = [
defineRoute("/", home),
defineRoute("/tasks", tasks),
];
const router = await mountApplication(routes);
// The HTML entry must contain <div id="root"></div>.
await router.navigate("/tasks");Server-rendered document
When a non-empty #smolter-data script exists, mounting reads its Hydration payload, restores serialized errors, and hydrates the document rather than rendering into #root. Use the matching server and browser route modules and root layout. Scripts emits the bootstrap payload for renderApplication. Keep server handlers out of the browser root module and use the file-router integration to split server route exports.
Bootstrap recovery
Pass deployment metadata during mounting; installing watchDeployment only after mounting cannot recover an initial import failure. A failed bootstrap checks for a different build and requests one reload per old build. Offline checks and unchanged builds preserve the original import error. The initial entry script itself must still be available.
import deployment from "virtual:smolter/deployment";
import routes from "virtual:smolter/routes";
import { mountApplication } from "@kasperrt/smolter/preact/client";
import * as root from "./root";
await mountApplication(routes, root, { deployment });Externally injected head elements
Whole-document hydration expects the server and client layouts to match. Mark externally injected direct children of head with data-smolter-preserve. mountApplication temporarily excludes those nodes from hydration and restores the same nodes afterward; the application must not also render them. This is an explicit integration contract, not automatic tolerance of arbitrary HTML rewriting. Do not use it for application-owned tags, body content, or changes to the hydration payload.
<script data-smolter-preserve src="/infrastructure-instrumentation.js"></script>
<meta data-smolter-preserve name="infrastructure-tag" content="example">Deferred data deadline
Initial deferred data has an 8,000 ms deadline after its hydration payload is read. A document connection can stop without firing a browser completion event; unfinished values then reject with Hydration stream timed out and reach their Await errorElement or error boundary. This is a total budget from payload revival: settlements do not restart it. It is not an inactivity timeout or a reliable disconnect detector. Set deferredTimeout to a larger millisecond budget for intentionally slow initial data (integer milliseconds from 1 through 2147483647; fractions, NaN, infinities and out-of-range values throw RangeError). There is one timer only when the initial payload contains pending values, cleared on completion or router disposal. No request or mutation is retried, and deployment recovery is not triggered. Ordinary resolved hydration creates no deadline timer.
await mountApplication(routes, root, {
deployment,
deferredTimeout: 60_000,
});React and Preact
Use react/client with React DOM 19+ or preact/client with Preact 11+. Both install scoped transport, root revalidation, retained navigation, browser or hash history, and deployment recovery before loading the initial route. Use matching server/client renderer imports. Neither entry bundles the other renderer.