How to Implement Server-Side Rendering (SSR) with Ant Design for Better Performance
Ant Design supports Server-Side Rendering through @ant-design/cssinjs, which extracts critical CSS during server rendering to eliminate flash of unstyled content and improve initial page load times.
Server-Side Rendering (SSR) with Ant Design allows you to generate HTML and critical CSS on the server before sending them to the client. This approach, implemented in the ant-design/ant-design repository, significantly reduces time-to-first-paint and prevents SEO issues caused by client-side-only rendering.
Why Use Server-Side Rendering with Ant Design?
Implementing SSR with Ant Design delivers measurable performance benefits for production applications. When you render components on the server, you eliminate the flash of unstyled content (FOUC) that occurs when browsers download JavaScript bundles before applying styles. The @ant-design/cssinjs engine generates design tokens and component styles during the server render, ensuring crawlers and users receive fully styled markup immediately.
Core SSR Concepts in Ant Design
Understanding the architecture behind Ant Design's SSR implementation helps you configure it correctly for your framework of choice.
Style Extraction Utilities
The primary API for SSR lives in components/style/index.ts. This file exports three critical functions:
createCache(): Initializes an isolated CSS cache for each server request, preventing style leakage between usersStyleProvider: A React component that wraps your application and records every CSS rule generated during renderextractStyle(cache): Returns a<style>element string containing all collected CSS rules that you inject into the HTML<head>
Design Token Preparation
Before components render on the server, Ant Design resolves design tokens through components/style/token.ts. The prepareComponentToken function ensures that color palettes, spacing scales, and border radius values are calculated identically on both server and client. This deterministic token generation prevents hydration mismatches where server and client generate different class names or CSS values.
Implementing SSR in a Node.js/Express Application
For custom server environments, you manually manage the style extraction lifecycle. Below is a complete Express server implementation using Ant Design's SSR utilities:
// server.ts (Node & Express)
import express from 'express';
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import { StyleProvider, createCache, extractStyle } from '@ant-design/cssinjs';
import { ConfigProvider, Button } from 'antd';
const app = express();
app.get('*', (req, res) => {
// 1️⃣ Create a CSS cache for this request
const cache = createCache();
// 2️⃣ Render with StyleProvider that records styles
const html = ReactDOMServer.renderToString(
<StyleProvider cache={cache}>
<ConfigProvider>
<Button type="primary">Ant Design SSR</Button>
</ConfigProvider>
</StyleProvider>,
);
// 3️⃣ Extract the collected CSS
const styleTag = extractStyle(cache);
// 4️⃣ Assemble full HTML
const fullHtml = `
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Ant Design SSR</title>
${styleTag}
</head>
<body>
<div id="root">${html}</div>
<script src="/client.bundle.js"></script>
</body>
</html>
`;
res.send(fullHtml);
});
app.listen(3000, () => console.log('SSR server listening on http://localhost:3000'));
This implementation creates an isolated cache per request using createCache(), ensuring that styles from different users never leak into each other's responses. The extractStyle(cache) function returns a <style data-antd-cssinjs> tag that you inject directly into the HTML template.
Implementing SSR in Next.js
Next.js provides built-in SSR capabilities, but you must configure the custom Document to extract Ant Design styles. The @ant-design/cssinjs package provides a renderStatic helper specifically for Next.js integration:
// pages/_document.tsx (Next.js custom Document)
import Document, { Html, Head, Main, NextScript } from 'next/document';
import { renderStatic } from '@ant-design/cssinjs';
import { ConfigProvider } from 'antd';
import React from 'react';
export default class MyDocument extends Document {
static async getInitialProps(ctx) {
// 1️⃣ Render the app to static markup while collecting styles
const page = await ctx.renderPage({
enhanceApp: (App) => (props) => (
<ConfigProvider>
<App {...props} />
</ConfigProvider>
),
});
// 2️⃣ Extract styles generated by Ant Design
const { extractStyle } = await renderStatic(() => page.html);
const styleTag = extractStyle();
return { ...page, styleTag };
}
render() {
// @ts-ignore – styleTag is injected by getInitialProps
const { styleTag } = this.props as any;
return (
<Html>
<Head>{styleTag}</Head>
<body>
<Main />
<NextScript />
</body>
</Html>
);
}
}
// pages/index.tsx (page component)
import { Button } from 'antd';
import React from 'react';
export default function Home() {
return (
<div style={{ padding: 24 }}>
<Button type="primary">Ant Design + Next.js SSR</Button>
</div>
);
}
The renderStatic function from @ant-design/cssinjs handles the style collection automatically. It runs your React tree once, captures all generated CSS, and provides an extractStyle function that returns the style elements ready for the HTML head.
Testing Your SSR Implementation
The Ant Design repository includes a concrete test case demonstrating SSR validation. Located at components/splitter/__tests__/ssr.test.tsx, this test shows how to verify that styles are correctly extracted during server rendering:
// components/splitter/__tests__/ssr.test.tsx
import { renderToString } from 'react-dom/server';
import { StyleProvider, createCache, extractStyle } from '@ant-design/cssinjs';
import Splitter from '../index';
test('SSR style extraction', () => {
const cache = createCache();
const html = renderToString(
<StyleProvider cache={cache}>
<Splitter />
</StyleProvider>,
);
const style = extractStyle(cache);
expect(style).toContain('.ant-splitter'); // class name is generated on the server
});
This test validates that the extractStyle function captures component-specific CSS classes like .ant-splitter, ensuring your SSR setup correctly generates the required styles before sending HTML to the client.
Summary
Implementing Server-Side Rendering with Ant Design requires understanding the @ant-design/cssinjs architecture and following a specific extraction workflow:
- Use
StyleProviderandcreateCacheto isolate CSS generation per request incomponents/style/index.ts - Extract critical CSS with
extractStylebefore sending HTML to eliminate FOUC and improve perceived performance - Prepare design tokens via
prepareComponentTokenincomponents/style/token.tsto ensure server-client consistency - Test your implementation using the patterns found in
components/splitter/__tests__/ssr.test.tsxto verify style extraction works correctly
Frequently Asked Questions
How does Ant Design prevent Flash of Unstyled Content during SSR?
Ant Design prevents FOUC by generating CSS during the server render phase using @ant-design/cssinjs. The extractStyle function captures all styles created while React renders your components to HTML, allowing you to inject that CSS into the <head> before the browser paints. This ensures the user sees styled content immediately rather than waiting for JavaScript bundles to download and execute.
Can I use Ant Design SSR with Next.js App Router?
Yes, Ant Design supports Next.js App Router through the same @ant-design/cssinjs utilities. You create a registry component that uses StyleProvider with createCache during the server render, then extract styles using extractStyle in the root layout. The extracted CSS is inserted into the HTML head, and the client hydrates without injecting duplicate styles. The official blog post at docs/blog/extract-ssr.en-US.md provides specific guidance for App Router implementation.
What is the performance impact of SSR with Ant Design?
Server-Side Rendering with Ant Design improves time-to-first-paint and largest-contentful-paint metrics by sending fully rendered HTML and critical CSS in the initial response. However, SSR increases server compute time because React must render the component tree and @ant-design/cssinjs must generate styles for each request. The trade-off favors SSR for content-heavy pages requiring SEO or fast initial loads, while highly interactive applications may benefit from static site generation or selective SSR for specific routes.
How do I debug SSR style extraction issues?
To debug SSR style extraction, verify that StyleProvider wraps your root component and that you pass the same cache instance to both the provider and extractStyle. Check that extractStyle is called after renderToString or renderToStaticMarkup completes, as styles are collected during the render phase. If styles are missing, ensure you are not using dynamic imports that execute after the initial render, and consult the reference test at components/splitter/__tests__/ssr.test.tsx for a working implementation pattern.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →