From dd6559d09e5788ed0ba8cf9aaf0ed506670016b8 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 22 Jul 2025 08:35:15 +0900 Subject: [PATCH 1/6] chore(rsc): deprecate `rsc-html-stream` re-exports --- packages/plugin-rsc/src/rsc-html-stream/browser.ts | 1 + packages/plugin-rsc/src/rsc-html-stream/ssr.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/plugin-rsc/src/rsc-html-stream/browser.ts b/packages/plugin-rsc/src/rsc-html-stream/browser.ts index eb2a74e8d..166faf332 100644 --- a/packages/plugin-rsc/src/rsc-html-stream/browser.ts +++ b/packages/plugin-rsc/src/rsc-html-stream/browser.ts @@ -1,4 +1,5 @@ import * as rscHtmlStreamClient from 'rsc-html-stream/client' +/** @deprecated use `rsc-html-stream/client` instead */ export const getRscStreamFromHtml = (): ReadableStream => rscHtmlStreamClient.rscStream diff --git a/packages/plugin-rsc/src/rsc-html-stream/ssr.ts b/packages/plugin-rsc/src/rsc-html-stream/ssr.ts index 109d09eb0..92c0b57d1 100644 --- a/packages/plugin-rsc/src/rsc-html-stream/ssr.ts +++ b/packages/plugin-rsc/src/rsc-html-stream/ssr.ts @@ -1,5 +1,6 @@ import * as rscHtmlStreamServer from 'rsc-html-stream/server' +/** @deprecated use `rsc-html-stream/server` instead */ export const injectRscStreamToHtml = ( stream: ReadableStream, options?: { nonce?: string }, From 918e7ae6daea32d5fa900f85538f25e70ba5be45 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 22 Jul 2025 08:47:04 +0900 Subject: [PATCH 2/6] chore: deps --- packages/plugin-rsc/README.md | 2 +- packages/plugin-rsc/examples/basic/package.json | 1 + packages/plugin-rsc/examples/starter/README.md | 4 ++-- packages/plugin-rsc/examples/starter/package.json | 1 + pnpm-lock.yaml | 6 ++++++ 5 files changed, 11 insertions(+), 3 deletions(-) diff --git a/packages/plugin-rsc/README.md b/packages/plugin-rsc/README.md index 8aadcb337..50a165080 100644 --- a/packages/plugin-rsc/README.md +++ b/packages/plugin-rsc/README.md @@ -11,7 +11,7 @@ This package provides [React Server Components](https://react.dev/reference/rsc/ ## Getting Started -You can start a project by copying an example locally by: +You can create a starter project by: ```sh npx degit vitejs/vite-plugin-react/packages/plugin-rsc/examples/starter my-app diff --git a/packages/plugin-rsc/examples/basic/package.json b/packages/plugin-rsc/examples/basic/package.json index bed815b99..ff1648a7f 100644 --- a/packages/plugin-rsc/examples/basic/package.json +++ b/packages/plugin-rsc/examples/basic/package.json @@ -25,6 +25,7 @@ "@vitejs/test-dep-client-in-server2": "file:./test-dep/client-in-server2", "@vitejs/test-dep-server-in-client": "file:./test-dep/server-in-client", "@vitejs/test-dep-server-in-server": "file:./test-dep/server-in-server", + "rsc-html-stream": "^0.0.7", "tailwindcss": "^4.1.11", "vite": "^7.0.4", "vite-plugin-inspect": "^11.3.0", diff --git a/packages/plugin-rsc/examples/starter/README.md b/packages/plugin-rsc/examples/starter/README.md index 15efa6164..daf79d3bd 100644 --- a/packages/plugin-rsc/examples/starter/README.md +++ b/packages/plugin-rsc/examples/starter/README.md @@ -24,11 +24,11 @@ See [`@vitejs/plugin-rsc`](https://github.com/vitejs/vite-plugin-react/tree/main - `import.meta.viteRsc.loadModule` - [`./src/framework/entry.ssr.tsx`](./src/framework/entry.ssr.tsx) - `@vitejs/plugin-rsc/ssr` - - `@vitejs/plugin-rsc/rsc-html-stream/ssr` - `import.meta.viteRsc.loadBootstrapScriptContent` + - `rsc-html-stream/server` - [`./src/framework/entry.browser.tsx`](./src/framework/entry.browser.tsx) - `@vitejs/plugin-rsc/browser` - - `@vitejs/plugin-rsc/rsc-html-stream/browser` + - `rsc-html-stream/client` ## Notes diff --git a/packages/plugin-rsc/examples/starter/package.json b/packages/plugin-rsc/examples/starter/package.json index b400b4da0..059e49fee 100644 --- a/packages/plugin-rsc/examples/starter/package.json +++ b/packages/plugin-rsc/examples/starter/package.json @@ -18,6 +18,7 @@ "@types/react": "^19.1.8", "@types/react-dom": "^19.1.6", "@vitejs/plugin-react": "latest", + "rsc-html-stream": "^0.0.7", "vite": "^7.0.4", "vite-plugin-inspect": "^11.3.0" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 74e6d51d0..7be516b19 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -542,6 +542,9 @@ importers: '@vitejs/test-dep-server-in-server': specifier: file:./test-dep/server-in-server version: file:packages/plugin-rsc/examples/basic/test-dep/server-in-server(react@19.1.0) + rsc-html-stream: + specifier: ^0.0.7 + version: 0.0.7 tailwindcss: specifier: ^4.1.11 version: 4.1.11 @@ -690,6 +693,9 @@ importers: '@vitejs/plugin-react': specifier: latest version: link:../../../plugin-react + rsc-html-stream: + specifier: ^0.0.7 + version: 0.0.7 vite: specifier: ^7.0.4 version: 7.0.4(@types/node@22.16.3)(jiti@2.4.2)(lightningcss@1.30.1)(tsx@4.20.3)(yaml@2.7.1) From 6eaba064cb99bf091df61d2541f14b759f875ace Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 22 Jul 2025 08:53:07 +0900 Subject: [PATCH 3/6] chore: replace --- .../examples/basic/src/framework/entry.browser.tsx | 4 ++-- .../plugin-rsc/examples/basic/src/framework/entry.ssr.tsx | 5 +++-- .../examples/ssg/src/framework/entry.browser.tsx | 7 +++---- .../plugin-rsc/examples/ssg/src/framework/entry.ssr.tsx | 4 ++-- .../starter-cf-single/src/framework/entry.browser.tsx | 4 ++-- .../examples/starter-cf-single/src/framework/entry.ssr.tsx | 4 ++-- .../examples/starter/src/framework/entry.browser.tsx | 4 ++-- .../examples/starter/src/framework/entry.ssr.tsx | 4 ++-- 8 files changed, 18 insertions(+), 18 deletions(-) diff --git a/packages/plugin-rsc/examples/basic/src/framework/entry.browser.tsx b/packages/plugin-rsc/examples/basic/src/framework/entry.browser.tsx index 9ba4ae4ee..473fc492d 100644 --- a/packages/plugin-rsc/examples/basic/src/framework/entry.browser.tsx +++ b/packages/plugin-rsc/examples/basic/src/framework/entry.browser.tsx @@ -1,7 +1,7 @@ import * as ReactClient from '@vitejs/plugin-rsc/browser' -import { getRscStreamFromHtml } from '@vitejs/plugin-rsc/rsc-html-stream/browser' import React from 'react' import * as ReactDOMClient from 'react-dom/client' +import { rscStream } from 'rsc-html-stream/client' import type { RscPayload } from './entry.rsc' async function main() { @@ -12,7 +12,7 @@ async function main() { // deserialize RSC stream back to React VDOM for CSR const initialPayload = await ReactClient.createFromReadableStream( // initial RSC stream is injected in SSR stream as - getRscStreamFromHtml(), + rscStream, ) // browser root component to (re-)render RSC payload as state diff --git a/packages/plugin-rsc/examples/basic/src/framework/entry.ssr.tsx b/packages/plugin-rsc/examples/basic/src/framework/entry.ssr.tsx index 129dbadf1..0fd5e92f3 100644 --- a/packages/plugin-rsc/examples/basic/src/framework/entry.ssr.tsx +++ b/packages/plugin-rsc/examples/basic/src/framework/entry.ssr.tsx @@ -1,8 +1,8 @@ -import { injectRscStreamToHtml } from '@vitejs/plugin-rsc/rsc-html-stream/ssr' // helper API import * as ReactClient from '@vitejs/plugin-rsc/ssr' // RSC API import React from 'react' import type { ReactFormState } from 'react-dom/client' import * as ReactDOMServer from 'react-dom/server.edge' +import { injectRSCPayload } from 'rsc-html-stream/server' import type { RscPayload } from './entry.rsc' export async function renderHTML( @@ -42,8 +42,9 @@ export async function renderHTML( let responseStream: ReadableStream = htmlStream if (!options?.debugNojs) { // initial RSC stream is injected in HTML stream as + // using utility made by devongovett https://github.com/devongovett/rsc-html-stream responseStream = responseStream.pipeThrough( - injectRscStreamToHtml(rscStream2, { + injectRSCPayload(rscStream2, { nonce: options?.nonce, }), ) diff --git a/packages/plugin-rsc/examples/ssg/src/framework/entry.browser.tsx b/packages/plugin-rsc/examples/ssg/src/framework/entry.browser.tsx index 6ef285d86..9ae2e9c85 100644 --- a/packages/plugin-rsc/examples/ssg/src/framework/entry.browser.tsx +++ b/packages/plugin-rsc/examples/ssg/src/framework/entry.browser.tsx @@ -1,5 +1,5 @@ import * as ReactClient from '@vitejs/plugin-rsc/browser' -import { getRscStreamFromHtml } from '@vitejs/plugin-rsc/rsc-html-stream/browser' +import { rscStream } from 'rsc-html-stream/client' import React from 'react' import ReactDomClient from 'react-dom/client' import { RSC_POSTFIX, type RscPayload } from './shared' @@ -12,9 +12,8 @@ async function hydrate(): Promise { setPayload(payload) } - const initialPayload = await ReactClient.createFromReadableStream( - getRscStreamFromHtml(), - ) + const initialPayload = + await ReactClient.createFromReadableStream(rscStream) let setPayload: (v: RscPayload) => void diff --git a/packages/plugin-rsc/examples/ssg/src/framework/entry.ssr.tsx b/packages/plugin-rsc/examples/ssg/src/framework/entry.ssr.tsx index 452978250..9a41454d9 100644 --- a/packages/plugin-rsc/examples/ssg/src/framework/entry.ssr.tsx +++ b/packages/plugin-rsc/examples/ssg/src/framework/entry.ssr.tsx @@ -1,4 +1,4 @@ -import { injectRscStreamToHtml } from '@vitejs/plugin-rsc/rsc-html-stream/ssr' +import { injectRSCPayload } from 'rsc-html-stream/server' import * as ReactClient from '@vitejs/plugin-rsc/ssr' import React from 'react' import * as ReactDomServer from 'react-dom/server.edge' @@ -24,6 +24,6 @@ export async function renderHtml(rscStream: ReadableStream) { await htmlStream.allReady let responseStream: ReadableStream = htmlStream - responseStream = responseStream.pipeThrough(injectRscStreamToHtml(rscStream2)) + responseStream = responseStream.pipeThrough(injectRSCPayload(rscStream2)) return responseStream } diff --git a/packages/plugin-rsc/examples/starter-cf-single/src/framework/entry.browser.tsx b/packages/plugin-rsc/examples/starter-cf-single/src/framework/entry.browser.tsx index 9ba4ae4ee..473fc492d 100644 --- a/packages/plugin-rsc/examples/starter-cf-single/src/framework/entry.browser.tsx +++ b/packages/plugin-rsc/examples/starter-cf-single/src/framework/entry.browser.tsx @@ -1,7 +1,7 @@ import * as ReactClient from '@vitejs/plugin-rsc/browser' -import { getRscStreamFromHtml } from '@vitejs/plugin-rsc/rsc-html-stream/browser' import React from 'react' import * as ReactDOMClient from 'react-dom/client' +import { rscStream } from 'rsc-html-stream/client' import type { RscPayload } from './entry.rsc' async function main() { @@ -12,7 +12,7 @@ async function main() { // deserialize RSC stream back to React VDOM for CSR const initialPayload = await ReactClient.createFromReadableStream( // initial RSC stream is injected in SSR stream as - getRscStreamFromHtml(), + rscStream, ) // browser root component to (re-)render RSC payload as state diff --git a/packages/plugin-rsc/examples/starter-cf-single/src/framework/entry.ssr.tsx b/packages/plugin-rsc/examples/starter-cf-single/src/framework/entry.ssr.tsx index c9e0fab93..9fae3468b 100644 --- a/packages/plugin-rsc/examples/starter-cf-single/src/framework/entry.ssr.tsx +++ b/packages/plugin-rsc/examples/starter-cf-single/src/framework/entry.ssr.tsx @@ -1,8 +1,8 @@ -import { injectRscStreamToHtml } from '@vitejs/plugin-rsc/rsc-html-stream/ssr' // helper API import * as ReactClient from '@vitejs/plugin-rsc/ssr' // RSC API import React from 'react' import type { ReactFormState } from 'react-dom/client' import * as ReactDOMServer from 'react-dom/server.edge' +import { injectRSCPayload } from 'rsc-html-stream/server' import type { RscPayload } from './entry.rsc' export type RenderHTML = typeof renderHTML @@ -45,7 +45,7 @@ export async function renderHTML( if (!options?.debugNojs) { // initial RSC stream is injected in HTML stream as responseStream = responseStream.pipeThrough( - injectRscStreamToHtml(rscStream2, { + injectRSCPayload(rscStream2, { nonce: options?.nonce, }), ) diff --git a/packages/plugin-rsc/examples/starter/src/framework/entry.browser.tsx b/packages/plugin-rsc/examples/starter/src/framework/entry.browser.tsx index 9ba4ae4ee..65bf5c1c8 100644 --- a/packages/plugin-rsc/examples/starter/src/framework/entry.browser.tsx +++ b/packages/plugin-rsc/examples/starter/src/framework/entry.browser.tsx @@ -1,5 +1,5 @@ import * as ReactClient from '@vitejs/plugin-rsc/browser' -import { getRscStreamFromHtml } from '@vitejs/plugin-rsc/rsc-html-stream/browser' +import { rscStream } from 'rsc-html-stream/client' import React from 'react' import * as ReactDOMClient from 'react-dom/client' import type { RscPayload } from './entry.rsc' @@ -12,7 +12,7 @@ async function main() { // deserialize RSC stream back to React VDOM for CSR const initialPayload = await ReactClient.createFromReadableStream( // initial RSC stream is injected in SSR stream as - getRscStreamFromHtml(), + rscStream, ) // browser root component to (re-)render RSC payload as state diff --git a/packages/plugin-rsc/examples/starter/src/framework/entry.ssr.tsx b/packages/plugin-rsc/examples/starter/src/framework/entry.ssr.tsx index 129dbadf1..1918f834b 100644 --- a/packages/plugin-rsc/examples/starter/src/framework/entry.ssr.tsx +++ b/packages/plugin-rsc/examples/starter/src/framework/entry.ssr.tsx @@ -1,8 +1,8 @@ -import { injectRscStreamToHtml } from '@vitejs/plugin-rsc/rsc-html-stream/ssr' // helper API import * as ReactClient from '@vitejs/plugin-rsc/ssr' // RSC API import React from 'react' import type { ReactFormState } from 'react-dom/client' import * as ReactDOMServer from 'react-dom/server.edge' +import { injectRSCPayload } from 'rsc-html-stream/server' import type { RscPayload } from './entry.rsc' export async function renderHTML( @@ -43,7 +43,7 @@ export async function renderHTML( if (!options?.debugNojs) { // initial RSC stream is injected in HTML stream as responseStream = responseStream.pipeThrough( - injectRscStreamToHtml(rscStream2, { + injectRSCPayload(rscStream2, { nonce: options?.nonce, }), ) From c9ae3cf95b1d71400858069e364c741cebac407e Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 22 Jul 2025 08:53:35 +0900 Subject: [PATCH 4/6] chore: tweak --- .../plugin-rsc/examples/ssg/src/framework/entry.browser.tsx | 2 +- packages/plugin-rsc/examples/ssg/src/framework/entry.ssr.tsx | 2 +- .../plugin-rsc/examples/starter/src/framework/entry.browser.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/plugin-rsc/examples/ssg/src/framework/entry.browser.tsx b/packages/plugin-rsc/examples/ssg/src/framework/entry.browser.tsx index 9ae2e9c85..7f77627e5 100644 --- a/packages/plugin-rsc/examples/ssg/src/framework/entry.browser.tsx +++ b/packages/plugin-rsc/examples/ssg/src/framework/entry.browser.tsx @@ -1,7 +1,7 @@ import * as ReactClient from '@vitejs/plugin-rsc/browser' -import { rscStream } from 'rsc-html-stream/client' import React from 'react' import ReactDomClient from 'react-dom/client' +import { rscStream } from 'rsc-html-stream/client' import { RSC_POSTFIX, type RscPayload } from './shared' async function hydrate(): Promise { diff --git a/packages/plugin-rsc/examples/ssg/src/framework/entry.ssr.tsx b/packages/plugin-rsc/examples/ssg/src/framework/entry.ssr.tsx index 9a41454d9..449c6ca21 100644 --- a/packages/plugin-rsc/examples/ssg/src/framework/entry.ssr.tsx +++ b/packages/plugin-rsc/examples/ssg/src/framework/entry.ssr.tsx @@ -1,7 +1,7 @@ -import { injectRSCPayload } from 'rsc-html-stream/server' import * as ReactClient from '@vitejs/plugin-rsc/ssr' import React from 'react' import * as ReactDomServer from 'react-dom/server.edge' +import { injectRSCPayload } from 'rsc-html-stream/server' import type { RscPayload } from './shared' export async function renderHtml(rscStream: ReadableStream) { diff --git a/packages/plugin-rsc/examples/starter/src/framework/entry.browser.tsx b/packages/plugin-rsc/examples/starter/src/framework/entry.browser.tsx index 65bf5c1c8..473fc492d 100644 --- a/packages/plugin-rsc/examples/starter/src/framework/entry.browser.tsx +++ b/packages/plugin-rsc/examples/starter/src/framework/entry.browser.tsx @@ -1,7 +1,7 @@ import * as ReactClient from '@vitejs/plugin-rsc/browser' -import { rscStream } from 'rsc-html-stream/client' import React from 'react' import * as ReactDOMClient from 'react-dom/client' +import { rscStream } from 'rsc-html-stream/client' import type { RscPayload } from './entry.rsc' async function main() { From b0888e86e1434d1a529f51b6b2f5b6252dd89a0d Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 22 Jul 2025 09:00:56 +0900 Subject: [PATCH 5/6] chore: fix starter-cf-single config --- .../plugin-rsc/examples/starter-cf-single/vite.config.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/plugin-rsc/examples/starter-cf-single/vite.config.ts b/packages/plugin-rsc/examples/starter-cf-single/vite.config.ts index 3b839553c..9617078a7 100644 --- a/packages/plugin-rsc/examples/starter-cf-single/vite.config.ts +++ b/packages/plugin-rsc/examples/starter-cf-single/vite.config.ts @@ -35,6 +35,9 @@ export default defineConfig({ platform: 'neutral', }, }, + optimizeDeps: { + include: ['turbo-stream'], + }, }, ssr: { keepProcessEnv: false, @@ -43,6 +46,9 @@ export default defineConfig({ // wrangler can deploy self-contained `dist/rsc` outDir: './dist/rsc/ssr', }, + resolve: { + noExternal: true, + }, }, }, }) From 6eb1c9dc00b6c45eab313ac6593a549244cd879f Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 22 Jul 2025 09:03:44 +0900 Subject: [PATCH 6/6] chore(rsc): fix temporary references in examples --- .../examples/no-ssr/src/framework/entry.rsc.tsx | 11 ++++++----- .../starter-cf-single/src/framework/entry.rsc.tsx | 12 ++++++------ .../examples/starter/src/framework/entry.rsc.tsx | 12 ++++++------ 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/packages/plugin-rsc/examples/no-ssr/src/framework/entry.rsc.tsx b/packages/plugin-rsc/examples/no-ssr/src/framework/entry.rsc.tsx index da968de3a..439cfddc9 100644 --- a/packages/plugin-rsc/examples/no-ssr/src/framework/entry.rsc.tsx +++ b/packages/plugin-rsc/examples/no-ssr/src/framework/entry.rsc.tsx @@ -32,11 +32,12 @@ export default async function handler(request: Request): Promise { } } - const rscStream = ReactServer.renderToReadableStream({ - root: , - returnValue, - formState, - }) + const rscPayload: RscPayload = { root: , formState, returnValue } + const rscOptions = { temporaryReferences } + const rscStream = ReactServer.renderToReadableStream( + rscPayload, + rscOptions, + ) return new Response(rscStream, { headers: { diff --git a/packages/plugin-rsc/examples/starter-cf-single/src/framework/entry.rsc.tsx b/packages/plugin-rsc/examples/starter-cf-single/src/framework/entry.rsc.tsx index a3060d2f4..1113c4bcc 100644 --- a/packages/plugin-rsc/examples/starter-cf-single/src/framework/entry.rsc.tsx +++ b/packages/plugin-rsc/examples/starter-cf-single/src/framework/entry.rsc.tsx @@ -41,12 +41,12 @@ async function handler(request: Request): Promise { // we render RSC stream after handling server function request // so that new render reflects updated state from server function call // to achieve single round trip to mutate and fetch from server. - const rscStream = ReactServer.renderToReadableStream({ - // in this example, we always render the same `` - root: , - returnValue, - formState, - }) + const rscPayload: RscPayload = { root: , formState, returnValue } + const rscOptions = { temporaryReferences } + const rscStream = ReactServer.renderToReadableStream( + rscPayload, + rscOptions, + ) // respond RSC stream without HTML rendering based on framework's convention. // here we use request header `content-type`. diff --git a/packages/plugin-rsc/examples/starter/src/framework/entry.rsc.tsx b/packages/plugin-rsc/examples/starter/src/framework/entry.rsc.tsx index b45e053e2..58b0c60be 100644 --- a/packages/plugin-rsc/examples/starter/src/framework/entry.rsc.tsx +++ b/packages/plugin-rsc/examples/starter/src/framework/entry.rsc.tsx @@ -51,12 +51,12 @@ export default async function handler(request: Request): Promise { // we render RSC stream after handling server function request // so that new render reflects updated state from server function call // to achieve single round trip to mutate and fetch from server. - const rscStream = ReactServer.renderToReadableStream({ - // in this example, we always render the same `` - root: , - returnValue, - formState, - }) + const rscPayload: RscPayload = { root: , formState, returnValue } + const rscOptions = { temporaryReferences } + const rscStream = ReactServer.renderToReadableStream( + rscPayload, + rscOptions, + ) // respond RSC stream without HTML rendering based on framework's convention. // here we use request header `content-type`.