-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #62 from neondatabase/andrelandgraf/with-react-router
With react-router example
- Loading branch information
Showing
15 changed files
with
383 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
.react-router | ||
build | ||
node_modules | ||
README.md |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
DATABASE_URL="postgresql://neondb_owner:...@ep-...us-east-1.aws.neon.tech/neondb?sslmode=require" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
.DS_Store | ||
/node_modules/ | ||
|
||
# React Router | ||
/.react-router/ | ||
/build/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
FROM node:20-alpine AS development-dependencies-env | ||
COPY . /app | ||
WORKDIR /app | ||
RUN npm ci | ||
|
||
FROM node:20-alpine AS production-dependencies-env | ||
COPY ./package.json package-lock.json /app/ | ||
WORKDIR /app | ||
RUN npm ci --omit=dev | ||
|
||
FROM node:20-alpine AS build-env | ||
COPY . /app/ | ||
COPY --from=development-dependencies-env /app/node_modules /app/node_modules | ||
WORKDIR /app | ||
RUN npm run build | ||
|
||
FROM node:20-alpine | ||
COPY ./package.json package-lock.json /app/ | ||
COPY --from=production-dependencies-env /app/node_modules /app/node_modules | ||
COPY --from=build-env /app/build /app/build | ||
WORKDIR /app | ||
CMD ["npm", "run", "start"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
<img width="250px" src="https://neon.tech/brand/neon-logo-dark-color.svg" /> | ||
|
||
# Getting started with Neon and React Router | ||
|
||
## Clone the repository | ||
|
||
```bash | ||
npx degit neondatabase/examples/with-react-router ./with-react-router | ||
``` | ||
|
||
## Create a .env file | ||
|
||
Run the command below to copy the `.env.example` file: | ||
|
||
```bash | ||
cp .env.example .env | ||
``` | ||
|
||
## Store your Neon credentials | ||
|
||
Store your Neon credentials in the `.env` file. You can obtain the connection string for your database from the Connection Details widget on the Neon Dashboard. | ||
|
||
``` | ||
DATABASE_URL="postgresql://neondb_owner:...@ep-...us-east-1.aws.neon.tech/neondb?sslmode=require" | ||
``` | ||
|
||
- `user` is the database user. | ||
- `password` is the database user’s password. | ||
- `endpoint_hostname` is the host with neon.tech as the [TLD](https://www.cloudflare.com/en-gb/learning/dns/top-level-domain/). | ||
- `dbname` is the name of the database. “neondb” is the default database created with each Neon project. | ||
- `?sslmode=require` an optional query parameter that enforces the [SSL](https://www.cloudflare.com/en-gb/learning/ssl/what-is-ssl/) mode while connecting to the Postgres instance for better security. | ||
|
||
**Important**: To ensure the security of your data, never expose your Neon credentials to the browser. | ||
|
||
## Install dependencies | ||
|
||
Run the command below to install project dependencies: | ||
|
||
```bash | ||
npm install | ||
``` | ||
|
||
## Development | ||
|
||
Run the application using the following command: | ||
|
||
```bash | ||
npm run dev | ||
``` | ||
|
||
Your application will be available at `http://localhost:5173`. | ||
|
||
## Building for Production | ||
|
||
Create a production build: | ||
|
||
```bash | ||
npm run build | ||
``` | ||
|
||
## Deployment | ||
|
||
React Router can be deployed to many different deployment targets including Vercel, Netlify, Cloudflare Workers, and more. Check out [React Router's official templates on GitHub](https://github.com/remix-run/react-router-templates) for more information. This template is based on React Router's default template running a standard Node server. It also ships with example Docker files for dockerized deployments. | ||
|
||
### Docker Deployment | ||
|
||
This template includes three Dockerfiles optimized for different package managers: | ||
|
||
- `Dockerfile` - for npm | ||
- `Dockerfile.pnpm` - for pnpm | ||
- `Dockerfile.bun` - for bun | ||
|
||
To build and run using Docker: | ||
|
||
```bash | ||
# For npm | ||
docker build -t my-app . | ||
|
||
# For pnpm | ||
docker build -f Dockerfile.pnpm -t my-app . | ||
|
||
# For bun | ||
docker build -f Dockerfile.bun -t my-app . | ||
|
||
# Run the container | ||
docker run -p 3000:3000 my-app | ||
``` | ||
|
||
The containerized application can be deployed to any platform that supports Docker, including: | ||
|
||
- AWS ECS | ||
- Google Cloud Run | ||
- Azure Container Apps | ||
- Digital Ocean App Platform | ||
- Fly.io | ||
- Railway | ||
|
||
### DIY Deployment | ||
|
||
If you're familiar with deploying Node applications, the built-in app server is production-ready. | ||
|
||
Make sure to deploy the output of `npm run build` | ||
|
||
``` | ||
├── package.json | ||
├── package-lock.json (or pnpm-lock.yaml, or bun.lockb) | ||
├── build/ | ||
│ ├── client/ # Static assets | ||
│ └── server/ # Server-side code | ||
``` | ||
|
||
## Styling | ||
|
||
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
@import "tailwindcss"; | ||
|
||
@theme { | ||
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif, | ||
"Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; | ||
} | ||
|
||
html, | ||
body { | ||
@apply bg-gray-950; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
import { | ||
isRouteErrorResponse, | ||
Links, | ||
Meta, | ||
Outlet, | ||
Scripts, | ||
ScrollRestoration, | ||
} from "react-router"; | ||
|
||
import type { Route } from "./+types/root"; | ||
import "./app.css"; | ||
|
||
export const links: Route.LinksFunction = () => [ | ||
{ rel: "preconnect", href: "https://fonts.googleapis.com" }, | ||
{ | ||
rel: "preconnect", | ||
href: "https://fonts.gstatic.com", | ||
crossOrigin: "anonymous", | ||
}, | ||
{ | ||
rel: "stylesheet", | ||
href: "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap", | ||
}, | ||
]; | ||
|
||
export function Layout({ children }: { children: React.ReactNode }) { | ||
return ( | ||
<html lang="en"> | ||
<head> | ||
<meta charSet="utf-8" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1" /> | ||
<Meta /> | ||
<Links /> | ||
</head> | ||
<body> | ||
{children} | ||
<ScrollRestoration /> | ||
<Scripts /> | ||
</body> | ||
</html> | ||
); | ||
} | ||
|
||
export default function App() { | ||
return <Outlet />; | ||
} | ||
|
||
export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) { | ||
let message = "Oops!"; | ||
let details = "An unexpected error occurred."; | ||
let stack: string | undefined; | ||
|
||
if (isRouteErrorResponse(error)) { | ||
message = error.status === 404 ? "404" : "Error"; | ||
details = | ||
error.status === 404 | ||
? "The requested page could not be found." | ||
: error.statusText || details; | ||
} else if (import.meta.env.DEV && error && error instanceof Error) { | ||
details = error.message; | ||
stack = error.stack; | ||
} | ||
|
||
return ( | ||
<main className="pt-16 p-4 container mx-auto"> | ||
<h1>{message}</h1> | ||
<p>{details}</p> | ||
{stack && ( | ||
<pre className="w-full p-4 overflow-x-auto"> | ||
<code>{stack}</code> | ||
</pre> | ||
)} | ||
</main> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import { type RouteConfig, index } from "@react-router/dev/routes"; | ||
|
||
export default [index("routes/home.tsx")] satisfies RouteConfig; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { neon } from "@neondatabase/serverless"; | ||
import type { Route } from "./+types/home"; | ||
import { Welcome } from "../welcome/welcome"; | ||
|
||
export function meta({}: Route.MetaArgs) { | ||
return [ | ||
{ title: "Neon with React Router" }, | ||
{ name: "description", content: "Welcome to React Router + Neon!" }, | ||
]; | ||
} | ||
|
||
export async function loader({}: Route.ClientLoaderArgs) { | ||
const sql = neon(`${process.env.DATABASE_URL}`); | ||
const response = await sql`SELECT version()`; | ||
const { version } = response[0]; | ||
return { version }; | ||
} | ||
|
||
export default function Home({ loaderData }: Route.ComponentProps) { | ||
return <Welcome databaseVersion={loaderData.version} />; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
export function Welcome({ databaseVersion }: { databaseVersion: string }) { | ||
return ( | ||
<main className="flex items-center justify-center pt-16 pb-4"> | ||
<div className="flex-1 flex flex-col items-center gap-16 min-h-0"> | ||
<header className="flex flex-col items-center gap-9"> | ||
<div className="w-[500px] max-w-[100vw] p-4"> | ||
<img | ||
src="https://neon.tech/brand/neon-logo-dark-color.svg" | ||
alt="React Router" | ||
className="w-full" | ||
/> | ||
</div> | ||
<p className="text-white font-semibold text-sm"> | ||
Database version: {databaseVersion} | ||
</p> | ||
</header> | ||
<div className="max-w-[300px] w-full space-y-6 px-4"> | ||
<nav className="rounded-3xl border p-4 border-gray-700"> | ||
<ul className="flex flex-col gap-4"> | ||
{resources.map(({ href, text }) => ( | ||
<li key={href}> | ||
<a | ||
className="p-4 leading-normal hover:underline text-blue-500" | ||
href={href} | ||
target="_blank" | ||
rel="noreferrer" | ||
> | ||
{text} | ||
</a> | ||
</li> | ||
))} | ||
</ul> | ||
</nav> | ||
</div> | ||
</div> | ||
</main> | ||
); | ||
} | ||
|
||
const resources = [ | ||
{ | ||
href: "https://reactrouter.com/docs", | ||
text: "React Router Docs", | ||
}, | ||
{ | ||
href: "https://neon.tech/docs/introduction", | ||
text: "Neon Docs", | ||
}, | ||
]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
{ | ||
"type": "module", | ||
"name": "with-react-router", | ||
"scripts": { | ||
"dev": "react-router dev", | ||
"build": "react-router build", | ||
"start": "react-router-serve ./build/server/index.js", | ||
"typecheck": "react-router typegen && tsc" | ||
}, | ||
"dependencies": { | ||
"@neondatabase/serverless": "^0.10.4", | ||
"@react-router/node": "^7.1.4", | ||
"@react-router/serve": "^7.1.4", | ||
"isbot": "^5.1.17", | ||
"react": "^19.0.0", | ||
"react-dom": "^19.0.0", | ||
"react-router": "^7.1.4" | ||
}, | ||
"devDependencies": { | ||
"@react-router/dev": "^7.1.4", | ||
"@tailwindcss/vite": "^4.0.0", | ||
"@types/node": "^20", | ||
"@types/react": "^19.0.1", | ||
"@types/react-dom": "^19.0.1", | ||
"react-router-devtools": "^1.1.0", | ||
"tailwindcss": "^4.0.0", | ||
"typescript": "^5.7.2", | ||
"vite": "^5.4.11", | ||
"vite-tsconfig-paths": "^5.1.4" | ||
} | ||
} |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import type { Config } from "@react-router/dev/config"; | ||
|
||
export default { | ||
// Config options... | ||
// Server-side render by default, to enable SPA mode set this to `false` | ||
ssr: true, | ||
} satisfies Config; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
{ | ||
"include": [ | ||
"**/*", | ||
"**/.server/**/*", | ||
"**/.client/**/*", | ||
".react-router/types/**/*" | ||
], | ||
"compilerOptions": { | ||
"lib": ["DOM", "DOM.Iterable", "ES2022"], | ||
"types": ["node", "vite/client"], | ||
"target": "ES2022", | ||
"module": "ES2022", | ||
"moduleResolution": "bundler", | ||
"jsx": "react-jsx", | ||
"rootDirs": [".", "./.react-router/types"], | ||
"baseUrl": ".", | ||
"paths": { | ||
"~/*": ["./app/*"] | ||
}, | ||
"esModuleInterop": true, | ||
"verbatimModuleSyntax": true, | ||
"noEmit": true, | ||
"resolveJsonModule": true, | ||
"skipLibCheck": true, | ||
"strict": true | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { reactRouter } from "@react-router/dev/vite"; | ||
import tailwindcss from "@tailwindcss/vite"; | ||
import { defineConfig } from "vite"; | ||
import tsconfigPaths from "vite-tsconfig-paths"; | ||
|
||
export default defineConfig({ | ||
plugins: [tailwindcss(), reactRouter(), tsconfigPaths()], | ||
build: { | ||
cssMinify: true, | ||
ssr: false, | ||
}, | ||
}); |