CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-react-server-dom-webpack

React Server Components bindings for DOM using Webpack, enabling server-side rendering with streaming support and client-server communication.

Pending
Overview
Eval results
Files

static-rendering.mddocs/

Static Rendering

Static pre-rendering capabilities for React Server Components, enabling build-time generation of server component output for improved performance and SEO.

Capabilities

Unstable Prerender

Pre-renders React Server Components at build time, generating static output that can be served directly or hydrated on the client.

/**
 * Pre-renders server components for static generation
 * @param model - React server component tree to pre-render
 * @param webpackMap - Client manifest for module resolution
 * @param options - Optional rendering configuration
 * @returns Promise resolving to pre-render result
 */
function unstable_prerender(
  model: ReactClientValue,
  webpackMap: ClientManifest,
  options?: ServerOptions
): Promise<StaticResult>;

Usage Examples:

import { unstable_prerender } from "react-server-dom-webpack/static.browser";

// Pre-render a server component at build time
async function generateStaticPage() {
  const clientManifest = JSON.parse(
    await fs.readFile("./react-client-manifest.json", "utf8")
  );

  const result = unstable_prerender(
    <HomePage posts={staticPosts} />,
    clientManifest,
    {
      identifierPrefix: "static-",
      onError: (error) => {
        console.error("Static render error:", error);
      }
    }
  );

  // Save static output
  await fs.writeFile("./dist/home.html", result.prelude);
  return result;
}

// Build-time page generation
const pages = [
  { path: "/", component: <HomePage /> },
  { path: "/about", component: <AboutPage /> },
  { path: "/contact", component: <ContactPage /> }
];

for (const page of pages) {
  const result = unstable_prerender(page.component, clientManifest);
  await saveStaticPage(page.path, result);
}

Node.js Pre-rendering

Node.js-specific pre-rendering with stream support for build pipelines.

/**
 * Pre-renders server components to Node.js stream for build tools
 * @param model - React server component tree to pre-render  
 * @param webpackMap - Client manifest for module resolution
 * @param options - Optional rendering configuration
 * @returns ReadableStream containing pre-rendered content
 */
function unstable_prerenderToNodeStream(
  model: ReactClientValue,
  webpackMap: ClientManifest,
  options?: ServerOptions
): ReadableStream;

Usage Examples:

import { unstable_prerenderToNodeStream } from "react-server-dom-webpack/static.node";
import { createWriteStream } from "fs";

// Stream pre-rendered content to file
async function prerenderToFile(component, outputPath) {
  const clientManifest = JSON.parse(
    fs.readFileSync("./react-client-manifest.json", "utf8")
  );

  const stream = unstable_prerenderToNodeStream(
    component,
    clientManifest,
    {
      onError: (error) => console.error("Prerender error:", error),
      signal: abortController.signal
    }
  );

  const writeStream = createWriteStream(outputPath);
  
  // Pipe pre-rendered output to file
  stream.pipe(writeStream);
  
  return new Promise((resolve, reject) => {
    writeStream.on("finish", resolve);
    writeStream.on("error", reject);
  });
}

// Build-time static generation
await prerenderToFile(<BlogPost post={postData} />, "./dist/blog/post-123.html");

Static Site Generation Patterns

Common patterns for integrating static pre-rendering into build workflows.

Build-Time Page Generation

// build/generatePages.js
import { unstable_prerender } from "react-server-dom-webpack/static.node";
import { loadClientManifest, getAllPosts, getAllPages } from "./utils";

async function generateStaticSite() {
  const clientManifest = await loadClientManifest();
  const posts = await getAllPosts();
  const pages = await getAllPages();

  // Generate static pages
  for (const page of pages) {
    const result = unstable_prerender(
      <Page {...page} />,
      clientManifest,
      { identifierPrefix: `page-${page.id}-` }
    );

    await fs.writeFile(`./dist/${page.slug}.html`, result.prelude);
    console.log(`Generated: ${page.slug}`);
  }

  // Generate blog posts
  for (const post of posts) {
    const result = unstable_prerender(
      <BlogPost post={post} />,
      clientManifest,
      { identifierPrefix: `post-${post.id}-` }
    );

    await fs.writeFile(`./dist/blog/${post.slug}.html`, result.prelude);
    console.log(`Generated: blog/${post.slug}`);
  }
}

generateStaticSite().catch(console.error);

Incremental Static Regeneration

// api/revalidate.js
import { unstable_prerender } from "react-server-dom-webpack/static.browser";

export async function POST(request) {
  const { path, data } = await request.json();
  
  try {
    // Re-generate specific page
    const result = unstable_prerender(
      <DynamicPage data={data} />,
      clientManifest,
      {
        identifierPrefix: `revalidate-${Date.now()}-`,
        onError: (error) => {
          console.error(`Revalidation error for ${path}:`, error);
        }
      }
    );

    // Update static file
    await fs.writeFile(`./dist${path}.html`, result.prelude);
    
    return Response.json({ success: true, path });
  } catch (error) {
    return Response.json({ error: error.message }, { status: 500 });
  }
}

Build Tool Integration

// webpack.config.js
const ReactFlightWebpackPlugin = require("react-server-dom-webpack/plugin");

