Skip to content

Commit 6558263

Browse files
committed
docs: run automd on example readme files
1 parent 044ac5b commit 6558263

16 files changed

Lines changed: 619 additions & 115 deletions

File tree

automd.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ function resolvePath(srcPath: string, options: { url?: string; dir?: string }):
187187
}
188188

189189
export default {
190-
input: ["README.md", "docs/**/*.md"],
190+
input: ["README.md", "docs/**/*.md", "examples/**/README.md"],
191191
generators: {
192192
compatDate: {
193193
name: "compatDate",

examples/fastify/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export default defineConfig({});
2525
"dev": "nitro dev"
2626
},
2727
"devDependencies": {
28-
"fastify": "^5.7.2",
28+
"fastify": "^5.7.4",
2929
"nitro": "latest"
3030
}
3131
}

examples/hono/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export default defineConfig({});
2525
"dev": "nitro dev"
2626
},
2727
"devDependencies": {
28-
"hono": "^4.11.7",
28+
"hono": "^4.11.8",
2929
"nitro": "latest"
3030
}
3131
}

examples/shiki/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export default defineConfig({
5353
},
5454
"devDependencies": {
5555
"nitro": "latest",
56-
"shiki": "^3.21.0"
56+
"shiki": "^3.22.0"
5757
}
5858
}
5959
```

examples/vite-rsc/README.md

Lines changed: 197 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@ dist
3333
"react-dom": "^19.2.4"
3434
},
3535
"devDependencies": {
36-
"@types/react": "^19.2.10",
36+
"@types/react": "^19.2.13",
3737
"@types/react-dom": "^19.2.3",
38-
"@vitejs/plugin-react": "^5.1.2",
39-
"@vitejs/plugin-rsc": "^0.5.17",
38+
"@vitejs/plugin-react": "^5.1.3",
39+
"@vitejs/plugin-rsc": "^0.5.19",
4040
"nitro": "latest",
4141
"rsc-html-stream": "^0.0.7",
4242
"vite": "beta"
@@ -860,6 +860,200 @@ export function parseRenderRequest(request: Request): RenderRequest {
860860

861861
<!-- automd:file src="GUIDE.md" -->
862862

863+
This example demonstrates React Server Components (RSC) using Vite's experimental RSC plugin with Nitro. It includes server components, client components, server actions, and streaming SSR.
864+
865+
## Overview
866+
867+
1. **SSR Entry** handles incoming requests and renders React components to HTML
868+
2. **Root Component** defines the page structure as a server component
869+
3. **Client Components** use the `"use client"` directive for interactive parts
870+
871+
## 1. SSR Entry
872+
873+
```tsx [app/framework/entry.ssr.tsx]
874+
import { createFromReadableStream } from "@vitejs/plugin-rsc/ssr";
875+
import React from "react";
876+
import type { ReactFormState } from "react-dom/client";
877+
import { renderToReadableStream } from "react-dom/server.edge";
878+
import { injectRSCPayload } from "rsc-html-stream/server";
879+
import type { RscPayload } from "./entry.rsc";
880+
881+
export default {
882+
fetch: async (request: Request) => {
883+
const rscEntryModule = await import.meta.viteRsc.loadModule<typeof import("./entry.rsc")>(
884+
"rsc",
885+
"index"
886+
);
887+
return rscEntryModule.default(request);
888+
},
889+
};
890+
891+
export async function renderHTML(
892+
rscStream: ReadableStream<Uint8Array>,
893+
options: {
894+
formState?: ReactFormState;
895+
nonce?: string;
896+
debugNoJS?: boolean;
897+
}
898+
): Promise<{ stream: ReadableStream<Uint8Array>; status?: number }> {
899+
// Duplicate one RSC stream into two.
900+
// - one for SSR (ReactClient.createFromReadableStream below)
901+
// - another for browser hydration payload by injecting <script>...FLIGHT_DATA...</script>.
902+
const [rscStream1, rscStream2] = rscStream.tee();
903+
904+
// Deserialize RSC stream back to React VDOM
905+
let payload: Promise<RscPayload> | undefined;
906+
function SsrRoot() {
907+
// Deserialization needs to be kicked off inside ReactDOMServer context
908+
// for ReactDOMServer preinit/preloading to work
909+
payload ??= createFromReadableStream<RscPayload>(rscStream1);
910+
return React.use(payload).root;
911+
}
912+
913+
// Render HTML (traditional SSR)
914+
const bootstrapScriptContent = await import.meta.viteRsc.loadBootstrapScriptContent("index");
915+
916+
let htmlStream: ReadableStream<Uint8Array>;
917+
let status: number | undefined;
918+
919+
try {
920+
htmlStream = await renderToReadableStream(<SsrRoot />, {
921+
bootstrapScriptContent: options?.debugNoJS ? undefined : bootstrapScriptContent,
922+
nonce: options?.nonce,
923+
formState: options?.formState,
924+
});
925+
} catch {
926+
// fallback to render an empty shell and run pure CSR on browser,
927+
// which can replay server component error and trigger error boundary.
928+
status = 500;
929+
htmlStream = await renderToReadableStream(
930+
<html>
931+
<body>
932+
<noscript>Internal Server Error: SSR failed</noscript>
933+
</body>
934+
</html>,
935+
{
936+
bootstrapScriptContent:
937+
`self.__NO_HYDRATE=1;` + (options?.debugNoJS ? "" : bootstrapScriptContent),
938+
nonce: options?.nonce,
939+
}
940+
);
941+
}
942+
943+
let responseStream: ReadableStream<Uint8Array> = htmlStream;
944+
if (!options?.debugNoJS) {
945+
// Initial RSC stream is injected in HTML stream as <script>...FLIGHT_DATA...</script>
946+
// using utility made by devongovett https://github.com/devongovett/rsc-html-stream
947+
responseStream = responseStream.pipeThrough(
948+
injectRSCPayload(rscStream2, {
949+
nonce: options?.nonce,
950+
})
951+
);
952+
}
953+
954+
return { stream: responseStream, status };
955+
}
956+
```
957+
958+
The SSR entry handles the rendering pipeline. It loads the RSC entry module, duplicates the RSC stream (one for SSR, one for hydration), deserializes the stream back to React VDOM, and renders it to HTML. The RSC payload is injected into the HTML for client hydration.
959+
960+
## 2. Root Server Component
961+
962+
```tsx [app/root.tsx]
963+
import "./index.css"; // css import is automatically injected in exported server components
964+
import viteLogo from "./assets/vite.svg";
965+
import { getServerCounter, updateServerCounter } from "./action.tsx";
966+
import reactLogo from "./assets/react.svg";
967+
import nitroLogo from "./assets/nitro.svg";
968+
import { ClientCounter } from "./client.tsx";
969+
970+
export function Root(props: { url: URL }) {
971+
return (
972+
<html lang="en">
973+
<head>
974+
{/* eslint-disable-next-line unicorn/text-encoding-identifier-case */}
975+
<meta charSet="UTF-8" />
976+
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
977+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
978+
<title>Nitro + Vite + RSC</title>
979+
</head>
980+
<body>
981+
<App {...props} />
982+
</body>
983+
</html>
984+
);
985+
}
986+
987+
function App(props: { url: URL }) {
988+
return (
989+
<div id="root">
990+
<div>
991+
<a href="https://vite.dev" target="_blank">
992+
<img src={viteLogo} className="logo" alt="Vite logo" />
993+
</a>
994+
<a href="https://react.dev/reference/rsc/server-components" target="_blank">
995+
<img src={reactLogo} className="logo react" alt="React logo" />
996+
</a>
997+
998+
<a href="https://v3.nitro.build" target="_blank">
999+
<img src={nitroLogo} className="logo" alt="Nitro logo" />
1000+
</a>
1001+
</div>
1002+
<h1>Vite + RSC + Nitro</h1>
1003+
<div className="card">
1004+
<ClientCounter />
1005+
</div>
1006+
<div className="card">
1007+
<form action={updateServerCounter.bind(null, 1)}>
1008+
<button>Server Counter: {getServerCounter()}</button>
1009+
</form>
1010+
</div>
1011+
<div className="card">Request URL: {props.url?.href}</div>
1012+
<ul className="read-the-docs">
1013+
<li>
1014+
Edit <code>src/client.tsx</code> to test client HMR.
1015+
</li>
1016+
<li>
1017+
Edit <code>src/root.tsx</code> to test server HMR.
1018+
</li>
1019+
<li>
1020+
Visit{" "}
1021+
<a href="./_.rsc" target="_blank">
1022+
<code>_.rsc</code>
1023+
</a>{" "}
1024+
to view RSC stream payload.
1025+
</li>
1026+
<li>
1027+
Visit{" "}
1028+
<a href="?__nojs" target="_blank">
1029+
<code>?__nojs</code>
1030+
</a>{" "}
1031+
to test server action without js enabled.
1032+
</li>
1033+
</ul>
1034+
</div>
1035+
);
1036+
}
1037+
```
1038+
1039+
Server components run only on the server. They can import CSS directly, use server-side data, and call server actions. The `ClientCounter` component is imported but runs on the client because it has the `"use client"` directive.
1040+
1041+
## 3. Client Component
1042+
1043+
```tsx [app/client.tsx]
1044+
"use client";
1045+
1046+
import React from "react";
1047+
1048+
export function ClientCounter() {
1049+
const [count, setCount] = React.useState(0);
1050+
1051+
return <button onClick={() => setCount((count) => count + 1)}>Client Counter: {count}</button>;
1052+
}
1053+
```
1054+
1055+
The `"use client"` directive marks this as a client component. It hydrates on the browser and handles interactive state. Server components can import and render client components, but client components cannot import server components.
1056+
8631057
<!-- /automd -->
8641058

8651059
## Learn More

examples/vite-ssr-preact/GUIDE.md

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Set up server-side rendering (SSR) with Preact, Vite, and Nitro. This setup enab
99

1010
## 1. Configure Vite
1111

12-
Add the Nitro and Preact plugins to your Vite config. Define the `client` environment with your client entry point:
12+
Add the Nitro and Preact plugins to your Vite config:
1313

1414
```js [vite.config.mjs]
1515
import { defineConfig } from "vite";
@@ -18,20 +18,9 @@ import preact from "@preact/preset-vite";
1818

