logoAnt Design

⌘ K
  • 디자인
  • 개발
  • 컴포넌트
  • 블로그
  • 자료
5.21.3
  • 为什么禁用日期这么难?
  • Why is it so hard to disable the date?
  • 封装 Form.Item 实现数组转对象
  • HOC Aggregate FieldItem
  • 行省略计算
  • Line Ellipsis Calculation
  • 📢 v4 维护周期截止
  • 📢 v4 surpassed maintenance period
  • Type Util
  • 一个构建的幽灵
  • A build ghost
  • 当 Ant Design 遇上 CSS 变量
  • Ant Design meets CSS Variables
  • API 기술 부채
  • 생동감 있는 Notification
  • 色彩模型与颜色选择器
  • Color Models and Color Picker
  • 主题拓展
  • Extends Theme
  • 虚拟表格来了!
  • Virtual Table is here!
  • Happy Work 테마
  • Happy Work Theme
  • 동적 스타일은 어디로 갔을까?
  • Suspense 引发的样式丢失问题
  • Suspense breaks styles
  • Bundle Size Optimization
  • 안녕, GitHub Actions
  • 所见即所得
  • To be what you see
  • 정적 메서드의 고통
  • SSR에서 정적 스타일 추출
  • SSR Static style export
  • 의존성 문제 해결
  • 贡献者开发维护指南
  • Contributor development maintenance guide
  • 转载-如何提交无法解答的问题
  • Repost: How to submit a riddle
  • 新的 Tooltip 对齐方式
  • Tooltip align update
  • Unnecessary Rerender
  • 如何成长为 Collaborator
  • How to Grow as a Collaborator
  • Funny Modal hook BUG
  • Modal hook 的有趣 BUG
  • about antd test library migration
  • antd 测试库迁移的那些事儿
  • Tree 的勾选传导
  • Tree's check conduction
  • getContainer 的一些变化
  • Some change on getContainer
  • Component-level CSS-in-JS

SSR Static style export

Resources

Ant Design Charts
Ant Design Pro
Ant Design Pro Components
Ant Design Mobile
Ant Design Mini
Ant Design Landing-Landing Templates
Scaffolds-Scaffold Market
Umi-React Application Framework
dumi-Component doc generator
qiankun-Micro-Frontends Framework
ahooks-React Hooks Library
Ant Motion-Motion Solution
China Mirror 🇨🇳

Community

Awesome Ant Design
Medium
Twitter
yuque logoAnt Design in YuQue
Ant Design in Zhihu
Experience Cloud Blog
seeconf logoSEE Conf-Experience Tech Conference
Work with Us

Help

GitHub
Change Log
FAQ
Bug Report
Issues
Discussions
StackOverflow
SegmentFault

Ant XTech logoMore Products

yuque logoYuQue-Document Collaboration Platform
AntV logoAntV-Data Visualization
Egg logoEgg-Enterprise Node.js Framework
Kitchen logoKitchen-Sketch Toolkit
Galacean logoGalacean-Interactive Graphics Solution
xtech logoAnt Financial Experience Tech
Theme Editor
Made with ❤ by
Ant Group and Ant Design Community
loading

For traditional js + css websites, SSR only needs to deal with the hydrate problem of the first rendering. With the introduction of CSS-in-JS technology, developers need to pay additional attention to how to export styles to HTML to ensure the correctness of the view. We provide a lot of implementation methods, and we just talk about the ideas here. If you need complete documentation or examples, please refer to Customize Theme.

Inline Style

The easiest way is to inline the styles directly into the HTML so that no extra requests. The disadvantage of this method is that the style cannot be cached by the browser and needs to be downloaded again for each request. Moreover, if there are too many styles, the HTML file will be too large, which will affect the speed of the first rendering.

In the v5 alpha version, in order to cover the SSR style rendering, we refer to the implementation of Emotion and add the corresponding inline style before each element:

<div>
<style>
:where(.css-bAmBOo).ant-btn {
// ...
}
</style>
<button className="ant-btn css-bAmBOo">Hello World</button>
</div>

This implementation is simple and effective, the only downside is style pollution for :nth selections. But considering that antd components rarely use this selector, the side effects can be ignored.

