Skip to content

SSR Reference

Generated from src/ssr/index.ts.

ExportKindSourceDescription
__callServerAction__functionsrc/ssr/action-client.tsCalled by server action stubs in the client bundle. Routes the call to POST /_snapshot/action on the server and returns the action’s result. Handles three response shapes from the server: - { result } — returned as-is to the caller - { error } — throws Error(message) - { redirect } — navigates via window.location.href When the first argument is a FormData instance the request is sent as a raw FormData body (for <form action={serverFn}> usage). Otherwise the call is serialised as JSON.
buildComponentIdfunctionsrc/ssr/rsc.tsBuilds the component ID string used as a key in {@link RscManifest.components}. Format: "{relativePath}#{exportName}"
createPprCachefunctionsrc/ssr/ppr-cache.tsCreate an in-memory PPR shell cache. At build time, shells are pre-computed for all PPR-enabled routes and stored here via set(). At request time, get() returns the shell instantly so the HTTP response can begin with the pre-computed HTML without any rendering overhead, while dynamic Suspense slots are streamed in behind it. Factory, not singleton. Call createPprCache() once at application startup and pass the instance to both prerenderPprShells() (build step) and renderPprPage() (request handler). Do not share instances across processes.
createReactRendererfunctionsrc/ssr/renderer.tsCreate the official React renderer for slingshot-ssr. Returns an object that satisfies SlingshotSsrRenderer from @lastshotlabs/slingshot-ssr by structural typing. The consumer app imports both packages and wires them together — no forced dependency between repos. File-based routing: createReactRenderer relies on slingshot-ssr’s built-in file resolver to match URLs to route files. The resolve() method returns null — the file resolver is authoritative. render() is called only when a match was found. Per-request isolation: Every call to render() creates a fresh QueryClient. The global snapshot.queryClient singleton is never used during SSR. Config freeze: The config object is frozen at construction time.
extractPprShellfunctionsrc/ssr/ppr.tsRender only the static shell of a React tree. Dynamic Suspense boundaries are replaced with their fallback content because renderToString emits Suspense fallback markup immediately for any boundary whose children suspend, then terminates without awaiting the async subtree. Wrapping the tree in StaticShellWrapper ensures the output contains only the static parts and pre-populated fallback markup. Used at build time to pre-render the static portions of PPR routes. The resulting shellHtml is stored in the PPR cache and sent immediately on every subsequent request before dynamic content is streamed.
hasUseClientDirectivefunctionsrc/ssr/rsc.tsReturns true if the source file contains a 'use client' or "use client" directive as its first meaningful content (before any non-whitespace, non-comment code). The check is intentionally loose — it matches the directive anywhere in the leading whitespace/comment region of the file, consistent with how bundlers like webpack and Parcel handle it.
hasUseServerDirectivefunctionsrc/ssr/rsc.tsReturns true if the source file contains a 'use server' or "use server" directive as its first meaningful content.
PprCacheinterfacesrc/ssr/ppr-cache.tsInterface for the PPR static shell cache. Implementations are free to use any backing store; the default produced by createPprCache() is in-process memory (suitable for single-instance servers). For multi-instance deployments, provide a Redis-backed implementation.
PprCacheEntryinterfacesrc/ssr/ppr-cache.tsA single entry in the PPR shell cache. Stored by PprCache.set() after build-time shell extraction. Retrieved by PprCache.get() at request time to serve the shell immediately.
PprShellinterfacesrc/ssr/ppr.tsThe result of a build-time static shell extraction for a PPR route.
renderPagefunctionsrc/ssr/render.tsRender a React element tree to a streaming HTML Response. Creates per-request QueryClient and HydrationBoundary wrapping the element, dehydrates the cache into the HTML, and streams the response. Per-request isolation: The context.queryClient is created fresh by the caller for each request. This function wraps it with providers and then allows it to be garbage-collected. Never pass a shared or cached QueryClient. Streaming: Uses React 19’s renderToReadableStream. The <head> preamble is written synchronously before the React stream begins. Suspense boundaries stream in as their promises resolve. Abort on timeout: An AbortController is created per call. If the render does not complete within timeoutMs, controller.abort() is called and the stream rejects. The caller (slingshot-ssr middleware) catches this and falls through to the SPA. ISR opt-out: The entire render executes within a withRequestStore() async context (from ./cache). If any loader calls unstable_noStore() during the render, getNoStore() returns true after the render completes and shell._isr.noStore is set to signal the ISR middleware to skip the cache write. RSC mode: When rscOptions is provided, the function performs a two-pass React Server Components render instead of the standard single-pass render: 1. The React tree is rendered to the RSC flight format via react-server-dom-webpack/server. 2. The RSC flight stream is piped through react-dom/server to produce HTML. When rscOptions is omitted, the existing single-pass render is used unchanged.
RenderPprOptionsinterfacesrc/ssr/render.tsOptions for renderPprPage().
renderPprPagefunctionsrc/ssr/render.tsRender a PPR (Partial Prerendering) route. When a pre-computed shell is available in the PPR cache, this function: 1. Immediately sends the shell HTML (including Suspense fallbacks) as the first chunk of a streaming Response — this achieves instant TTFB. 2. Pipes renderToReadableStream output after the shell. React’s streaming renderer emits inline <script> chunks that replace each Suspense fallback with the resolved dynamic content as promises settle. When no shell is cached, the function returns a standard streaming SSR response via renderPage() as a transparent fallback so the route still works correctly before build-time pre-rendering has run. Streaming contract: - Response uses Transfer-Encoding: chunked and Content-Type: text/html. - The shell chunk is written before any async work begins. - React’s hydration <script> tags keep the client in sync with the server.
RscManifestinterfacesrc/ssr/rsc.tsMaps client component IDs to their output chunk URLs. Generated by the snapshotSsr() Vite plugin as rsc-manifest.json in the client output directory. The manifest is consumed at runtime by renderPage() to resolve client references during the RSC two-pass render. Key format: "{relativePath}#{exportName}" - relativePath — file path relative to the Vite project root, using forward slashes. Example: "src/components/Button.tsx" - exportName — the name of the export. Default exports use "default". Value format: The hashed chunk URL as it appears in the Vite client manifest. Example: "assets/Button-Bx2kLm9a.js"
RscOptionsinterfacesrc/ssr/rsc.tsOptions for RSC-enabled rendering passed to renderPage(). When this object is provided, renderPage() performs a two-pass RSC render: 1. The React tree is rendered to the RSC flight format using react-server-dom-webpack/server. 2. The RSC flight stream is piped through React DOM server to produce HTML. When omitted, renderPage() falls back to the standard single-pass react-dom/server render (no RSC).
safeJsonStringifyfunctionsrc/ssr/state.tsJSON-stringify a value with XSS-safe escaping. Escapes </ as <\/ and <!-- as <\!-- in the JSON output. Both are valid JSON and both prevent the HTML parser from misinterpreting the script content.
ServerRouteMatchShapeinterfacesrc/ssr/types.tsStructural equivalent of SsrRouteMatch from @lastshotlabs/slingshot-ssr. Defined here without importing from slingshot-ssr to avoid cross-repo coupling. TypeScript structural typing ensures compatibility at the consumer’s compile time.
SnapshotSsrConfiginterfacesrc/ssr/types.tsConfiguration for createReactRenderer().
SsrElementTransformContextinterfacesrc/ssr/types.tsContext supplied to an application’s optional SSR root-element transformer. The matched loader has run and the request QueryClient is already seeded.
SsrForbiddenResultinterfacesrc/ssr/types.tsSignal from a server route’s load() that the user lacks permission. slingshot-ssr responds with 403 Forbidden. Co-locate a forbidden.ts convention file to render a custom UI instead of a plain-text fallback.
SsrLoadContextinterfacesrc/ssr/types.tsThe context object passed to every server route load() and meta() function. Provides request data and direct access to the slingshot instance for DB calls without HTTP round-trips.
SsrLoaderReturntypealiassrc/ssr/types.tsAll valid return types from a server route’s load() function.
SsrLoadResultinterfacesrc/ssr/types.tsSuccessful result from a server route’s load() function. Both data and queryCache must be JSON-serializable — they are embedded in the HTML as dehydrated state for client hydration. The renderer enforces this by JSON round-tripping both before render, so the server component tree sees exactly the values the client will hydrate with (e.g. a Date read straight from a storage adapter renders as its ISO string on both sides instead of causing a hydration mismatch).
SsrMetainterfacesrc/ssr/head.tsSsrMeta shape — structural equivalent of SsrMeta from @lastshotlabs/slingshot-ssr. Defined here so consumers can import from @lastshotlabs/snapshot/ssr only, without needing to install slingshot-ssr as a dependency.
SsrNotFoundResultinterfacesrc/ssr/types.tsSignal from a server route’s load() that the resource was not found. slingshot-ssr falls through to the SPA, which renders its own 404 page.
SsrQueryCacheEntryinterfacesrc/ssr/types.tsA TanStack Query cache entry to pre-seed during SSR. The queryKey must match exactly the key used by the corresponding client-side useQuery() hook. On hydration, the client reads this entry from the dehydrated state and skips the network request.
SsrRedirectResultinterfacesrc/ssr/types.tsSignal from a server route’s load() that the client should be redirected.
SsrRequestContextinterfacesrc/ssr/types.tsPer-request SSR context. Created fresh for every render call. Never shared between concurrent requests.
SsrShellShapeinterfacesrc/ssr/types.tsStructural equivalent of SsrShell from @lastshotlabs/slingshot-ssr. Contains the HTML tag strings injected into the document <head> by slingshot-ssr. Also carries the _isr sink which the renderer populates after calling load().
SsrUnauthorizedResultinterfacesrc/ssr/types.tsSignal from a server route’s load() that the user is not authenticated. slingshot-ssr responds with 401 Unauthorized. Co-locate an unauthorized.ts convention file to render a custom UI instead of a plain-text fallback.
StaticShellWrapperfunctionsrc/ssr/ppr.tsWrap a React element so that all Suspense boundaries render only their fallbacks (never await the actual children). Used for static shell extraction. Place this as the outermost wrapper during build-time shell extraction. At request time it is not used — the real React tree renders normally.
unstable_noStorefunctionsrc/ssr/cache.tsOpt the current request out of ISR caching. Call this inside a loader when the response must never be stored in the ISR cache — for example, when the page contains personalised or real-time data. The revalidate value returned by load() is ignored for this request; the response is served fresh and never written to the cache. Must be called within the async context of renderPage() (i.e., inside a loader invoked during SSR). Calling it outside that context is a no-op.
usePrefetchRoutefunctionsrc/ssr/prefetch.tsReturns a callback that prefetches the JS chunks and CSS files for a given URL path by injecting <link rel="prefetch"> tags into document.head. The prefetch manifest (/prefetch-manifest.json) is loaded once per page load and cached. Duplicate prefetch injections for the same URL are suppressed. Safe to call during SSR — all document access is guarded. The returned function is a no-op on the server.

