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-string.mddocs/

String-based CSS Inlining

String-based CSS inlining functionality that directly injects critical CSS as <style> tags into HTML strings, providing immediate server-side rendering optimization.

Capabilities

renderStylesToString Function

Inlines critical CSS directly into HTML strings by parsing Emotion class names and injecting corresponding styles as <style> tags at appropriate positions.

/**
 * Inlines critical CSS directly into HTML string
 * @param html - Server-rendered HTML string containing Emotion class names
 * @returns HTML string with critical CSS inlined as <style> tags
 */
function renderStylesToString(html: string): string;

Usage Examples:

import React from "react";
import { renderToString } from "react-dom/server";
import { renderStylesToString } from "@emotion/server";
import styled from "@emotion/styled";

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

const Header = styled.h1`
  font-size: 24px;
  margin: 0;
`;

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

// Inline critical CSS
const htmlWithStyles = renderStylesToString(renderToString(<App />));

console.log(htmlWithStyles);
// Output includes:
// <style data-emotion="css abc123 def456">
//   /* CSS for Button and Header components */
// </style>
// <div>
//   <h1 class="css-def456">Welcome</h1>
//   <button class="css-abc123">Click me</button>
// </div>

Style Tag Generation

The function generates <style> tags with specific attributes:

  • data-emotion: Contains the cache key and space-separated style IDs
  • nonce: Security nonce if configured in the cache
  • CSS content: Actual CSS rules for the detected styles

Example with Nonce:

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

// Create cache with nonce for Content Security Policy
const cache = createCache({
  key: "css",
  nonce: "random-nonce-value"
});

const { renderStylesToString } = createEmotionServer(cache);

const htmlWithStyles = renderStylesToString(renderToString(<App />));
// Generated style tag includes: nonce="random-nonce-value"

Processing Strategy

The inlining process follows a sophisticated strategy:

  1. Global Styles First: Unregistered styles are injected at the beginning
  2. Component-Specific Styles: Styles are injected just before their first usage
  3. Deduplication: Each style is only included once, even if used multiple times
  4. Order Preservation: Maintains CSS cascade order for consistent styling

Advanced Usage with Multiple Components:

import React from "react";
import { renderToString } from "react-dom/server";
import { renderStylesToString } from "@emotion/server";
import styled from "@emotion/styled";

const GlobalStyle = styled.div`
  font-family: Arial, sans-serif;
  * {
    box-sizing: border-box;
  }
`;

const Card = styled.div`
  background: white;
  border: 1px solid #ccc;
  border-radius: 8px;
  padding: 16px;
`;

const Title = styled.h2`
  color: #333;
  margin: 0 0 16px 0;
`;

const App = () => (
  <GlobalStyle>
    <Card>
      <Title>Card Title</Title>
      <p>Card content goes here.</p>
    </Card>
  </GlobalStyle>
);

const htmlWithStyles = renderStylesToString(renderToString(<App />));
// Results in optimally ordered and deduplicated CSS injection

Integration Patterns

Express.js Server:

import express from "express";
import React from "react";
import { renderToString } from "react-dom/server";
import { renderStylesToString } from "@emotion/server";

const app = express();

app.get("/", (req, res) => {
  const htmlWithStyles = renderStylesToString(renderToString(<App />));
  
  const fullPage = `
    <!DOCTYPE html>
    <html>
    <head>
      <meta charset="utf-8">
      <title>My App</title>
    </head>
    <body>
      <div id="root">${htmlWithStyles}</div>
    </body>
    </html>
  `;
  
  res.send(fullPage);
});

Next.js Custom Document:

import Document, { Html, Head, Main, NextScript } from "next/document";
import { renderStylesToString } from "@emotion/server";

export default class MyDocument extends Document {
  static async getInitialProps(ctx) {
    const originalRenderPage = ctx.renderPage;

    ctx.renderPage = () =>
      originalRenderPage({
        enhanceApp: (App) => (props) => renderStylesToString(<App {...props} />),
      });

    const initialProps = await Document.getInitialProps(ctx);
    return initialProps;
  }

  render() {
    return (
      <Html>
        <Head />
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    );
  }
}

Performance Considerations

  • String Operations: Uses efficient string manipulation for large HTML documents
  • Memory Usage: Processes HTML in a single pass to minimize memory overhead
  • CSS Optimization: Automatically removes duplicate styles and unused CSS
  • Cache Efficiency: Leverages Emotion's cache for fast style lookup

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