It worked well at the beginning, and the official website of antd directly supports the SSR style without modification and meet the SEO needs. But as our components gradually migrated to the CSS-in-JS version, we found that the site's bundle size became very large and slowly became unusable. After looking at the HTML, we found that the default inline method is not good, it will cause the style to be doubled inline, for example, if there are 3 Buttons in a page, then it will repeat the inline 3 times:

<div>
<style>
:where(.css-bAmBOo).ant-btn {
// ...
}
</style>
<button className="ant-btn css-bAmBOo">Hello World 1</button>
<style>
:where(.css-bAmBOo).ant-btn {
// ...
}
</style>
<button className="ant-btn css-bAmBOo">Hello World 2</button>
<style>
:where(.css-bAmBOo).ant-btn {
// ...
}
</style>
<button className="ant-btn css-bAmBOo">Hello World 3</button>
</div>

And when most components are converted to CSS-in-JS, inline styles can become huge. So we removed the automatic inlining function in the later stage, and converted it to a form that needs to be collected manually:

import { createCache, extractStyle, StyleProvider } from '@ant-design/cssinjs';
import { renderToString } from 'react-dom/server';
const cache = createCache();
// HTML Content
const html = renderToString(
<StyleProvider cache={cache}>
<MyApp />
</StyleProvider>,
);
// Style Content
const styleText = extractStyle(cache);

This is the traditional CSS-in-JS injection implementation. As mentioned in the introduction, inline styles cannot be cached and cause additional loading overhead. Therefore, we try to explore some new implementation methods to obtain a loading experience like native CSS.

Static Extract Style

We are thinking about whether we can pre-bake the style of the component for front-end consumption like the v4 version, so we proposed [RFC] Static Extract style. Its idea is very simple that only need to render all the components once in advance to get the complete style from the cache, and then write it into the css file.

const cache = createCache();
// HTML Content
renderToString(
<StyleProvider cache={cache}>
<Button />
<Switch />
<Input />
{/* Rest antd components */}
</StyleProvider>,
);
// Style Content
const styleText = extractStyle(cache);

Of course, this is a little cumbersome for developers. So we extracted a third-party package to achieve this requirement:

import { extractStyle } from '@ant-design/static-style-extract';
import fs from 'fs';
// `extractStyle` containers all the antd component
// excludes popup like component which is no need in ssr: Modal, message, notification, etc.
const css = extractStyle();
fs.writeFile(...);

If developers use a hybrid theme, they can also implement the hybrid requirements by themselves:

// `node` is the components set we prepared
const css = extractStyle((node) => (
<>
<ConfigProvider theme={theme1}>{node}</ConfigProvider>
<ConfigProvider theme={theme2}>{node}</ConfigProvider>
<ConfigProvider theme={theme3}>{node}</ConfigProvider>
</>
));

Part Static Extract Style

In most cases, the above usage has met the needs. But sometimes, we want to combine the flexibility of CSS-in-JS with the benefits of static file caching. Then at this time we need to start at the application level. After rendering and exporting the required content, it is different from the Inline Style, but converts it to file storage. File caching can be achieved through a simple hash:

import { createHash } from 'crypto';
// Get Style content like above
const styleText = extractStyle(cache);
const hash = createHash('md5').update(styleText).digest('hex');
const cssFileName = `css-${hash.substring(0, 8)}.css`;
if (!fs.existsSync(cssFileName)) {
fs.writeFileSync(cssFileName, styleText);
}

Then add the corresponding CSS file on the HTML template side:

<!doctype html>
<html>
<head>
<link rel="stylesheet" href="${hashCssFileUrl}" />
</head>
<body>
<div id="root">${html}</div>
</body>
</html>

Click here for complete implementation.

Corresponding CSS will be generated when visiting different pages, and each CSS will have its corresponding Hash value. When the Hash hits, it means that the CSS file has been placed on the disk and can be used directly. Then, for the client, it is a normal CSS file access and also enjoys the caching capability.

Different styles or custom themes required by different users to access the same page can be distinguished through this Hash.

Finally

For uncomplicated applications, we recommend using the former Static Extract Style. It is simple enough, but for developers who want finer-grained control over SSR-style rendering for a better access speed experience, you can try the partial static capability. Thanks.