@hiogawa/vite-rsc v0.3.4
@hiogawa/vite-rsc
Features
- Framework-less: Implements RSC conventions and provides direct
react-server-dom
runtime API without framework-specific abstractions. - CSS support: CSS is automatically code-split at client boundaries and injected upon rendering. For server components, CSS assets can be manually rendered via
import.meta.viteRscCss
API based on own routing conventions. - HMR support: Enables editing both client and server components without full page reloads.
- Runtime agnostic: Built on Vite environment API and works with other runtimes (e.g.,
@cloudflare/vite-plugin
).
Examples
Basic Concepts
This example can be found in ./examples/basic-doc
.
import rsc from "@hiogawa/vite-rsc/plugin";
export default defineConfig() {
plugins: [
rsc({
entries: {
// server entry with react-server condition, which should manage:
// - RSC serialization
// - server functions handling
rsc: "./src/entry.rsc.tsx",
// server entry without react-server condition, which should manage:
// - RSC deserialization for SSR
ssr: "./src/entry.ssr.tsx",
// main script entry executed on browser, which should manage:
// - RSC deserialization for hydration
// - refetch and re-render RSC
// - calling server functions
browser: "./src/entry.browser.tsx",
},
})
]
}
import * as ReactServer from "@hiogawa/vite-rsc/rsc"; // React core API
import { importSsr } from "@hiogawa/vite-rsc/rsc"; // Vite specifc helper
// the plugin assumes `rsc` entry having default export of request handler
export default async function handler(request: Request): Promise<Response> {
// serialize RSC
const root = <html><body><h1>Test</h1></body></html>;
const rscStream = ReactServer.renderToReadableStream(root);
// respond direct RSC stream request based on framework's convention
if (request.url.endsWith(".rsc")) {
return new Response(rscStream, {
headers: {
'Content-type': 'text/html'
}
})
}
// delegate to SSR environment for html rendering
const { handleSsr } = await importSsr<typeof import("./entry.ssr.tsx")>();
const htmlStream = await handleSsr(rscStream);
// respond html
return new Response(htmlStream, {
headers: {
'Content-type': 'text/html'
}
})
}
import * as ReactClient from "@hiogawa/vite-rsc/ssr";
import * as ReactDOMServer from "react-dom/server.edge";
import { getAssetsManifest } from "@hiogawa/vite-rsc/ssr";
export async function handleSsr(rscStream: ReadableStream) {
// deserialize RSC
// (NOTE: ssr deserization should be done inside a wrapper component, but it's simplified for doc.)
const root = await ReactClient.createFromReadableStream(rscStream);
// render html (traditional SSR)
const htmlStream = ReactDOMServer.renderToReadableStream(root, {
bootstrapScriptContent: getAssetsManifest().bootstrapScriptContent,
})
return htmlStream;
}
import * as ReactClient from "@hiogawa/vite-rsc/browser";
import * as ReactDOMClient from "react-dom/client";
async function main() {
// fetch and deserialize RSC
// (NOTE: extra fetch for hydration can be avoided but it's simplified for doc.)
const rscResponse = await fetch(window.location.href + ".rsc");
const root = await ReactClient.createFromReadableStream(rscResponse.body);
// hydration (traditional CSR)
ReactDOMClient.hydrateRoot(document, root);
}
main();
Handling server function
TODO
For now, read ./src/extra/{rsc,browser}
or ./examples/react-router
for the idea.
RSC API
These are mostly re-exports of react-server-dom-xxx/server
and react-server-dom-xxx/client
, aka React flight API.
@hiogawa/vite-rsc/rsc
renderToReadableStream
: RSC serializationcreateFromReadableStream
: RSC deserialization (This is also available on rsc environment itself. For example, it allows saving serailized RSC and deserializing it for later use.)decodeAction/decodeReply/loadServerAction
: server function related
@hiogawa/vite-rsc/ssr
createFromReadableStream
: RSC deserialization on server for SSR
@hiogawa/vite-rsc/browser
createFromReadableStream
: RSC deserialization on browser for hydrationcreateFromFetch
: a robust way ofcreateFromReadableStream((await fetch("...")).body)
encodeReply/setServerCallback
: server function related
Helper API
These API provide a necessary API to integrate multi environment features into an app.
@hiogawa/vite-rsc/rsc
importSsr<T>: () => Promise<T>
This allows importingssr
entry module insidersc
environment.
@hiogawa/vite-rsc/ssr
importRsc<T>: () => Promise<T>
This allows importingrsc
entry module insidessr
environment.getAssetsManifest().bootstrapScriptContent: string
This provides a code to execute browser entry on browser.import.meta.viteRscCss: React.ReactNode
This allows collecting css which is imported through a current server module and injecting them inside server components.
import "./test.css";
import child from "./child.tsx";
export function ServerPage() {
// this will include css assets for "test.css"
// and any css transitively imported through "child.tsx"
return <>
{import.meta.viteRscCss}
...
</>
}
Higher level RSC API
This is a simple wrapper of the first "RSC API". See ./examples/basic
for usage.
Also you can read implementations ./src/extra/{rsc,ssr,browser}
to understand
how bare RSC API is intended to be used.
@hiogawa/vite-rsc/extra/rsc
renderRequest
@hiogawa/vite-rsc/extra/ssr
renderHtml
@hiogawa/vite-rsc/extra/browser
hydrate
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
7 months ago
7 months ago
7 months ago