Skip to content

Commit 0a52a18

Browse files
authored
React Router 7 example (#7694)
* init rr7 example * spruce up example * final tweaks * Delete orm/react-router-7/.mise.toml * remove remix content * Update orm/remix/README.md * test file
1 parent 42b6b01 commit 0a52a18

38 files changed

Lines changed: 494 additions & 1283 deletions
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22

33
set -eu
44

5-
npm install
5+
npm install
66
npx prisma migrate dev --name init
77
npm run dev &
88
pid=$!
99

1010
sleep 15
1111

1212
# check frontend
13-
curl --fail 'http://localhost:3000/'
13+
curl --fail 'http://localhost:5173'
1414

1515
kill "$pid"

orm/react-router-7/.gitignore

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.DS_Store
2+
/node_modules/
3+
4+
# React Router
5+
/.react-router/
6+
/build/
7+
8+
.mise*
9+
10+
prisma/dev.db*
11+
prisma/migrations/

orm/react-router-7/README.md

Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
# React Router 7 Example
2+
3+
This example shows how to implement a simple web app using [React Router 7](https://reactrouter.com) and [Prisma ORM](https://www.prisma.io/docs). This example was bootstrapped using the React Router CLI tool `create-react-router`.
4+
5+
## Getting started
6+
7+
### 1. Download the example and navigate to the project directory
8+
9+
Download this example:
10+
11+
```
12+
npx try-prisma@latest --template orm/react-router-7
13+
```
14+
15+
Then navigate to the project directory
16+
17+
```
18+
cd react-router-7
19+
```
20+
21+
<details><summary><strong>Alternative:</strong> Clone the entire repo</summary>
22+
23+
Clone this repository:
24+
25+
```
26+
git clone git@github.com:prisma/prisma-examples.git --depth=1
27+
```
28+
29+
Install npm dependencies:
30+
31+
```
32+
cd prisma-examples/orm/nextjs
33+
npm install
34+
```
35+
36+
</details>
37+
38+
#### [Optional] Switch database to Prisma Postgres
39+
40+
This example uses a local SQLite database by default. If you want to use to [Prisma Postgres](https://prisma.io/postgres), follow these instructions (otherwise, skip to the next step):
41+
42+
1. Set up a new Prisma Postgres instance in the Prisma Data Platform [Console](https://console.prisma.io) and copy the database connection URL.
43+
2. Update the `datasource` block to use `postgresql` as the `provider` and paste the database connection URL as the value for `url`:
44+
45+
```prisma
46+
datasource db {
47+
provider = "postgresql"
48+
url = "prisma+postgres://accelerate.prisma-data.net/?api_key=ey...."
49+
}
50+
```
51+
52+
> **Note**: In production environments, we recommend that you set your connection URL via an [environment variable](https://www.prisma.io/docs/orm/more/development-environment/environment-variables/managing-env-files-and-setting-variables), e.g. using a `.env` file.
53+
54+
3. Install the Prisma Accelerate extension:
55+
```
56+
npm install @prisma/extension-accelerate
57+
```
58+
4. Add the Accelerate extension to the `PrismaClient` instance:
59+
60+
```diff
61+
+ import { withAccelerate } from "@prisma/extension-accelerate"
62+
63+
+ const prisma = new PrismaClient().$extends(withAccelerate())
64+
```
65+
66+
That's it, your project is now configured to use Prisma Postgres!
67+
68+
### 2. Generate Prisma Client
69+
70+
Run the following command to generate the Prisma Client. This is what you will be using to interact with your database.
71+
72+
```
73+
npx prisma generate
74+
```
75+
76+
### 3. Start the React Router 7 server
77+
78+
```
79+
npm run dev
80+
```
81+
82+
The server is now running at http://localhost:5173. You can now view your todo list!
83+
84+
## Switch to another database (e.g. PostgreSQL, MySQL, SQL Server, MongoDB)
85+
86+
If you want to try this example with another database than SQLite, you can adjust the the database connection in [`prisma/schema.prisma`](./prisma/schema.prisma) by reconfiguring the `datasource` block.
87+
88+
Learn more about the different connection configurations in the [docs](https://www.prisma.io/docs/reference/database-reference/connection-urls).
89+
90+
<details><summary>Expand for an overview of example configurations with different databases</summary>
91+
92+
### PostgreSQL
93+
94+
For PostgreSQL, the connection URL has the following structure:
95+
96+
```prisma
97+
datasource db {
98+
provider = "postgresql"
99+
url = "postgresql://USER:PASSWORD@HOST:PORT/DATABASE?schema=SCHEMA"
100+
}
101+
```
102+
103+
Here is an example connection string with a local PostgreSQL database:
104+
105+
```prisma
106+
datasource db {
107+
provider = "postgresql"
108+
url = "postgresql://janedoe:mypassword@localhost:5432/notesapi?schema=public"
109+
}
110+
```
111+
112+
### MySQL
113+
114+
For MySQL, the connection URL has the following structure:
115+
116+
```prisma
117+
datasource db {
118+
provider = "mysql"
119+
url = "mysql://USER:PASSWORD@HOST:PORT/DATABASE"
120+
}
121+
```
122+
123+
Here is an example connection string with a local MySQL database:
124+
125+
```prisma
126+
datasource db {
127+
provider = "mysql"
128+
url = "mysql://janedoe:mypassword@localhost:3306/notesapi"
129+
}
130+
```
131+
132+
### Microsoft SQL Server
133+
134+
Here is an example connection string with a local Microsoft SQL Server database:
135+
136+
```prisma
137+
datasource db {
138+
provider = "sqlserver"
139+
url = "sqlserver://localhost:1433;initial catalog=sample;user=sa;password=mypassword;"
140+
}
141+
```
142+
143+
### MongoDB
144+
145+
Here is an example connection string with a local MongoDB database:
146+
147+
```prisma
148+
datasource db {
149+
provider = "mongodb"
150+
url = "mongodb://USERNAME:PASSWORD@HOST/DATABASE?authSource=admin&retryWrites=true&w=majority"
151+
}
152+
```
153+
154+
</details>
155+
156+
## Next steps
157+
158+
- Check out the [Prisma docs](https://www.prisma.io/docs)
159+
- Share your feedback on the [Prisma Discord](https://pris.ly/discord/)
160+
- Create issues and ask questions on [GitHub](https://github.com/prisma/prisma/)

orm/react-router-7/app/app.css

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
@import "tailwindcss";
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+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { useFetcher } from 'react-router'
2+
import type { Todo } from '@prisma/client'
3+
4+
type TodoProps = {
5+
todo: Todo
6+
}
7+
8+
export function TodoItem({ todo }: TodoProps) {
9+
const updateFetcher = useFetcher()
10+
const deleteFetcher = useFetcher()
11+
12+
return (
13+
<div className="flex items-center gap-4 p-4 bg-white dark:bg-gray-800 rounded-lg shadow">
14+
<updateFetcher.Form
15+
method="PATCH"
16+
className="flex items-center gap-4 flex-1"
17+
onChange={(e) =>
18+
updateFetcher.submit(e.currentTarget, { method: 'PATCH' })
19+
}
20+
>
21+
<input type="hidden" name="id" value={todo.id} />
22+
<input
23+
type="checkbox"
24+
name="complete"
25+
defaultChecked={todo.complete}
26+
className="w-5 h-5"
27+
/>
28+
<span
29+
className={`flex-1 text-gray-900 dark:text-gray-100 ${
30+
todo.complete ? 'line-through text-gray-500 dark:text-gray-400' : ''
31+
}`}
32+
>
33+
{todo.title}
34+
</span>
35+
</updateFetcher.Form>
36+
37+
<deleteFetcher.Form method="DELETE">
38+
<input type="hidden" name="id" value={todo.id} />
39+
<button
40+
type="submit"
41+
className="px-2 py-1 text-sm text-red-600 dark:text-red-400 hover:text-red-800 dark:hover:text-red-300"
42+
>
43+
Delete
44+
</button>
45+
</deleteFetcher.Form>
46+
</div>
47+
)
48+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { PrismaClient } from '@prisma/client'
2+
3+
const globalForPrisma = globalThis as unknown as {
4+
prisma: PrismaClient | undefined
5+
}
6+
7+
export const prisma = globalForPrisma.prisma ?? new PrismaClient()
8+
9+
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = prisma

orm/react-router-7/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+
}

orm/react-router-7/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;

0 commit comments

Comments
 (0)