Static Pre-Rendering & SSG
Generate complete, beautifully styled API documentation on the server using Declarative Shadow DOM (DSD) or semantic Light DOM. Achieve zero FOUC, instant First Contentful Paint, and complete search engine indexability across Astro, Next.js, Nuxt, or pure Node.js scripts.
Why Pre-Render on the Server?
Traditional client-only OpenAPI viewers (Swagger UI, standard Web Components, Redoc) force the visitor's browser to execute heavy CPU tasks before anything is visible. RapiDoc static pre-rendering moves this computation to your build server.
- ✕Slow LCP: Browser must fetch 600KB+ JS, download remote YAML/JSON, and dereference complex schemas.
- ✕FOUC & Blank Screens: Users stare at a blank canvas or spinning loader for 1.5–3 seconds.
- ✕Poor SEO: Search crawlers that do not execute client JavaScript index an empty shell.
- ✕No MDX Embedding: Difficult to cleanly embed individual endpoints inside documentation guides.
- ✓Instant First Paint: Complete HTML and CSS stream directly from your CDN/edge server.
- ✓Zero FOUC: Native browser Declarative Shadow DOM renders styles and layout before any JS executes.
- ✓Full SEO Indexing: Googlebot and AI crawlers index all endpoint paths, parameters, schemas, and descriptions.
- ✓Seamless Hydration: Client script attaches smoothly to the existing DOM for Try-It and live filtering.
Pre-Rendered Endpoint Preview
The endpoint card below was generated at build-time on the server by renderEndpoint() with zero client-side JavaScript execution.
Pre-Rendering via Node.js or Bun
Generate static .html files in your build pipeline with a single function call. Install the renderer with npm install @rapidoc/prerender; it works in Node.js >= 22 or Bun.
// prerender.mjs - Standalone Node.js / Bun script
import fs from 'node:fs';
import { renderRapiDoc } from '@rapidoc/prerender';
// 1. Pre-render the OpenAPI specification into Declarative Shadow DOM
const staticHtml = await renderRapiDoc({
spec: './specs/petstore.yaml', // file path, remote URL, or JSON/YAML string
renderStyle: 'read', // 'read' | 'view' | 'focused'
theme: 'ember', // 'ember' | 'carbon' | 'cobalt' | 'jade' | 'indigo' | hex
colorScheme: 'dark', // 'dark' | 'light' | 'system'
outputMode: 'dsd', // Declarative Shadow DOM (default)
});
// 2. Wrap inside standard HTML boilerplate with optional client bundle
const htmlPage = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>API Reference</title>
<!-- Client bundle enables interactive Try-It console, live search, and tab switching -->
<script type="module" src="https://unpkg.com/rapidoc/dist/rapidoc-min.js"></script>
</head>
<body style="margin: 0; padding: 0; background: #0f172a;">
${staticHtml}
</body>
</html>`;
// 3. Save to disk
fs.writeFileSync('./dist/api.html', htmlPage, 'utf-8');
console.log('API Reference pre-rendered successfully to dist/api.html');Pre-Rendering in Astro
Astro's zero-JS-by-default architecture is the perfect companion for RapiDoc's isomorphic renderer. Use renderRapiDoc inside your frontmatter.
---
// src/pages/api.astro - Astro SSG Page
import { renderRapiDoc } from '@rapidoc/prerender';
// Executed at BUILD TIME on the server
const preRenderedDoc = await renderRapiDoc({
spec: './public/specs/petstore.json',
renderStyle: 'read',
theme: 'ember',
colorScheme: 'dark',
});
---
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Petstore API Reference</title>
<!-- Client bundle hydrates the pre-rendered shadow DOM with zero FOUC -->
<script type="module" src="https://unpkg.com/rapidoc/dist/rapidoc-min.js"></script>
</head>
<body style="margin: 0; padding: 0;">
<!-- Injects server-rendered Declarative Shadow DOM -->
<Fragment set:html={preRenderedDoc} />
</body>
</html>Pre-Rendering in Next.js & Nuxt
Render API documentation inside React Server Components or Nuxt SSR routes with Incremental Static Regeneration (ISR).
// app/api-reference/page.tsx - Next.js App Router (React Server Component)
import { renderRapiDoc } from '@rapidoc/prerender';
import Script from 'next/script';
export const revalidate = 3600; // ISR: Re-cache once per hour
export default async function ApiReferencePage() {
const staticDocHtml = await renderRapiDoc({
spec: 'https://api.example.com/openapi.json',
renderStyle: 'read',
theme: 'ember',
colorScheme: 'dark',
});
return (
<main style={{ minHeight: '100vh', margin: 0, padding: 0 }}>
{/* Client-side hydration for Try-It console & filter */}
<Script
type="module"
src="https://unpkg.com/rapidoc/dist/rapidoc-min.js"
strategy="lazyOnload"
/>
<div dangerouslySetInnerHTML={{ __html: staticDocHtml }} />
</main>
);
}Isolated Endpoints & Schemas for MDX Guides
Building a custom developer portal like Zudoku or Cosmo Cargo? Use renderEndpoint() and renderSchema() to embed isolated operations directly into Markdown and MDX articles.
// MDX / Developer Portal Endpoint Embedding (Zudoku / Cosmo Cargo Style)
import { renderEndpoint, renderSchema } from '@rapidoc/prerender';
// Pre-render a single endpoint card inside a guide article
export const shipmentEndpointHtml = await renderEndpoint({
spec: './specs/logistics.yaml',
path: '/v1/shipments/{shipmentId}',
method: 'get',
theme: 'ember',
colorScheme: 'dark',
allowTry: false, // Static documentation view
});
// Pre-render a standalone JSON Schema model
export const schemaHtml = await renderSchema({
spec: './specs/logistics.yaml',
schema: 'ShipmentResponse',
style: 'table', // 'table' or 'tree'
theme: 'ember',
});How Declarative Shadow DOM (DSD) Operates
Declarative Shadow DOM allows standard HTML to declare encapsulated shadow roots without calling element.attachShadow() via JavaScript.
<!-- Declarative Shadow DOM output from renderRapiDoc({ outputMode: 'dsd' }) -->
<rapi-doc theme="ember" color-scheme="dark" render-style="read" layout="column">
<!-- Native browser Declarative Shadow DOM -->
<template shadowrootmode="open">
<style>
/* Complete scoped design tokens, layout, typography, and method badges */
:host { all: initial; display: flex; ... }
</style>
<!-- Complete pre-rendered HTML DOM: navbar, tag groups, endpoints, schemas -->
<div id="the-main-body" class="body">
<nav class="nav-bar"> ... </nav>
<main class="main-content">
<section class="m-endpoint regular-font get expanded"> ... </section>
</main>
</div>
</template>
</rapi-doc>If your project uses a CSS framework (like Tailwind or Bootstrap) that needs to style RapiDoc's internal elements directly, specify outputMode: 'html' or 'light'. RapiDoc will emit a standard semantic <div class="rapidoc-prerender-container"> instead of a custom element.
renderRapiDoc() Configuration Options
Complete options dictionary accepted by renderRapiDoc().
| Option | Type | Default | Description |
|---|---|---|---|
spec | string | object | (required) | Path to local file (.json/.yaml), remote HTTPS URL, raw YAML/JSON string, or pre-parsed spec object. |
renderStyle | 'read' | 'view' | 'focused' | 'read' | Layout style: continuous document ("read"), collapsible accordions ("view"), or single-endpoint sidebar ("focused"). |
theme | string | 'ember' | Brand color preset ("ember", "carbon", "cobalt", "jade", "indigo") or custom hex code. |
colorScheme | 'dark' | 'light' | 'system' | 'dark' | Active color scheme applied to background, cards, text, and method badges. |
outputMode | 'dsd' | 'html' | 'light' | 'dsd' | Container format: Declarative Shadow DOM with native browser hydration ("dsd") or scoped Light DOM div ("html"). |
includeStyles | boolean | true | Whether to inline the complete bundled CSS styles directly within the pre-rendered container. |
schemaStyle | 'table' | 'tree' | 'table' | Default display style for JSON Schemas and parameter objects. |
schemaExpandLevel | number | 99999 | Initial expansion depth for nested schemas. |
allowTry | boolean | true | Whether to include the interactive request execution console markup. |
allowSearch | boolean | true | Whether the sidebar search and filter input is included. |