module.exports = {
  plugins: [
    new ReactFlightWebpackPlugin({
      isServer: false,
      clientReferences: ["./src/**/*.client.js"]
    }),
    
    // Custom plugin for static generation
    {
      apply(compiler) {
        compiler.hooks.afterEmit.tapAsync("StaticGeneration", async (compilation, callback) => {
          const { unstable_prerender } = require("react-server-dom-webpack/static.node");
          
          // Generate static pages after build
          await generateStaticPages(unstable_prerender);
          callback();
        });
      }
    }
  ]
};

Pre-render Result Handling

Working with pre-render results and their metadata.

// Handle pre-render result
const result = unstable_prerender(<App />, clientManifest);

// Extract different parts of the result
const {
  prelude,    // Static HTML content
  postponed,  // Postponed content for streaming
  metadata    // Additional rendering metadata
} = result;

// Save static content
await fs.writeFile("./static/app.html", prelude);

// Handle postponed content if any
if (postponed) {
  await handlePostponedContent(postponed);
}

Error Handling in Static Rendering

Comprehensive error handling for build-time rendering issues.

import { unstable_prerender } from "react-server-dom-webpack/static.browser";

async function safePrerender(component, clientManifest, options = {}) {
  const errors = [];
  
  const result = unstable_prerender(
    component,
    clientManifest,
    {
      ...options,
      onError: (error) => {
        errors.push({
          message: error.message,
          stack: error.stack,
          timestamp: new Date().toISOString()
        });
        
        // Log error but continue rendering
        console.error("Pre-render error:", error);
      }
    }
  );

  return {
    ...result,
    errors,
    hasErrors: errors.length > 0
  };
}

// Use with error reporting
const result = await safePrerender(<HomePage />, clientManifest);

if (result.hasErrors) {
  await reportBuildErrors(result.errors);
  
  // Decide whether to fail build or use partial result
  if (result.errors.some(e => e.critical)) {
    throw new Error("Critical pre-render errors detected");
  }
}

Performance Optimization

Optimizing static rendering for large sites and complex components.

Parallel Pre-rendering

import { unstable_prerender } from "react-server-dom-webpack/static.node";
import pLimit from "p-limit";

// Limit concurrent pre-rendering operations
const limit = pLimit(4);

async function prerenderPages(pages, clientManifest) {
  const tasks = pages.map(page => 
    limit(async () => {
      const result = unstable_prerender(
        <Page {...page} />,
        clientManifest,
        { identifierPrefix: `page-${page.id}-` }
      );
      
      await fs.writeFile(`./dist/${page.slug}.html`, result.prelude);
      return { page: page.slug, success: true };
    })
  );

  const results = await Promise.allSettled(tasks);
  
  const successful = results.filter(r => r.status === "fulfilled").length;
  const failed = results.filter(r => r.status === "rejected").length;
  
  console.log(`Pre-rendered ${successful} pages, ${failed} failed`);
  
  return results;
}

Caching Pre-rendered Content

// build/cache.js
import crypto from "crypto";

const prerenderCache = new Map();

function getCacheKey(component, manifest) {
  const componentHash = crypto
    .createHash("md5")
    .update(JSON.stringify(component))
    .digest("hex");
    
  const manifestHash = crypto
    .createHash("md5")
    .update(JSON.stringify(manifest))
    .digest("hex");
    
  return `${componentHash}-${manifestHash}`;
}

async function cachedPrerender(component, clientManifest) {
  const cacheKey = getCacheKey(component, clientManifest);
  
  if (prerenderCache.has(cacheKey)) {
    console.log("Cache hit for pre-render");
    return prerenderCache.get(cacheKey);
  }
  
  const result = unstable_prerender(component, clientManifest);
  prerenderCache.set(cacheKey, result);
  
  return result;
}

Types

interface StaticResult {
  /** ReadableStream containing pre-rendered content */
  prelude: ReadableStream;
}

type ReactClientValue = any;
type ClientManifest = Record<string, ImportManifestEntry>;

interface ImportManifestEntry {
  id: string;
  chunks: string[];
  name: string;
}

Note: Static rendering functions use the same ServerOptions interface as documented in Server APIs.

Build Integration Notes

Webpack Integration

Static rendering works best with proper Webpack configuration:

// webpack.static.config.js
module.exports = {
  target: "node",
  entry: "./build/static-generator.js",
  plugins: [
    new ReactFlightWebpackPlugin({
      isServer: false,
      clientReferences: ["./src/**/*.client.js"]
    })
  ],
  externals: {
    // Externalize Node.js modules for static generation
    "fs": "commonjs fs",
    "path": "commonjs path"
  }
};

Framework Integration

Integration with popular static site generators:

  • Next.js: Use in getStaticProps or build scripts
  • Gatsby: Integrate with createPages API
  • Vite: Use in build plugins or custom generators
  • Custom: Create build-time generation scripts

SEO and Performance

Static pre-rendering provides:

  • Improved SEO: Search engines can crawl static HTML
  • Faster Initial Load: No client-side rendering delay
  • Better Core Web Vitals: Reduced Time to First Byte
  • Progressive Enhancement: Works without JavaScript

Install with Tessl CLI

npx tessl i tessl/npm-react-server-dom-webpack

docs

client-apis.md

data-communication.md

index.md

nodejs-integration.md

server-apis.md

static-rendering.md

webpack-plugin.md

tile.json