@wrnexus/ssr
Secure HTML document rendering and SEO metadata.
Install the package
After WorkRoot approves private registry access, install the release-aligned package:
bun add @wrnexus/ssr@0.8.7Request preview access. Never put registry tokens in source control.
Server-side rendering: wraps a page's HTML body in a complete HTML document with a metadata-driven <head>.
Part of the WRNexusJS framework — an SSR-first, Bun-native full-stack web framework.
Overview
Pages in WRNexusJS return an HTML string for the body. @wrnexus/ssr takes that body and produces a full HTML document — building the <head> from page metadata and global SEO defaults, resolving canonical/Open Graph/Twitter tags, and injecting module preloads and <script type="module"> tags. It is deliberately server-only: nothing in this package touches the DOM or ships to the browser, keeping server code genuinely server-only. Reach for it on the server when turning a rendered page body into a response document.
bun add @wrnexus/ssr
Private package — the machine must be authenticated to the wrnexus npm org
(a read token in ~/.npmrc). Requires Bun (Node is not supported).
API
The package has a single export.
renderDocument(opts: RenderOptions): string
Renders a complete HTML document as a string, beginning with <!doctype html>. All metadata is HTML-escaped (via escapeHtml from @wrnexus/core), so a malicious title or description cannot break out of its element or attribute. The body is placed inside <div id="app">.
RenderOptions
| Field | Type | Description |
|---|---|---|
meta | PageMeta | Page metadata for the document head (required). |
body | string | Rendered HTML for the body, placed inside #app (required). |
seo | SeoConfig | Global SEO defaults, typically from wrnexus.config.ts. |
url | URL | Current request URL, used to resolve canonical/Open Graph URLs. |
scripts | string[] | URLs of <script type="module"> tags to load (e.g. per-island chunks or the reactive runtime). Each also gets a <link rel="modulepreload">. |
defaultTitle | string | Default document title used when meta.title is absent. |
extraHead | string | Raw HTML injected at the end of <head> (trusted, framework-controlled — not escaped). |
extraBody | string | Raw HTML injected at the end of <body> (trusted, framework-controlled — not escaped). |
htmlAttrs | string | Attributes for the <html> element, e.g. data-theme="dark" (trusted). |
PageMeta and SeoConfig come from @wrnexus/core. PageMeta is an alias of SeoConfig, whose fields are all optional:
type SeoConfig = {
title?: string;
titleTemplate?: string; // e.g. "%s — My Site"; %s is replaced with the page title
description?: string;
canonical?: string;
canonicalBase?: string; // origin used to absolutize canonical/image URLs
robots?: string;
keywords?: string | string[];
image?: string;
siteName?: string;
type?: string; // Open Graph type; defaults to "website"
locale?: string;
twitterCard?: string; // defaults to "summary"
twitterSite?: string;
themeColor?: string;
};
Metadata resolution
renderDocument merges page metadata (meta) over global defaults (seo), field by field, so per-page values win. Notable behavior:
- Title: uses
meta.title, elseseo.title, elsedefaultTitle, else"WRNexusJS". When the page sets its own title andseo.titleTemplatecontains%s, the template is applied. - Canonical / image URLs: resolved against
canonicalBase(or the requesturl's origin) into absolute URLs when possible. - Keywords: an array is joined with
", ". - Emitted tags:
<title>, and as applicabledescription,robots,keywords,theme-color, andcanonicallink, plus Open Graph (og:title,og:description,og:type,og:url,og:site_name,og:locale,og:image) and Twitter (twitter:card,twitter:title,twitter:description,twitter:image,twitter:site) meta tags. The document always includescharset,viewport, and a/favicon.icoicon link.
Usage
Render an SEO-ready application page
import { renderDocument } from "@wrnexus/ssr";
const html = renderDocument({
meta: {
title: "About Us",
description: "Learn more about our team.",
},
seo: {
titleTemplate: "%s — Acme",
siteName: "Acme",
canonicalBase: "https://acme.example",
twitterSite: "@acme",
},
url: new URL("https://acme.example/about"),
body: "<h1>About Us</h1>",
scripts: ["/_wire/runtime.js", "/_wire/islands/about.js"],
htmlAttrs: ' data-theme="dark"',
});
return new Response(html, {
headers: { "content-type": "text/html; charset=utf-8" },
});
The produced document has <title>About Us — Acme</title>, the SEO/Open Graph/Twitter tags derived from the merged metadata, a modulepreload link and module <script> for each entry in scripts, and the body wrapped in <div id="app">.
Add trusted framework assets and boot data
Use extraHead and extraBody only for HTML generated by your application or the framework. User-provided values belong in meta, where they are escaped.
const html = renderDocument({
meta: { title: "Dashboard", robots: "noindex" },
body: dashboardHtml,
url: ctx.url,
extraHead: '<link rel="stylesheet" href="/_wrnexus/admin.css">',
extraBody: `<script type="application/json" id="boot">${JSON.stringify(bootData).replaceAll("<", "\\u003c")}</script>`,
});
return new Response(html, { headers: { "content-type": "text/html; charset=utf-8" } });
Requirements / Notes
- Server-only. This module never imports or touches the DOM and is safe to keep out of client bundles.
- Depends on [
@wrnexus/core](../core) forescapeHtmland thePageMeta/SeoConfigtypes. - Bun-only — like the rest of WRNexusJS, this package targets the Bun runtime (Node is not supported).
Complete TypeScript API
Generated from the exact installed package declarations.
import { PageMeta, SeoConfig } from '@wrnexus/core';
export { RpcContext, RpcHandlerOptions, RpcManifestContract, RpcParameterContract, RpcRequestPayload, createRpcHandler } from './rpc.js';
export { disposeRequestStores, renderStoreHydration, requestStoreContainer } from './store-context.js';
import '@wrnexus/store';
/**
* @wrnexus/ssr — server-side rendering.
*
* Pages return an HTML string for the body; this module wraps that body in a
* full document with a `<head>` built from page metadata. It is intentionally
* isolated from any client runtime: nothing here touches the DOM or ships to
* the browser, which keeps "server-only code" genuinely server-only.
*/
interface ScriptAsset {
src: string;
/** Module scripts are the default for backward compatibility. */
type?: "module" | "classic";
async?: boolean;
defer?: boolean;
integrity?: string;
crossOrigin?: "anonymous" | "use-credentials";
nonce?: string;
attributes?: Record<string, string | boolean>;
}
type RenderScript = string | ScriptAsset;
interface PartialPrerenderResult {
shell: string;
regions: Array<{
id: string;
html: string;
}>;
}
/** Extract compiler-emitted dynamic regions into a cacheable static shell. */
declare function partialPrerender(html: string, startIndex?: number): PartialPrerenderResult;
/** Stream the static shell first, followed by inert region templates for client insertion. */
declare function streamPartialDocument(result: PartialPrerenderResult, nonce?: string): ReadableStream<Uint8Array>;
interface RenderOptions {
/** Page metadata for the document head. */
meta: PageMeta;
/** Global SEO defaults from `wrnexus.config.ts`. */
seo?: SeoConfig;
/** Current request URL, used to resolve canonical/Open Graph URLs. */
url?: URL;
/** Rendered HTML for the body (placed inside `#app`). */
body: string;
/**
* URLs of `<script type="module">` tags to load (e.g. per-island chunks or
* the reactive runtime). Only the scripts a page actually needs are passed.
*/
scripts?: RenderScript[];
/** Optional default document title used when meta.title is absent. */
defaultTitle?: string;
/** Raw HTML injected at the end of `<head>` (trusted, framework-controlled). */
extraHead?: string;
/** CSP nonce applied to framework-promoted `.wrn` style blocks. */
styleNonce?: string;
/** Raw HTML injected at the end of `<body>` (trusted, framework-controlled). */
extraBody?: string;
/** Attributes for the `<html>` element, e.g. ` data-theme="dark"` (trusted). */
htmlAttrs?: string;
/**
* Optional application-authored full document shell. It must contain
* `<html>`, `<head>`, and `<body>`. Framework metadata, assets, and scripts
* are merged into it instead of wrapping the rendered body again.
*/
documentTemplate?: string;
}
/**
* Render a complete HTML document.
*
* Metadata is HTML-escaped so a malicious title/description can never break
* out of its element or attribute.
*/
declare function renderDocument(opts: RenderOptions): string;
interface ExtractedWrnexusStyles {
html: string;
styles: string;
}
/**
* Promote compiler-emitted `.wrn` style blocks out of rendered markup and into
* the document head. They are emitted after global stylesheets, deduplicated by
* stable id, ordered layout -> page -> component, and nonce-tagged for CSP.
*/
declare function extractWrnexusStyles(html: string, nonce?: string): ExtractedWrnexusStyles;
interface StreamRenderOptions extends Omit<RenderOptions, "body"> {
body: string | Promise<string> | AsyncIterable<string>;
}
/**
* Stream a complete document while preserving the exact head/body contract of
* `renderDocument`. Async iterables can flush a shell, primary content, and
* slower fragments without buffering the entire route.
*/
declare function renderDocumentStream(opts: StreamRenderOptions): ReadableStream<Uint8Array>;
declare function streamDocumentResponse(opts: StreamRenderOptions, init?: ResponseInit): Response;
export { type PartialPrerenderResult, type RenderOptions, type RenderScript, type ScriptAsset, type StreamRenderOptions, extractWrnexusStyles, partialPrerender, renderDocument, renderDocumentStream, streamDocumentResponse, streamPartialDocument };
Examples
Copy-ready examples from the installed package documentation.
Render an SEO-ready application page
import { renderDocument } from "@wrnexus/ssr";
const html = renderDocument({
meta: {
title: "About Us",
description: "Learn more about our team.",
},
seo: {
titleTemplate: "%s — Acme",
siteName: "Acme",
canonicalBase: "https://acme.example",
twitterSite: "@acme",
},
url: new URL("https://acme.example/about"),
body: "<h1>About Us</h1>",
scripts: ["/_wire/runtime.js", "/_wire/islands/about.js"],
htmlAttrs: ' data-theme="dark"',
});
return new Response(html, {
headers: { "content-type": "text/html; charset=utf-8" },
});Add trusted framework assets and boot data
const html = renderDocument({
meta: { title: "Dashboard", robots: "noindex" },
body: dashboardHtml,
url: ctx.url,
extraHead: '<link rel="stylesheet" href="/_wrnexus/admin.css">',
extraBody: `<script type="application/json" id="boot">${JSON.stringify(bootData).replaceAll("<", "\\u003c")}</script>`,
});
return new Response(html, { headers: { "content-type": "text/html; charset=utf-8" } });