---
url: >-
  https://lit-ui-router.dev/reference/lit-ui-router-ssr/index/functions/prerender.md
---
[lit-ui-router-ssr](../../index.md) / [lit-ui-router-ssr](../index.md) / prerender

# Function: prerender()

```ts
function prerender(options): Promise<PrerenderResult>;
```

Defined in: [packages/lit-ui-router-ssr/src/prerender.ts:291](https://github.com/simshanith/lit-ui-router/blob/6e47ef8d8a4b24e99c074fb5c8c9cfaddfdb36a4/packages/lit-ui-router-ssr/src/prerender.ts#L291)

Emits a static site from a mount table's verdicts: a shell verdict becomes
`<subpath>/index.html`, or a rules line when the transition its
`renderShell` landed was redirected, a response state becomes the body or rules line its
handler in [responses](../interfaces/PrerenderOptions.md#responses) answers, a
redirect becomes a rules line and no page, and the `otherwise` projection
becomes the 404 document. The loop is
`ui-router-server/prerender`'s `prerender`; this supplies its lit render.

The router is provided on the render root for `context-request` and scoped
with `withRouterSync` around each render, so a template's `<ui-router>`
descendants and its sref attribute directives read the same router. Renders
run one at a time: the scope is a module slot, so there is nothing to
parallelise.

A rendered page opens on its hydration signature: a JSON data block,
`<script type="application/json" data-lit-ui-router-ssr>`, naming this
package's version, the state and parameter values the router stood on, and
the fully qualified name of each `<ui-view>` it drew a routed component
into, which `hydrateRoot()` reads before it touches the document and
`bootstrap()` reports each view's outcome against.
[\`dehydrate\`](../interfaces/PrerenderOptions.md#dehydrate), or a
[\`payload\`](../interfaces/PrerenderOptions.md#payload) hook, adds the resolved values
the page was drawn with, which `bootstrap()` adopts; the page's entry in
the result's `pages` reports the bytes they add and, from `dehydrate`, the
resolves left to re-run on the client. A `renderShell`
that returns a string is written as-is, with no signature, so the client
renders it cold.

Pages are rendered with `deferHydration`, so every custom element one holds
carries `defer-hydration` and renders nothing until `hydrateRoot()`'s walk
reaches it on the client. A property binding emits only its part marker:
the property is set when the page hydrates, so what an element must show on
the served page arrives as an attribute or as children.

## Parameters

| Parameter | Type | Description |
| ------ | ------ | ------ |
| `options` | [`PrerenderOptions`](../interfaces/PrerenderOptions.md) | the mount table, the router, the paths, and the hooks |

## Returns

`Promise`<[`PrerenderResult`](../interfaces/PrerenderResult.md)>

what was emitted, planned or written

## Throws

an `Error` when neither `mounts` nor `resolver` is supplied

## Throws

an `Error` when the 404 document needs a default probe and only a `resolver` is supplied

## Throws

an `Error` when both `dehydrate` and `payload` are supplied

## Example

```ts
import type { TemplateResult } from 'lit';
import type { UIRouterLit } from 'lit-ui-router';
import { prerender, settle, type PrerenderOptions } from 'lit-ui-router-ssr';

declare const mounts: PrerenderOptions['mounts'];
declare const router: UIRouterLit;
declare const titles: Map<string, string>;
declare function page(): TemplateResult;
declare function fillShell(title: string | undefined, body: string): string;

const result = await prerender({
  mounts,
  router,
  outDir: 'dist',
  paths: ['/', '/sheet/7B', '/legacy'],
  renderShell: async (verdict, { path }) => {
    await settle(router, path);
    return page();
  },
  document: (body, { path }) => fillShell(titles.get(path), body),
});
```
