CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-emotion--server

Extract and inline critical css with emotion for server side rendering

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

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

Install with Tessl CLI

npx tessl i tessl/npm-emotion--server

docs

create-instance.md

extract-critical.md

index.md

render-to-stream.md

render-to-string.md

tile.json