1919
export default defineConfig({
2020
plugins: [nitro(), preact()],
21-
environments: {
22-
client: {
23-
build: {
24-
rollupOptions: {
25-
input: "./src/entry-client.tsx",
26-
},
27-
},
28-
},
29-
},
3021
});
3122
```
3223

33-
The `environments.client` configuration tells Vite which file to use as the browser entry point. Nitro automatically detects the server entry from files named `entry-server` or `server` in common directories.
34-
3524
## 2. Create the App Component
3625

3726
Create a shared Preact component that runs on both server and client:

examples/vite-ssr-preact/README.md

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ icon: i-logos-preact
2323
"@preact/preset-vite": "^2.10.3",
2424
"@tailwindcss/vite": "^4.1.18",
2525
"nitro": "latest",
26-
"preact": "^10.28.2",
26+
"preact": "^10.28.3",
2727
"preact-render-to-string": "^6.6.5",
2828
"tailwindcss": "^4.1.18",
2929
"vite": "beta"
@@ -48,15 +48,6 @@ import preact from "@preact/preset-vite";
4848

4949
export default defineConfig({
5050
plugins: [nitro(), preact()],
51-
environments: {
52-
client: {
53-
build: {
54-
rollupOptions: {
55-
input: "./src/entry-client.tsx",
56-
},
57-
},
58-
},
59-
},
6051
});
6152
```
6253

