@wrnexus/ssr
Secure HTML document rendering and SEO metadata.
bun add @wrnexus/ssr@0.2.15Server-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.
Installation
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
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">.
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
This declaration is generated from the exact published package and lists its exported functions, classes, interfaces, and types.
import { PageMeta, SeoConfig } from '@wrnexus/core';
/**
* @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 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?: string[];
/** 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;
/** 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;
}
/**
* 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;
export { type RenderOptions, renderDocument };
Examples
Copy-ready examples taken from this package's published documentation.
Example 1
bun add @wrnexus/ssrExample 2
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;
};Example 3
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" },
});