Skip to content

Commit 01894d0

Browse files
Copilothi-ogawa
andcommitted
Update only react-router-vite entry files with RSC improvements
Co-authored-by: hi-ogawa <[email protected]>
1 parent f165436 commit 01894d0

36 files changed

+6365
-278
lines changed
Lines changed: 17 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,85 +1,34 @@
1-
# Welcome to React Router! (Experimental RSC)
1+
# rsc react-router
22

3-
⚠️ **EXPERIMENTAL**: This template demonstrates React Server Components with React Router. This is experimental technology and not recommended for production use.
4-
5-
A modern template for exploring React Server Components (RSC) with React Router, powered by Vite.
3+
https://vite-rsc-react-router.hiro18181.workers.dev
64

75
> [!NOTE]
8-
> React Router now provides [official RSC support](https://reactrouter.com/how-to/react-server-components) for Vite. This example is updated to match the latest official template from @remix-run/react-router-templates.
9-
10-
## Features
6+
> React Router now provides [official RSC support](https://reactrouter.com/how-to/react-server-components) for Vite. The example might not be kept up to date with the latest version. Please refer to React Router's official documentation for the latest integrations.
117
12-
- 🧪 **Experimental React Server Components**
13-
- 🚀 Server-side rendering with RSC
14-
- ⚡️ Hot Module Replacement (HMR)
15-
- 📦 Asset bundling and optimization with Vite
16-
- 🔄 Data loading and mutations
17-
- 🔒 TypeScript by default
18-
- 🎉 TailwindCSS for styling
19-
- 📖 [React Router docs](https://reactrouter.com/)
20-
- 📚 [React Server Components guide](https://reactrouter.com/how-to/react-server-components)
8+
Vite RSC example based on demo made by React router team with Parcel:
219

22-
## Getting Started
10+
- https://github.com/jacob-ebey/parcel-plugin-react-router/
11+
- https://github.com/jacob-ebey/experimental-parcel-react-router-starter
12+
- https://github.com/remix-run/react-router/tree/rsc/playground/rsc-vite
2313

24-
### Installation
14+
See also [`rsc-movies`](https://github.com/hi-ogawa/rsc-movies/).
2515

26-
Install the dependencies:
27-
28-
```bash
29-
npm install
30-
```
16+
[![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/vitejs/vite-plugin-react/tree/main/packages/plugin-rsc/examples/react-router?file=src%2Froutes%2Froot.tsx)
3117

32-
### Development
18+
Or try it locally by:
3319

34-
Start the development server with HMR:
35-
36-
```bash
20+
```sh
21+
npx giget gh:vitejs/vite-plugin-react/packages/plugin-rsc/examples/react-router my-app
22+
cd my-app
23+
npm i
3724
npm run dev
38-
```
39-
40-
Your application will be available at `http://localhost:5173`.
41-
42-
## Building for Production
43-
44-
Create a production build:
45-
46-
```bash
4725
npm run build
48-
```
49-
50-
## Running Production Build
51-
52-
Run the production server:
26+
npm run preview
5327

54-
```bash
55-
npm start
56-
```
57-
58-
## Understanding React Server Components
59-
60-
This template includes three entry points:
61-
62-
- **`entry.rsc.tsx`** - React Server Components entry point
63-
- **`entry.ssr.tsx`** - Server-side rendering entry point
64-
- **`entry.browser.tsx`** - Client-side hydration entry point
65-
66-
Learn more about React Server Components with React Router in our [comprehensive guide](https://reactrouter.com/how-to/react-server-components).
67-
68-
## Styling
69-
70-
This template comes with [Tailwind CSS](https://tailwindcss.com/) already configured for a simple default starting experience. You can use whatever CSS framework you prefer.
71-
72-
## CloudFlare Support
73-
74-
CloudFlare specific configurations are available in the `cf/` directory:
75-
76-
```bash
28+
# run on @cloudflare/vite-plugin and deploy.
29+
# a separate configuration is found in ./cf/vite.config.ts
7730
npm run cf-dev
7831
npm run cf-build
7932
npm run cf-preview
8033
npm run cf-release
8134
```
82-
83-
---
84-
85-
Built with ❤️ using React Router.
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
@theme {
2+
--default-font-family: 'Patrick Hand SC', sans-serif;
3+
--default-mono-font-family: 'Patrick Hand SC', sans-serif;
4+
5+
--color-foreground: black;
6+
--color-danger: rgb(167, 52, 45);
7+
--color-secondary: rgb(11, 116, 213);
8+
--color-success: rgb(134, 163, 97);
9+
--color-warning: rgb(221, 205, 69);
10+
--color-border: #cdcccb;
11+
--color-border-active: rgba(0, 0, 0, 0.2);
12+
13+
--color-paper-background: white;
14+
--color-paper-border: #cdcccb;
15+
--shadow-paper: -1px 5px 35px -9px rgba(0, 0, 0, 0.2);
16+
17+
--shadow-btn: 15px 28px 25px -18px rgba(0, 0, 0, 0.2);
18+
--shadow-btn-hover: 2px 8px 8px -5px rgba(0, 0, 0, 0.3);
19+
--color-btn-border: black;
20+
--btn-color-danger: var(--color-danger);
21+
--btn-color-secondary: var(--color-secondary);
22+
--btn-color-success: var(--color-success);
23+
--btn-color-warning: var(--color-warning);
24+
}
25+
26+
@utility paper-border {
27+
@apply border-2 border-border;
28+
border-bottom-left-radius: 25px 115px;
29+
border-bottom-right-radius: 155px 25px;
30+
border-top-left-radius: 15px 225px;
31+
border-top-right-radius: 25px 150px;
32+
}
33+
34+
@utility no-paper-border {
35+
@apply border-0;
36+
border-bottom-left-radius: 0;
37+
border-bottom-right-radius: 0;
38+
border-top-left-radius: 0;
39+
border-top-right-radius: 0;
40+
}
41+
42+
@utility paper-underline {
43+
@apply border-b-3 border-[currentcolor];
44+
border-bottom-left-radius: 15px 3px;
45+
border-bottom-right-radius: 15px 5px;
46+
border-bottom-style: solid;
47+
}
48+
49+
@utility paper-underline-hover {
50+
@apply paper-underline border-transparent;
51+
@variant hover {
52+
@apply border-[currentcolor];
53+
}
54+
}
55+
56+
@utility paper {
57+
@apply border border-paper-border bg-paper-background p-8 shadow-paper;
58+
}
59+
60+
@utility breadcrumbs {
61+
@apply flex flex-wrap gap-2;
62+
& > * {
63+
@apply inline-block after:text-lg after:content-[""] not-last:after:ml-2 not-last:after:text-foreground not-last:after:content-["/"];
64+
}
65+
& > a {
66+
@apply text-secondary;
67+
}
68+
}
69+
70+
@utility btn {
71+
@apply inline-block cursor-pointer bg-paper-background paper-border px-4 py-2 text-lg shadow-btn transition-[shadow_transition];
72+
73+
@variant active {
74+
@apply border-border-active;
75+
}
76+
@variant hover {
77+
@apply translate-y-1 shadow-btn-hover;
78+
}
79+
80+
&.btn-icon {
81+
@apply aspect-square px-2 py-2;
82+
& img,
83+
& svg {
84+
@apply h-7 w-7;
85+
}
86+
}
87+
}
88+
89+
@utility btn-* {
90+
border-color: --value(--btn-color-*);
91+
color: --value(--btn-color-*);
92+
}
93+
94+
@utility btn-sm {
95+
@apply px-2 py-1 text-base;
96+
}
97+
98+
@utility btn-lg {
99+
@apply px-6 py-3 text-2xl;
100+
}
101+
102+
@utility label {
103+
@apply mb-1 block font-semibold;
104+
}
105+
106+
@utility input {
107+
@apply paper-border px-3 py-2;
108+
109+
@variant disabled {
110+
@apply border-border-active;
111+
}
112+
}
113+
114+
@utility checkbox {
115+
@apply h-6 w-6 paper-border;
116+
117+
@variant disabled {
118+
@apply border-border-active;
119+
}
120+
}
121+
122+
@utility select {
123+
@apply paper-border px-3 py-2;
124+
125+
@variant disabled {
126+
@apply border-border-active;
127+
}
128+
}
129+
130+
@layer base {
131+
body {
132+
@apply text-foreground;
133+
}
134+
135+
* {
136+
@apply outline-secondary;
137+
}
138+
}
139+
140+
@layer utilities {
141+
.prose {
142+
:where(u):not(:where([class~='not-prose'], [class~='not-prose'] *)) {
143+
@apply paper-underline no-underline;
144+
}
145+
146+
:where(a):not(:where([class~='not-prose'], [class~='not-prose'] *)) {
147+
@apply paper-underline-hover no-underline text-secondary;
148+
}
149+
}
150+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import './styles.css'
2+
import { Link, Outlet } from 'react-router'
3+
import { ServerHmr } from '../react-router-vite/server-hmr'
4+
import { TestClientState, TestHydrated } from './routes/client'
5+
import { DumpError, GlobalNavigationLoadingBar } from './routes/root.client'
6+
7+
export function Layout({ children }: { children: React.ReactNode }) {
8+
console.log('Layout')
9+
return (
10+
<html lang="en">
11+
<head>
12+
<meta charSet="utf-8" />
13+
<meta name="viewport" content="width=device-width, initial-scale=1" />
14+
<title>React Router Vite</title>
15+
</head>
16+
<body>
17+
<header className="container px-8 my-8 mx-auto">
18+
<nav className="paper paper-border">
19+
<ul className="flex gap-4 flex-wrap">
20+
<li className="flex gap-4 not-last:after:block not-last:after:content-['|']">
21+
<Link to="/">Home</Link>
22+
</li>
23+
<li className="flex gap-4 not-last:after:block">
24+
<Link to="/about">About</Link>
25+
</li>
26+
<li className="flex-1"></li>
27+
<li className="flex items-center gap-2 text-gray-500">
28+
<TestHydrated />
29+
<TestClientState />
30+
<span data-testid="root-style" className="text-[#0000ff]">
31+
[style]
32+
</span>
33+
</li>
34+
</ul>
35+
</nav>
36+
</header>
37+
<GlobalNavigationLoadingBar />
38+
<ServerHmr />
39+
{children}
40+
</body>
41+
</html>
42+
)
43+
}
44+
45+
export default function Component() {
46+
console.log('Root')
47+
return (
48+
<>
49+
<Outlet />
50+
</>
51+
)
52+
}
53+
54+
export function ErrorBoundary() {
55+
return <DumpError />
56+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { type RouteConfig, index, route } from '@react-router/dev/routes'
2+
3+
export default [
4+
index('routes/home.tsx'),
5+
route('about', 'routes/about.tsx'),
6+
] satisfies RouteConfig
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
'use client'
2+
3+
import React from 'react'
4+
5+
export function Component() {
6+
const [count, setCount] = React.useState(0)
7+
8+
return (
9+
<main className="container my-8 px-8 mx-auto">
10+
<article className="paper prose max-w-none">
11+
<h1>About</h1>
12+
<p>This is the about page.</p>
13+
<p className="test-style-home">[test-style-home]</p>
14+
<button className="btn" onClick={() => setCount((c) => c + 1)}>
15+
Client counter: {count}
16+
</button>
17+
</article>
18+
</main>
19+
)
20+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
'use client'
2+
3+
import React from 'react'
4+
5+
export function TestHydrated() {
6+
const hydrated = React.useSyncExternalStore(
7+
React.useCallback(() => () => {}, []),
8+
() => true,
9+
() => false,
10+
)
11+
return <span data-testid="hydrated">[hydrated: {hydrated ? 1 : 0}]</span>
12+
}
13+
14+
export function TestClientState() {
15+
return (
16+
<input
17+
className="input py-0"
18+
data-testid="client-state"
19+
placeholder="client-state"
20+
/>
21+
)
22+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
'use server'
2+
3+
export async function sayHello(defaultName: string, formData: FormData) {
4+
await new Promise((resolve) => setTimeout(resolve, 500))
5+
const name = formData.get('name') || defaultName
6+
console.log(`Hello, ${name}`)
7+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
'use client'
2+
3+
import { useFormStatus } from 'react-dom'
4+
5+
export function PendingButton() {
6+
const status = useFormStatus()
7+
return (
8+
<button className="btn" type="submit" disabled={status.pending}>
9+
{status.pending ? 'Pending...' : 'Log on server'}
10+
</button>
11+
)
12+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.test-style-home {
2+
color: rgb(250, 150, 0);
3+
}

0 commit comments

Comments
 (0)