Readme

React SSR and client-side hydration for Val Town

For streaming responses use ReactStream

Usage

/** @jsxImportSource https://esm.sh/react */ import resrv, { React } from "https://esm.town/v/jxnblk/resrv"; function App() { const [count, setCount] = React.useState(0); return ( <div> <h1>Resrv</h1> <p>React SSR with client-side hydration in Val Town</p> <pre>{count}</pre> <button onClick={() => setCount(count - 1)}>-</button> <button onClick={() => setCount(count + 1)}>+</button> </div> ); } export default resrv(App, import.meta.url);

Live example

React requires matching versions for SSR and hydration. Import React from https://esm.town/v/jxnblk/resrv to ensure your component uses the same version as this library (currently react@18.3.1).

HTML Root Hydration

To render a component that includes a <head> and <body> tag, pass root: true to the third options argument:

function App ({ script }) { return ( <body> <h1>Hello</h1> {script} </body> ); } export default resrv(App, import.meta.url, { root: true });

Inspired by https://www.val.town/v/stevekrouse/react_http

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/** @jsxImportSource https://esm.sh/react */
import { renderToString } from "https://esm.sh/react-dom@18.3.1/server";
import * as React from "https://esm.sh/react@18.3.1";
// API:
// import resrv, { React } from "https://esm.town/v/jxnblk/resrv";
// export default resrv(Component, import.meta.url);
export { React };
const createScript = (module: string) => `
import App from "${module}";
import { hydrateRoot } from "https://esm.sh/react-dom@18.3.1/client";
import { jsx } from "https://esm.sh/react@18.3.1/jsx-runtime";
const root = document.getElementById("root");
hydrateRoot(root, jsx(App, {}));
`;
const App = ({ Component, module, simple }: {
Component: any;
module: string;
simple?: boolean;
}) => {
const script = (
<script
type="module"
dangerouslySetInnerHTML={{ __html: createScript(module) }}
/>
);
if (simple) {
return (
<>
<head></head>
<body id="root">
<Component />
{script}
</body>
</>
);
}
return <Component script={script} />;
};
interface Options {
root?: boolean;
}
export default function resrv(Component, module: string, opts: Options = {}) {
return function<T extends object>(args: Request | T): Response | any {
if (args instanceof Request) {
const body = renderToString(<App Component={Component} module={module} simple={!opts.root} />);
const html = `<!DOCTYPE html>
<html lang="en" ${opts.root ? "id='root'" : ""}>
${body}
</html>`;
return new Response(html, {
headers: {
"Content-Type": "text/html",
},
});
}
return <App Component={Component} module={module} />;
};
}
Val Town is a social website to write and deploy JavaScript.
Build APIs and schedule functions from your browser.
Comments
Nobody has commented on this val yet: be the first!
May 5, 2024