6.0 KiB
@wrnexus/ssr
Server-side rendering: wraps a page's HTML body in a complete HTML document with a metadata-driven
<head>.
Part of the WrNexus framework — an SSR-first, Bun-native full-stack web framework.
Overview
Pages in WrNexus 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
wrnexusnpm 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"WrNexus". 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/coreforescapeHtmland thePageMeta/SeoConfigtypes. - Bun-only — like the rest of WrNexus, this package targets the Bun runtime (Node is not supported).