Skip to content

Commit 1bb56db

Browse files
SoonIterCopilot
andauthored
docs(guide): rewrite SSG documentation with comprehensive explanations (#2985)
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
1 parent 18f9b18 commit 1bb56db

File tree

2 files changed

+450
-112
lines changed

2 files changed

+450
-112
lines changed
Lines changed: 225 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,259 @@
11
# Static site generation (SSG)
22

3-
## Introduction
3+
## What is SSG
44

5-
In the production build, Rspress will automatically generate a static site for you, that is, generate the HTML content of each page. After the build is completed, the HTML will appear in the default product directory, such as:
5+
SSG (Static Site Generation) refers to pre-rendering pages into HTML files during the **build phase**, rather than rendering them when users visit.
6+
7+
**Advantages of SSG:**
8+
9+
- **Faster First Contentful Paint**: Users don't need to wait for JavaScript to load and execute; they see complete content as soon as the browser loads the HTML
10+
- **SEO Friendly**: Search engine crawlers can directly access complete HTML content
11+
- **Easy to Deploy**: Output consists of pure static files with no server required, can be directly hosted on any static hosting service
12+
13+
Rspress enables SSG by default, which means when you run `rspress build`, each page will be pre-rendered into an HTML file containing complete content. The following details explain the implementation of SSG, helping you gain a deeper understanding of how it works.
14+
15+
## Differences between dev and build
16+
17+
Rspress employs different rendering strategies depending on the mode: it uses Client-Side Rendering (CSR) during development for a better experience, while defaulting to SSG during production builds for optimal performance.
18+
19+
| Aspect | Dev Mode (Development) | Build Mode (Production) |
20+
| -------------- | -------------------------------- | -------------------------------------- |
21+
| Command | `rspress dev` | `rspress build` |
22+
| Rendering | Pure CSR (Client-Side Rendering) | SSG (default) or CSR |
23+
| Pre-rendering | None | Pre-renders all pages when SSG enabled |
24+
| Focus | Debugging, HMR | Performance, SEO |
25+
| Preview Method | Access dev server directly | `rspress preview` |
26+
27+
### Dev mode
28+
29+
```bash
30+
rspress dev
31+
```
32+
33+
Dev mode uses **pure Client-Side Rendering (CSR)** without pre-rendering. This prioritizes iteration speed and Hot Module Replacement (HMR) capabilities.
34+
35+
:::tip
36+
If your code works fine in Dev mode but throws errors in Build mode, it's usually because SSG renders in a Node.js environment and cannot access browser APIs (like `window` or `document`). See ["Common Issues and Solutions"](#common-issues-and-solutions) below.
37+
:::
38+
39+
### Build mode
40+
41+
```bash
42+
rspress build
43+
```
44+
45+
Build mode enables SSG by default. You can control this via the [`ssg` configuration](#configuration):
46+
47+
- `ssg: true` (Default): Enable SSG. During the build, Rspress executes React component rendering in a Node.js environment, converting each page into an HTML file with complete content.
48+
- `ssg: false`: Disable SSG. Use pure CSR. The generated HTML contains only an empty container, waiting for client-side rendering.
49+
50+
**After building:**
51+
52+
- **Local Preview**: Use `rspress preview` to start a local static server for previewing the output
53+
54+
```bash
55+
rspress preview
56+
```
57+
58+
- **Server Deployment**: Deploy the `doc_build` directory to static hosting services (GitHub Pages, Netlify, Vercel, etc.)
59+
60+
## SSG vs CSR output
61+
62+
### Output directory structure
63+
64+
Whether using SSG or CSR mode, the output directory structure is the same:
665

766
```tree
8-
doc_build
9-
├── static
10-
│ ├── main.js
11-
│ └── ...
67+
doc_build/
68+
├── static/
69+
│ ├── js/
70+
│ │ ├── main.[hash].js
71+
│ │ └── async/
72+
│ └── css/
73+
│ └── main.[hash].css
1274
├── index.html
13-
├── about.html
14-
── posts
15-
│ ├── hello-world.html
16-
└── ...
75+
├── guide/
76+
│ └── getting-started.html
77+
── api/
78+
└── config.html
1779
```
1880

19-
You can deploy the contents of this product directory to any static site hosting service, such as GitHub Pages, Netlify, Vercel, etc.
81+
### HTML content differences
2082

21-
## Advantages of SSG
83+
The core difference between the two modes lies in the HTML file content:
2284

23-
The essence of static site generation is to pre-render components at the build stage, render the components into HTML strings, and then write them into HTML files. This has many benefits, such as:
85+
**SSG Output HTML** (Pre-rendered complete content):
2486

25-
- Faster FCP, because there is no need to wait for JS to load before rendering.
26-
- More conducive to SEO, because search engine spiders can directly crawl the complete HTML content.
87+
```html
88+
<body>
89+
<div id="__rspress_root">
90+
<!-- Pre-rendered complete page content -->
91+
<nav>...</nav>
92+
<main>
93+
<article>
94+
<h1>Getting Started</h1>
95+
<p>Welcome to Rspress...</p>
96+
</article>
97+
</main>
98+
</div>
99+
<script src="/static/js/main.[hash].js"></script>
100+
</body>
101+
```
27102

28-
Considering the cost of static site generation, Rspress only pre-renders during production environment builds. In the development environment, it still uses the traditional SPA rendering mode without pre-rendering.
103+
**CSR Output HTML** (only empty container, waiting for JS to render):
29104

30-
## Adding custom site content
105+
```html
106+
<body>
107+
<div id="__rspress_root"></div>
108+
<script src="/static/js/main.[hash].js"></script>
109+
</body>
110+
```
31111

32-
Through `builderConfig.html.tags`, you can customize the site HTML content, such as adding statistical code, adding scripts and styles, etc.
112+
### Loading flow differences
33113

34-
```js title="rspress.config.ts"
35-
import { defineConfig } from '@rspress/core';
114+
**SSG Loading Flow:**
36115

37-
export default defineConfig({
38-
// ...
39-
builderConfig: {
40-
html: {
41-
tags: [
42-
{
43-
tag: 'script',
44-
attrs: {
45-
src: 'https://cdn.example.com/your-script.js',
46-
},
47-
},
48-
{
49-
tag: 'link',
50-
attrs: {
51-
rel: 'stylesheet',
52-
href: 'https://cdn.example.com/your-style.css',
53-
},
54-
},
55-
],
56-
},
57-
},
58-
});
59-
```
116+
1. Browser loads HTML → User **immediately sees complete content**
117+
2. JavaScript finishes loading → React hydrates, binds event interactions
118+
3. Subsequent navigation → SPA mode, client-side rendering
119+
120+
**CSR Loading Flow:**
121+
122+
1. Browser loads HTML → User sees **blank page**
123+
2. JavaScript finishes loading → React renders page content
124+
3. Subsequent navigation → SPA mode, client-side rendering
60125

61-
For more detailed config of `builderConfig.html.tags`, please refer to the [documentation](https://rsbuild.rs/config/html/tags).
126+
## Common issues and solutions
62127

63-
## Preview
128+
### `window is not defined` / `document is not defined`
64129

65-
After the production build is complete, you can preview the output by using the `rspress preview` command. This command will start a local static site service, and you can access this service in your browser to preview the output.
130+
**Cause**: SSG renders pages in a Node.js environment, where browser-specific global objects like `window` and `document` don't exist.
66131

67-
```shell
68-
> rspress preview
132+
**Solutions**:
69133

70-
Preview server running at http://localhost:4173/
134+
1. **Use `useEffect` for delayed execution**: Place browser API calls inside `useEffect` to ensure they only run on the client
135+
136+
```tsx
137+
import { useEffect, useState } from 'react';
138+
139+
function MyComponent() {
140+
const [width, setWidth] = useState(0);
141+
142+
useEffect(() => {
143+
// Only runs on client
144+
setWidth(window.innerWidth);
145+
}, []);
146+
147+
return <div>Window width: {width}</div>;
148+
}
149+
```
150+
151+
2. **Conditional check**: Check the environment before accessing browser APIs
152+
153+
```tsx
154+
if (typeof window !== 'undefined') {
155+
// Browser environment
156+
console.log(window.location.href);
157+
}
158+
```
159+
160+
3. **Dynamic import**: For third-party libraries that depend on browser APIs, use dynamic imports
161+
162+
```tsx
163+
import { useEffect, useState } from 'react';
164+
165+
function MyComponent() {
166+
const [Editor, setEditor] = useState(null);
167+
168+
useEffect(() => {
169+
import('some-browser-only-library').then((mod) => {
170+
setEditor(() => mod.default);
171+
});
172+
}, []);
173+
174+
if (!Editor) return <div>Loading...</div>;
175+
return <Editor />;
176+
}
177+
```
178+
179+
### Hydration mismatch
180+
181+
**Cause**: The server-rendered HTML content doesn't match the client's first render. React checks for consistency during hydration, and mismatches will cause warnings or errors.
182+
183+
**Common scenarios**:
184+
185+
- Using `Date.now()` or random numbers
186+
- Rendering different content based on `window` object properties (like `window.innerWidth`)
187+
- Using data that only exists on the client (like localStorage)
188+
189+
**Solution**: Ensure the first render output is consistent between server and client. For content that needs to change dynamically on the client, use `useEffect` to update after hydration completes.
190+
191+
```tsx
192+
import { useEffect, useState } from 'react';
193+
194+
function MyComponent() {
195+
// First render uses default value for server/client consistency
196+
const [theme, setTheme] = useState('light');
197+
198+
useEffect(() => {
199+
// Read localStorage after hydration completes
200+
const savedTheme = localStorage.getItem('theme');
201+
if (savedTheme) {
202+
setTheme(savedTheme);
203+
}
204+
}, []);
205+
206+
return <div className={theme}>...</div>;
207+
}
71208
```
72209

73-
## Disabling SSG
210+
## Configuration
74211

75-
If you do not want to use Static Site Generation, you can disable it through the `ssg` config.
212+
You can control whether SSG is enabled through the `ssg` configuration:
76213

77-
```js title="rspress.config.ts"
214+
```ts title="rspress.config.ts"
78215
import { defineConfig } from '@rspress/core';
79216

80217
export default defineConfig({
81-
// ...
82-
ssg: false,
218+
ssg: true, // Default value, SSG enabled
83219
});
84220
```
85221

86-
:::warning
222+
If your site has special requirements, you can disable SSG:
87223

88-
Please be cautious when disabling SSG, as this will forfeit many of the advantages of Static Site Generation mentioned above.
224+
```ts title="rspress.config.ts"
225+
import { defineConfig } from '@rspress/core';
89226

227+
export default defineConfig({
228+
ssg: false, // Disable SSG, use CSR
229+
});
230+
```
231+
232+
:::warning
233+
Please be cautious when disabling SSG, as this will forfeit the advantages of faster first contentful paint and SEO benefits.
90234
:::
235+
236+
## Custom HTML content
237+
238+
Through `builderConfig.html.tags`, you can inject custom content into HTML, such as adding analytics code, scripts, or styles:
239+
240+
```ts title="rspress.config.ts"
241+
import { defineConfig } from '@rspress/core';
242+
243+
export default defineConfig({
244+
builderConfig: {
245+
html: {
246+
tags: [
247+
{
248+
tag: 'script',
249+
attrs: {
250+
src: 'https://cdn.example.com/analytics.js',
251+
},
252+
},
253+
],
254+
},
255+
},
256+
});
257+
```
258+
259+
For more configuration details, please refer to the [Rsbuild html.tags documentation](https://rsbuild.rs/config/html/tags).

0 commit comments

Comments
 (0)