or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

create-instance.mdextract-critical.mdindex.mdrender-to-stream.mdrender-to-string.md
tile.json

render-to-stream.mddocs/

Stream-based CSS Inlining

Stream-based CSS inlining functionality that creates Node.js transform streams for inlining critical CSS with HTML streaming, ideal for large pages and improved performance through progressive rendering.

Capabilities

renderStylesToNodeStream Function

Creates a Node.js transform stream that inlines critical CSS as HTML flows through it, enabling progressive rendering and better performance for large applications.

/**
 * Creates a Node.js transform stream for inlining critical CSS
 * @returns Transform stream that inlines critical CSS as HTML passes through
 */
function renderStylesToNodeStream(): NodeJS.ReadWriteStream;

Usage Examples:

import React from "react";
import { renderToNodeStream } from "react-dom/server";
import { renderStylesToNodeStream } from "@emotion/server";
import styled from "@emotion/styled";

const Button = styled.button`
  background: blue;
  color: white;
  padding: 10px 20px;
`;

const App = () => <Button>Click me</Button>;

// Create the CSS inlining stream
const cssStream = renderStylesToNodeStream();

// Pipe React's stream through the CSS stream
renderToNodeStream(<App />)
  .pipe(cssStream)
  .pipe(process.stdout);

// Output will include inlined <style> tags as the HTML streams

Stream Architecture

The stream uses a sophisticated pipeline built on:

  • html-tokenize: Parses HTML tokens as they flow through the stream
  • through: Provides the transform stream interface
  • multipipe: Composes multiple streams into a single pipeline

Internal Stream Flow:

HTML Input → Tokenizer → CSS Injector → HTML Output with Styles

Express.js Integration

Basic Streaming Response:

import express from "express";
import React from "react";
import { renderToNodeStream } from "react-dom/server";
import { renderStylesToNodeStream } from "@emotion/server";

const app = express();

app.get("/", (req, res) => {
  res.writeHead(200, { "Content-Type": "text/html" });
  
  // Write HTML head
  res.write(`<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Streaming App</title>
</head>
<body>
  <div id="root">`);

  // Stream the React app with CSS inlining
  const cssStream = renderStylesToNodeStream();
  
  renderToNodeStream(<App />)
    .pipe(cssStream)
    .pipe(res, { end: false });

  // Close HTML body after streaming completes
  cssStream.on("end", () => {
    res.write(`</div>
</body>
</html>`);
    res.end();
  });
});

Advanced Stream Usage

Custom Stream Processing:

import { Transform } from "stream";
import { renderStylesToNodeStream } from "@emotion/server";

// Create custom transform stream for additional processing
const customProcessor = new Transform({
  transform(chunk, encoding, callback) {
    // Add custom processing logic here
    const processed = chunk.toString().replace(/data-testid="[^"]*"/g, "");
    callback(null, processed);
  }
});

// Chain multiple streams
renderToNodeStream(<App />)
  .pipe(renderStylesToNodeStream())
  .pipe(customProcessor)
  .pipe(outputStream);

Error Handling with Streams:

import { renderToNodeStream } from "react-dom/server";
import { renderStylesToNodeStream } from "@emotion/server";

const streamWithErrorHandling = (app, res) => {
  const cssStream = renderStylesToNodeStream();
  const reactStream = renderToNodeStream(app);

  // Handle stream errors
  reactStream.on("error", (error) => {
    console.error("React stream error:", error);
    res.statusCode = 500;
    res.end("Server error occurred");
  });

  cssStream.on("error", (error) => {
    console.error("CSS stream error:", error);
    res.statusCode = 500;
    res.end("CSS processing error");
  });

  // Pipe streams
  reactStream.pipe(cssStream).pipe(res);
};

Processing Behavior

The stream processes HTML tokens and:

  1. Token Analysis: Examines each HTML token for Emotion class names
  2. Style Injection: Injects <style> tags when new Emotion styles are detected
  3. Deduplication: Tracks injected styles to prevent duplicates
  4. Order Preservation: Maintains proper CSS cascade order

Custom Cache with Streaming:

import createEmotionServer from "@emotion/server/create-instance";
import createCache from "@emotion/cache";

// Create cache with custom configuration
const cache = createCache({
  key: "streaming",
  nonce: "stream-nonce-123"
});

const { renderStylesToNodeStream } = createEmotionServer(cache);

// Use custom cache for streaming
const streamWithCustomCache = renderStylesToNodeStream();

Performance Benefits

Memory Efficiency:

  • Processes HTML in chunks rather than loading entire document
  • Reduces memory footprint for large applications
  • Enables server-side rendering of memory-intensive components

Progressive Rendering:

  • Begins sending HTML to client immediately
  • Reduces Time to First Byte (TTFB)
  • Improves perceived performance for users

Backpressure Handling:

  • Automatically handles stream backpressure
  • Prevents memory overflow in high-traffic scenarios
  • Maintains optimal throughput under load

Stream Event Handling

const cssStream = renderStylesToNodeStream();

// Monitor stream events
cssStream.on("pipe", (src) => {
  console.log("Stream piped from:", src.constructor.name);
});

cssStream.on("data", (chunk) => {
  console.log("Processed chunk:", chunk.length, "bytes");
});

cssStream.on("end", () => {
  console.log("CSS stream processing completed");
});

cssStream.on("close", () => {
  console.log("CSS stream closed");
});

Compatibility Notes

  • Node.js Environment Only: Requires Node.js stream APIs
  • React 16+: Compatible with React's streaming server renderer
  • HTTP/1.1 and HTTP/2: Works with both HTTP versions
  • Content Encoding: Compatible with gzip and other compression methods when properly configured