Skip to content

Commit dc79b1a

Browse files
Add Cloudflare template back (#162)
1 parent 0106dd2 commit dc79b1a

19 files changed

+524
-0
lines changed

cloudflare/.gitignore

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
.DS_Store
2+
.env
3+
/node_modules/
4+
*.tsbuildinfo
5+
6+
# React Router
7+
/.react-router/
8+
/build/
9+
10+
# Cloudflare
11+
.mf
12+
.wrangler
13+
.dev.vars*
14+
worker-configuration.d.ts
15+

cloudflare/README.md

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Welcome to React Router!
2+
3+
A modern, production-ready template for building full-stack React applications using React Router.
4+
5+
## Features
6+
7+
- 🚀 Server-side rendering
8+
- ⚡️ Hot Module Replacement (HMR)
9+
- 📦 Asset bundling and optimization
10+
- 🔄 Data loading and mutations
11+
- 🔒 TypeScript by default
12+
- 🎉 TailwindCSS for styling
13+
- 📖 [React Router docs](https://reactrouter.com/)
14+
15+
## Getting Started
16+
17+
### Installation
18+
19+
Install the dependencies:
20+
21+
```bash
22+
npm install
23+
```
24+
25+
### Development
26+
27+
Start the development server with HMR:
28+
29+
```bash
30+
npm run dev
31+
```
32+
33+
Your application will be available at `http://localhost:5173`.
34+
35+
## Previewing the Production Build
36+
37+
Preview the production build locally:
38+
39+
```bash
40+
npm run preview
41+
```
42+
43+
## Building for Production
44+
45+
Create a production build:
46+
47+
```bash
48+
npm run build
49+
```
50+
51+
## Deployment
52+
53+
Deployment is done using the Wrangler CLI.
54+
55+
To build and deploy directly to production:
56+
57+
```sh
58+
npm run deploy
59+
```
60+
61+
To deploy a preview URL:
62+
63+
```sh
64+
npx wrangler versions upload
65+
```
66+
67+
You can then promote a version to production after verification or roll it out progressively.
68+
69+
```sh
70+
npx wrangler versions deploy
71+
```
72+
73+
## Styling
74+
75+
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.
76+
77+
---
78+
79+
Built with ❤️ using React Router.

cloudflare/app/app.css

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
@import "tailwindcss" source(".");
2+
3+
@theme {
4+
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif,
5+
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
6+
}
7+
8+
html,
9+
body {
10+
@apply bg-white dark:bg-gray-950;
11+
12+
@media (prefers-color-scheme: dark) {
13+
color-scheme: dark;
14+
}
15+
}

cloudflare/app/entry.server.tsx

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import type { AppLoadContext, EntryContext } from "react-router";
2+
import { ServerRouter } from "react-router";
3+
import { isbot } from "isbot";
4+
import { renderToReadableStream } from "react-dom/server";
5+
6+
export default async function handleRequest(
7+
request: Request,
8+
responseStatusCode: number,
9+
responseHeaders: Headers,
10+
routerContext: EntryContext,
11+
_loadContext: AppLoadContext
12+
) {
13+
let shellRendered = false;
14+
const userAgent = request.headers.get("user-agent");
15+
16+
const body = await renderToReadableStream(
17+
<ServerRouter context={routerContext} url={request.url} />,
18+
{
19+
onError(error: unknown) {
20+
responseStatusCode = 500;
21+
// Log streaming rendering errors from inside the shell. Don't log
22+
// errors encountered during initial shell rendering since they'll
23+
// reject and get logged in handleDocumentRequest.
24+
if (shellRendered) {
25+
console.error(error);
26+
}
27+
},
28+
}
29+
);
30+
shellRendered = true;
31+
32+
// Ensure requests from bots and SPA Mode renders wait for all content to load before responding
33+
// https://react.dev/reference/react-dom/server/renderToPipeableStream#waiting-for-all-content-to-load-for-crawlers-and-static-generation
34+
if ((userAgent && isbot(userAgent)) || routerContext.isSpaMode) {
35+
await body.allReady;
36+
}
37+
38+
responseHeaders.set("Content-Type", "text/html");
39+
return new Response(body, {
40+
headers: responseHeaders,
41+
status: responseStatusCode,
42+
});
43+
}

cloudflare/app/root.tsx

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
import {
2+
isRouteErrorResponse,
3+
Links,
4+
Meta,
5+
Outlet,
6+
Scripts,
7+
ScrollRestoration,
8+
} from "react-router";
9+
10+
import type { Route } from "./+types/root";
11+
import "./app.css";
12+
13+
export const links: Route.LinksFunction = () => [
14+
{ rel: "preconnect", href: "https://fonts.googleapis.com" },
15+
{
16+
rel: "preconnect",
17+
href: "https://fonts.gstatic.com",
18+
crossOrigin: "anonymous",
19+
},
20+
{
21+
rel: "stylesheet",
22+
href: "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap",
23+
},
24+
];
25+
26+
export function Layout({ children }: { children: React.ReactNode }) {
27+
return (
28+
<html lang="en">
29+
<head>
30+
<meta charSet="utf-8" />
31+
<meta name="viewport" content="width=device-width, initial-scale=1" />
32+
<Meta />
33+
<Links />
34+
</head>
35+
<body>
36+
{children}
37+
<ScrollRestoration />
38+
<Scripts />
39+
</body>
40+
</html>
41+
);
42+
}
43+
44+
export default function App() {
45+
return <Outlet />;
46+
}
47+
48+
export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
49+
let message = "Oops!";
50+
let details = "An unexpected error occurred.";
51+
let stack: string | undefined;
52+
53+
if (isRouteErrorResponse(error)) {
54+
message = error.status === 404 ? "404" : "Error";
55+
details =
56+
error.status === 404
57+
? "The requested page could not be found."
58+
: error.statusText || details;
59+
} else if (import.meta.env.DEV && error && error instanceof Error) {
60+
details = error.message;
61+
stack = error.stack;
62+
}
63+
64+
return (
65+
<main className="pt-16 p-4 container mx-auto">
66+
<h1>{message}</h1>
67+
<p>{details}</p>
68+
{stack && (
69+
<pre className="w-full p-4 overflow-x-auto">
70+
<code>{stack}</code>
71+
</pre>
72+
)}
73+
</main>
74+
);
75+
}

cloudflare/app/routes.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { type RouteConfig, index } from "@react-router/dev/routes";
2+
3+
export default [index("routes/home.tsx")] satisfies RouteConfig;

cloudflare/app/routes/home.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import type { Route } from "./+types/home";
2+
import { Welcome } from "../welcome/welcome";
3+
4+
export function meta({}: Route.MetaArgs) {
5+
return [
6+
{ title: "New React Router App" },
7+
{ name: "description", content: "Welcome to React Router!" },
8+
];
9+
}
10+
11+
export function loader({ context }: Route.LoaderArgs) {
12+
return { message: context.cloudflare.env.VALUE_FROM_CLOUDFLARE };
13+
}
14+
15+
export default function Home({ loaderData }: Route.ComponentProps) {
16+
return <Welcome message={loaderData.message} />;
17+
}
Lines changed: 23 additions & 0 deletions
Loading
Lines changed: 23 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)