W WRNexusJS
Runtime

@wrnexus/ssr

Secure HTML document rendering and SEO metadata.

bun add @wrnexus/ssr@0.2.15
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.

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

FieldTypeDescription
metaPageMetaPage metadata for the document head (required).
bodystringRendered HTML for the body, placed inside #app (required).
seoSeoConfigGlobal SEO defaults, typically from wrnexus.config.ts.
urlURLCurrent request URL, used to resolve canonical/Open Graph URLs.
scriptsstring[]URLs of <script type="module"> tags to load (e.g. per-island chunks or the reactive runtime). Each also gets a <link rel="modulepreload">.
defaultTitlestringDefault document title used when meta.title is absent.
extraHeadstringRaw HTML injected at the end of <head> (trusted, framework-controlled — not escaped).
extraBodystringRaw HTML injected at the end of <body> (trusted, framework-controlled — not escaped).
htmlAttrsstringAttributes 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, else seo.title, else defaultTitle, else "WRNexusJS". When the page sets its own title and seo.titleTemplate contains %s, the template is applied.
  • Canonical / image URLs: resolved against canonicalBase (or the request url's origin) into absolute URLs when possible.
  • Keywords: an array is joined with ", ".
  • Emitted tags: <title>, and as applicable description, robots, keywords, theme-color, and canonical link, 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 includes charset, viewport, and a /favicon.ico icon 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) for escapeHtml and the PageMeta / SeoConfig types.
  • 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/ssr

Example 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" },
});