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
/** @jsxImportSource https://esm.sh/react */
import { useEffect, useState } from "https://esm.sh/react@18.2.0";
import codeOnValTown from "https://esm.town/v/andreterron/codeOnValTown?v=46";
import { Button, Form, hydrate } from "https://esm.town/v/stevekrouse/ssr_react_mini";
// runs on page load, on the server
export async function loader(req: Request) {
// ensure any server-side imports only run server side
const { sqlite } = await import("https://esm.town/v/std/sqlite?v=4");
const [, { columns, rows }] = await sqlite.batch([
// you can optionally create your table here IF NOT EXISTS
`CREATE TABLE IF NOT EXISTS ssr_react_mini_startesr_clicks (
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)`,
// get the data your page needs on load
`select count(*) from ssr_react_mini_starter_clicks`,
]);
// return the props for your Component
return { initialClicks: rows[0][0] };
}
// handle <Form> submissions and other server-requests
export async function action(req: Request) {
const { sqlite } = await import("https://esm.town/v/std/sqlite?v=4");
if (req.method === "POST") {
await sqlite.execute(`INSERT INTO ssr_react_mini_starter_clicks DEFAULT VALUES`);
}
return Response.json("OK");
}
export function Component({ initialClicks }: { initialClicks: number }) {
const [clicks, setClicks] = useState(0);
const addClick = () => setClicks(clicks + 1); // optimistic, client-side
return (
<html>
<head>
<title>SSR React Starter</title>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<script src="https://cdn.tailwindcss.com" />
</head>
<body className="max-w-md mx-auto p-10">
<h1 className="text-3xl font-bold">Hello Val Town</h1>
<div className="flex justify-between">
<div>
<div>{clicks + initialClicks} clicks ever</div>
<div>{clicks} clicks since the page loaded</div>
</div>
<Form onSubmit={addClick}>
<Button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">Click!</Button>
</Form>
</div>
</body>
</html>
);
}
export default codeOnValTown(hydrate(import.meta.url));