__callServerAction__(action: string, module: string, args: unknown[]) => Promise<unknown>

Section titled “__callServerAction__(action: string, module: string, args: unknown[]) => Promise<unknown>”

Called by server action stubs in the client bundle.

Routes the call to POST /_snapshot/action on the server and returns the action’s result. Handles three response shapes from the server:

  • { result } — returned as-is to the caller
  • { error } — throws Error(message)
  • { redirect } — navigates via window.location.href

When the first argument is a FormData instance the request is sent as a raw FormData body (for <form action={serverFn}> usage). Otherwise the call is serialised as JSON.

Parameters:

NameDescription
actionThe exported function name from the action module.
moduleThe module name (relative to the server actions directory).
argsArguments to forward to the server function.

Returns: The value returned by the server function.

Example:

// Generated client stub — do not write this manually.
import { __callServerAction__ } from '@lastshotlabs/snapshot/ssr';
export async function createPost(...args: unknown[]) {
return __callServerAction__('createPost', 'posts', args);
}

buildComponentId(relativePath: string, exportName: string) => string

Section titled “buildComponentId(relativePath: string, exportName: string) => string”

Builds the component ID string used as a key in {@link RscManifest.components}.

Format: "{relativePath}#{exportName}"

Parameters:

NameDescription
relativePathFile path relative to the Vite project root, forward-slash separated. Example: "src/components/Button.tsx".
exportNameExport name. Use "default" for default exports.