@@ -151,7 +142,7 @@ Set up server-side rendering (SSR) with Preact, Vite, and Nitro. This setup enab
151142

152143
## 1. Configure Vite
153144

154-
Add the Nitro and Preact plugins to your Vite config. Define the `client` environment with your client entry point:
145+
Add the Nitro and Preact plugins to your Vite config:
155146

156147
```js [vite.config.mjs]
157148
import { defineConfig } from "vite";
@@ -160,20 +151,9 @@ import preact from "@preact/preset-vite";
160151

161152
export default defineConfig({
162153
plugins: [nitro(), preact()],
163-
environments: {
164-
client: {
165-
build: {
166-
rollupOptions: {
167-
input: "./src/entry-client.tsx",
168-
},
169-
},
170-
},
171-
},
172154
});
173155
```
174156

175-
The `environments.client` configuration tells Vite which file to use as the browser entry point. Nitro automatically detects the server entry from files named `entry-server` or `server` in common directories.
176-
177157
## 2. Create the App Component
178158

179159
Create a shared Preact component that runs on both server and client:

examples/vite-ssr-react/GUIDE.md

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ Set up server-side rendering (SSR) with React, Vite, and Nitro. This setup enabl
99

1010
## 1. Configure Vite
1111

12-
Add the Nitro and React plugins to your Vite config. Define the `client` environment with your client entry point:
12+
Add the Nitro and React plugins to your Vite config:
1313

1414
```js [vite.config.mjs]
1515
import { defineConfig } from "vite";
@@ -18,16 +18,9 @@ import react from "@vitejs/plugin-react";
1818

1919
export default defineConfig({
2020
plugins: [nitro(), react()],
21-
environments: {
22-
client: {
23-
build: { rollupOptions: { input: "./src/entry-client.tsx" } },
24-
},
25-
},
2621
});
2722
```
2823

29-
The `environments.client` configuration tells Vite which file to use as the browser entry point. Nitro automatically detects the server entry from files named `entry-server` or `server` in common directories.
30-
3124
## 2. Create the App Component
3225

3326
Create a shared React component that runs on both server and client:

0 commit comments

Comments
 (0)