Example:

buildComponentId('src/components/Button.tsx', 'default');
// → 'src/components/Button.tsx#default'
buildComponentId('src/components/Button.tsx', 'Button');
// → 'src/components/Button.tsx#Button'

Create an in-memory PPR shell cache.

At build time, shells are pre-computed for all PPR-enabled routes and stored here via set(). At request time, get() returns the shell instantly so the HTTP response can begin with the pre-computed HTML without any rendering overhead, while dynamic Suspense slots are streamed in behind it.

Factory, not singleton. Call createPprCache() once at application startup and pass the instance to both prerenderPprShells() (build step) and renderPprPage() (request handler). Do not share instances across processes.

Returns: A frozen PprCache backed by a plain Map.


createReactRenderer(config: SnapshotSsrConfig) => { resolve(url: URL, bsCtx: unknown): Promise<ServerRouteMatchShape | null>; render(match: ServerRouteMatchShape, shell: SsrShellShape, bsCtx: unknown): Promise<...>; ren...

Section titled “createReactRenderer(config: SnapshotSsrConfig) => { resolve(url: URL, bsCtx: unknown): Promise<ServerRouteMatchShape | null>; render(match: ServerRouteMatchShape, shell: SsrShellShape, bsCtx: unknown): Promise<...>; ren...”

Create the official React renderer for slingshot-ssr.

Returns an object that satisfies SlingshotSsrRenderer from @lastshotlabs/slingshot-ssr by structural typing. The consumer app imports both packages and wires them together — no forced dependency between repos.

File-based routing: createReactRenderer relies on slingshot-ssr’s built-in file resolver to match URLs to route files. The resolve() method returns null — the file resolver is authoritative. render() is called only when a match was found.

Per-request isolation: Every call to render() creates a fresh QueryClient. The global snapshot.queryClient singleton is never used during SSR.

Config freeze: The config object is frozen at construction time.

Parameters:

NameDescription
configRenderer configuration. resolveComponent is required.

Returns: An object with resolve() and render() satisfying SlingshotSsrRenderer.

Example:

import { createSsrPlugin } from '@lastshotlabs/slingshot-ssr'
import { createReactRenderer } from '@lastshotlabs/snapshot/ssr'
createSsrPlugin({
renderer: createReactRenderer({
resolveComponent: async (match) => {
const mod = await import(toClientPath(match.filePath))
return mod.default
},
}),
serverRoutesDir: import.meta.dir + '/server/routes',
assetsManifest: import.meta.dir + '/dist/.vite/manifest.json',
})

extractPprShell(element: ReactElement<unknown, string | JSXElementConstructor<any>>) => Promise<PprShell>

Section titled “extractPprShell(element: ReactElement<unknown, string | JSXElementConstructor<any>>) => Promise<PprShell>”

Render only the static shell of a React tree.

Dynamic Suspense boundaries are replaced with their fallback content because renderToString emits Suspense fallback markup immediately for any boundary whose children suspend, then terminates without awaiting the async subtree. Wrapping the tree in StaticShellWrapper ensures the output contains only the static parts and pre-populated fallback markup.

Used at build time to pre-render the static portions of PPR routes. The resulting shellHtml is stored in the PPR cache and sent immediately on every subsequent request before dynamic content is streamed.

Parameters:

NameDescription
elementThe React element representing the full page tree.

Returns: A PprShell containing the extracted static HTML and a success flag.


hasUseClientDirective(code: string) => boolean

Section titled “hasUseClientDirective(code: string) => boolean”

Returns true if the source file contains a 'use client' or "use client" directive as its first meaningful content (before any non-whitespace, non-comment code).

The check is intentionally loose — it matches the directive anywhere in the leading whitespace/comment region of the file, consistent with how bundlers like webpack and Parcel handle it.

Parameters:

NameDescription
codeRaw source text of the file.

hasUseServerDirective(code: string) => boolean

Section titled “hasUseServerDirective(code: string) => boolean”

Returns true if the source file contains a 'use server' or "use server" directive as its first meaningful content.

Parameters:

NameDescription
codeRaw source text of the file.

renderPage(element: ReactElement<unknown, string | JSXElementConstructor<any>>, context: SsrRequestContext, shell: SsrShellShape, timeoutMs?: number, rscOptions?: RscOptions | undefined, responseInit?: { ...; }...

Section titled “renderPage(element: ReactElement<unknown, string | JSXElementConstructor<any>>, context: SsrRequestContext, shell: SsrShellShape, timeoutMs?: number, rscOptions?: RscOptions | undefined, responseInit?: { ...; }...”

Render a React element tree to a streaming HTML Response.

Creates per-request QueryClient and HydrationBoundary wrapping the element, dehydrates the cache into the HTML, and streams the response.

Per-request isolation: The context.queryClient is created fresh by the caller for each request. This function wraps it with providers and then allows it to be garbage-collected. Never pass a shared or cached QueryClient.

Streaming: Uses React 19’s renderToReadableStream. The <head> preamble is written synchronously before the React stream begins. Suspense boundaries stream in as their promises resolve.

Abort on timeout: An AbortController is created per call. If the render does not complete within timeoutMs, controller.abort() is called and the stream rejects. The caller (slingshot-ssr middleware) catches this and falls through to the SPA.

ISR opt-out: The entire render executes within a withRequestStore() async context (from ./cache). If any loader calls unstable_noStore() during the render, getNoStore() returns true after the render completes and shell._isr.noStore is set to signal the ISR middleware to skip the cache write.

RSC mode: When rscOptions is provided, the function performs a two-pass React Server Components render instead of the standard single-pass render:

  1. The React tree is rendered to the RSC flight format via react-server-dom-webpack/server.
  2. The RSC flight stream is piped through react-dom/server to produce HTML. When rscOptions is omitted, the existing single-pass render is used unchanged.

Parameters:

NameDescription
elementThe React element to render (already constructed with props).
contextPer-request context containing a fresh QueryClient.
shellAsset and head tags from slingshot-ssr. headTags should already be populated by the renderer before calling this function.
timeoutMsAbort timeout in milliseconds. Default: 5000.
rscOptionsOptional RSC manifest. When provided, enables RSC two-pass rendering. When omitted, standard SSR is used (no RSC).
responseInitOptional response overrides (status/headers).

Returns: A streaming Response with Content-Type: text/html; charset=utf-8.


renderPprPage(options: RenderPprOptions) => Promise<Response>

Section titled “renderPprPage(options: RenderPprOptions) => Promise<Response>”

Render a PPR (Partial Prerendering) route.

When a pre-computed shell is available in the PPR cache, this function:

  1. Immediately sends the shell HTML (including Suspense fallbacks) as the first chunk of a streaming Response — this achieves instant TTFB.
  2. Pipes renderToReadableStream output after the shell. React’s streaming renderer emits inline <script> chunks that replace each Suspense fallback with the resolved dynamic content as promises settle.

When no shell is cached, the function returns a standard streaming SSR response via renderPage() as a transparent fallback so the route still works correctly before build-time pre-rendering has run.

Streaming contract:

  • Response uses Transfer-Encoding: chunked and Content-Type: text/html.
  • The shell chunk is written before any async work begins.
  • React’s hydration <script> tags keep the client in sync with the server.

Parameters:

NameDescription
optionsPPR render options including the element tree, shell, head, and bootstrap script URLs.

Returns: A streaming Response with Content-Type: text/html; charset=utf-8.


Maps client component IDs to their output chunk URLs.

Generated by the snapshotSsr() Vite plugin as rsc-manifest.json in the client output directory. The manifest is consumed at runtime by renderPage() to resolve client references during the RSC two-pass render.

Key format: "{relativePath}#{exportName}"

  • relativePath — file path relative to the Vite project root, using forward slashes. Example: "src/components/Button.tsx"
  • exportName — the name of the export. Default exports use "default".

Value format: The hashed chunk URL as it appears in the Vite client manifest. Example: "assets/Button-Bx2kLm9a.js"

Example:

{
"components": {
"src/components/Button.tsx#default": "assets/Button-Bx2kLm9a.js",
"src/components/PostCard.tsx#PostCard": "assets/PostCard-Lm9aBx2k.js"
}
}

safeJsonStringify(value: unknown) => string

Section titled “safeJsonStringify(value: unknown) => string”

JSON-stringify a value with XSS-safe escaping.

Escapes </ as <\/ and <!-- as <\!-- in the JSON output. Both are valid JSON and both prevent the HTML parser from misinterpreting the script content.

Parameters:

NameDescription
valueAny JSON-serializable value.

Returns: A JSON string safe for embedding in a <script> tag.


Signal from a server route’s load() that the user lacks permission.

slingshot-ssr responds with 403 Forbidden. Co-locate a forbidden.ts convention file to render a custom UI instead of a plain-text fallback.

Example:

export async function load(ctx: SsrLoadContext) {
const user = await ctx.getUser()
if (!user) return { unauthorized: true }
if (!user.roles.includes('admin')) return { forbidden: true }
return { data: { ... } }
}

The context object passed to every server route load() and meta() function.

Provides request data and direct access to the slingshot instance for DB calls without HTTP round-trips.

Example:

server/routes/posts/[slug].ts
import type { SsrLoadContext, SsrLoaderReturn } from '@lastshotlabs/snapshot/ssr'
export async function load(ctx: SsrLoadContext): Promise<SsrLoaderReturn> {
const user = await ctx.getUser()
if (!user) return { redirect: '/login' }
const post = await getPost(ctx.params.slug, ctx.bsCtx)
if (!post) return { notFound: true }
return { data: { post }, queryCache: [{ queryKey: ['post', ctx.params.slug], data: post }] }
}

Signal from a server route’s load() that the client should be redirected.

Example:

export async function load(ctx: SsrLoadContext) {
if (!await ctx.getUser()) return { redirect: '/login' }
}

Signal from a server route’s load() that the user is not authenticated.

slingshot-ssr responds with 401 Unauthorized. Co-locate an unauthorized.ts convention file to render a custom UI instead of a plain-text fallback.

Example:

export async function load(ctx: SsrLoadContext) {
const user = await ctx.getUser()
if (!user) return { unauthorized: true }
return { data: { ... } }
}

StaticShellWrapper({ children, }: { children?: ReactNode; }) => ReactElement<unknown, string | JSXElementConstructor<any>>

Section titled “StaticShellWrapper({ children, }: { children?: ReactNode; }) => ReactElement<unknown, string | JSXElementConstructor<any>>”

Wrap a React element so that all Suspense boundaries render only their fallbacks (never await the actual children). Used for static shell extraction.

Place this as the outermost wrapper during build-time shell extraction. At request time it is not used — the real React tree renders normally.

Parameters:

NameDescription
props.children- The React tree to wrap.

Returns: A React element whose Suspense boundaries resolve to their fallbacks.


Opt the current request out of ISR caching.

Call this inside a loader when the response must never be stored in the ISR cache — for example, when the page contains personalised or real-time data. The revalidate value returned by load() is ignored for this request; the response is served fresh and never written to the cache.

Must be called within the async context of renderPage() (i.e., inside a loader invoked during SSR). Calling it outside that context is a no-op.

Example:

import { unstable_noStore } from '@lastshotlabs/snapshot/ssr';
export async function load(ctx: SsrLoadContext) {
unstable_noStore(); // this response is never cached
const feed = await getLiveActivityFeed(ctx.bsCtx);
return { data: { feed } };
}

usePrefetchRoute() => (path: string) => void

Section titled “usePrefetchRoute() => (path: string) => void”

Returns a callback that prefetches the JS chunks and CSS files for a given URL path by injecting <link rel="prefetch"> tags into document.head.

The prefetch manifest (/prefetch-manifest.json) is loaded once per page load and cached. Duplicate prefetch injections for the same URL are suppressed.

Safe to call during SSR — all document access is guarded. The returned function is a no-op on the server.

Returns: A stable callback (path: string) => void. Call it with the URL path to prefetch (e.g. "/posts/my-slug").

Example:

function MyLink() {
const prefetch = usePrefetchRoute();
return (
<a href="/posts" onMouseEnter={() => prefetch('/posts')}>
Posts
</a>
);
}