How to use Prisma ORM with TanStack Start
Introduction
Prisma ORM simplifies database interactions, and TanStack Start offers a robust framework for building modern React applications. Together with Prisma Postgres, they provide a seamless full-stack development experience with type-safe queries and efficient data management.
This guide will walk you through integrating Prisma ORM with a Prisma Postgres database in a TanStack Start project from scratch.
Prerequisites
1. Set up your project
To begin, create a new TanStack Start project.
For the purpose of this guide, we're using the same setup instructions that you can find in the TanStart Start docs.
In the directory where you'd like to create your project, run the following commands:
mkdir tanstack-start-prisma
cd tanstack-start-prisma
npm init -y
This will create a new folder called tanstack-start-prisma
, navigate into it, and initialize a new Node.js project.
Open the directory in your IDE and create a tsconfig.json
file with the following configuration:
{
"compilerOptions": {
"jsx": "react-jsx",
"moduleResolution": "Bundler",
"module": "ESNext",
"target": "ES2022",
"skipLibCheck": true,
"strictNullChecks": true
}
}
We also need a .gitignore
file, so let's set that up now:
node_modules
.env
app/generated
Next, install TanStack Router and Vinxi, as TanStack Start currently requires them:
npm install @tanstack/react-start @tanstack/react-router vinxi
We also need React, the Vite React plugin, and TypeScript:
npm install react react-dom
npm install --save-dev @vitejs/plugin-react vite-tsconfig-paths
npm install --save-dev typescript @types/react @types/react-dom
Update your package.json
to use Vinxi's CLI. Add "type": "module"
and modify the scripts to use Vinxi's CLI:
{
"name": "tanstack-start-prisma",
"version": "1.0.0",
"main": "index.js",
"type": "module",
"scripts": {
"dev": "vinxi dev",
"build": "vinxi build",
"start": "vinxi start"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"@tanstack/react-router": "^1.119.0",
"@tanstack/react-start": "^1.119.0",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"vinxi": "^0.5.6"
},
"devDependencies": {
"@types/react": "^19.1.2",
"@types/react-dom": "^19.1.3",
"@vitejs/plugin-react": "^4.4.1",
"typescript": "^5.8.3",
"vite-tsconfig-paths": "^5.1.4"
}
}
Then, create and configure TanStack Start's app.config.ts
file:
import { defineConfig } from '@tanstack/react-start/config'
import tsConfigPaths from 'vite-tsconfig-paths'
export default defineConfig({
vite: {
plugins: [
tsConfigPaths({
projects: ['./tsconfig.json'],
}),
],
},
})
For TanStack Start to function, we need 5 files in ~/app/
:
router.tsx
(The router configuration)ssr.tsx
(The server entry point)client.tsx
(The client entry point)routes/__root.tsx
(The root of the app)routes/index.tsx
(The home page)
You can create them with these commands:
mkdir app
touch app/router.tsx
touch app/ssr.tsx
touch app/client.tsx
mkdir app/routes
touch app/routes/__root.tsx
touch app/routes/index.tsx
router.tsx
configures the application's main router with route definitions and settings:
import { createRouter as createTanStackRouter } from '@tanstack/react-router'
import { routeTree } from './routeTree.gen'
export function createRouter() {
const router = createTanStackRouter({
routeTree,
scrollRestoration: true,
})
return router
}
declare module '@tanstack/react-router' {
interface Register {
router: ReturnType<typeof createRouter>
}
}
You should be seeing an error about routeTree.gen.ts
not existing. This is expected. It will be generated when you run TanStack Start for the first time.
ssr.tsx
allows us to know what routes and loaders we need to execute when the user hits a given route:
import {
createStartHandler,
defaultStreamHandler,
} from '@tanstack/react-start/server'
import { getRouterManifest } from '@tanstack/react-start/router-manifest'
import { createRouter } from './router'
export default createStartHandler({
createRouter,
getRouterManifest,
})(defaultStreamHandler)
client.tsx
initializes the client-side logic to handle routes in the browser:
import { hydrateRoot } from "react-dom/client";
import { StartClient } from "@tanstack/react-start/client";
import { createRouter } from "./router";
const router = createRouter();
hydrateRoot(document, <StartClient router={router} />);
routes/__root.tsx
defines the root route and global HTML layout for the entire application:
import type { ReactNode } from "react";
import {
Outlet,
createRootRoute,
HeadContent,
Scripts,
} from "@tanstack/react-router";
export const Route = createRootRoute({
head: () => ({
meta: [
{
charSet: "utf-8",
},
{
name: "viewport",
content: "width=device-width, initial-scale=1",
},
{
title: "Prisma TanStack Start Demo",
},
],
}),
component: RootComponent,
});
function RootComponent() {
return (
<RootDocument>
<Outlet />
</RootDocument>
);
}
function RootDocument({ children }: Readonly<{ children: ReactNode }>) {
return (
<html>
<head>
<HeadContent />
</head>
<body>
{children}
<Scripts />
</body>
</html>
);
}
routes/index.tsx
is the home page of the application:
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/")({
component: Home,
});
function Home() {
return (
<div>
<h1>Posts</h1>
</div>
);
}
Now, run:
npm run dev
This will generate the routeTree.gen.ts
file and resolve any routing errors.
Your file tree should look like this (without node_modules
):
.
├── app
│ ├── client.tsx
│ ├── routeTree.gen.ts
│ ├── router.tsx
│ ├── routes
│ │ ├── __root.tsx
│ │ └── index.tsx
│ └── ssr.tsx
├── app.config.ts
├── package-lock.json
├── package.json
└── tsconfig.json
2. Install and Configure Prisma
2.1. Install dependencies
To get started with Prisma, you'll need to install a few dependencies:
- Prisma Postgres (recommended)
- Other databases
npm install prisma tsx --save-dev
npm install @prisma/extension-accelerate @prisma/client
npm install prisma tsx --save-dev
npm install @prisma/client
Once installed, initialize Prisma in your project:
npx prisma init --db --output ../app/generated/prisma
You'll need to answer a few questions while setting up your Prisma Postgres database. Select the region closest to your location and a memorable name for your database like "My __________ Project"
This will create:
- A
prisma
directory with aschema.prisma
file. - A Prisma Postgres database.
- A
.env
file containing theDATABASE_URL
at the project root. - An
output
directory for the generated Prisma Client asapp/generated/prisma
.
2.2. Define your Prisma Schema
In schema.prisma
, create a model for our posts and change the generator to use the prisma-client
provider:
generator client {
provider = "prisma-client"
output = "../app/generated/prisma"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model User {
id Int @id @default(autoincrement())
email String @unique
name String?
posts Post[]
}
model Post {
id Int @id @default(autoincrement())
title String
content String?
published Boolean @default(false)
authorId Int
author User @relation(fields: [authorId], references: [id])
}
This creates two models: User
and Post
, with a one-to-many relationship between them.
2.3. Configure the Prisma Client generator
Now, run the following command to create the database tables and generate the Prisma Client:
npx prisma migrate dev --name init
2.4. Seed the database
Let's add some seed data to populate the database with sample users and posts.
Create a new file called seed.ts
in the prisma/
directory:
import { PrismaClient, Prisma } from "../src/generated/prisma";
const prisma = new PrismaClient();
const userData: Prisma.UserCreateInput[] = [
{
name: "Alice",
email: "alice@prisma.io",
posts: {
create: [
{
title: "Join the Prisma Discord",
content: "https://pris.ly/discord",
published: true,
},
{
title: "Prisma on YouTube",
content: "https://pris.ly/youtube",
},
],
},
},
{
name: "Bob",
email: "bob@prisma.io",
posts: {
create: [
{
title: "Follow Prisma on Twitter",
content: "https://www.twitter.com/prisma",
published: true,
},
],
},
},
];
export async function main() {
for (const u of userData) {
await prisma.user.create({ data: u });
}
}
main();
Now, tell Prisma how to run this script by updating your package.json
:
"prisma": {
"seed": "tsx prisma/seed.ts"
}
Run the seed script:
npx prisma db seed
And open Prisma Studio to inspect your data:
npx prisma studio
3. Integrate Prisma into TanStack Start
3.1 Create a Prisma Client
Instead of creating a new Prisma Client instance in each file, create a single instance in a shared file to be used globally.
Create a /lib
directory and a prisma.ts
file inside it. This file will be used to create and export your Prisma Client instance.
Set up the Prisma client like this:
- Prisma Postgres (recommended)
- Other databases
import { PrismaClient } from "../generated/prisma";
import { withAccelerate } from "@prisma/extension-accelerate";
const prisma = new PrismaClient().$extends(withAccelerate());
export default prisma;
import { PrismaClient } from "../generated/prisma";
const prisma = new PrismaClient();
export default prisma;
We recommend using a connection pooler (like Prisma Accelerate) to manage database connections efficiently.
If you choose not to use one, avoid instantiating PrismaClient
globally in long-lived environments. Instead, create and dispose of the client per request to prevent exhausting your database connections.
3.2 Fetch users and posts on load
First, import the necessary modules. Then, create a server function using the createServerFn
function. This function will fetch the users from the database using the .findMany()
method. Use the include
option to fetch the related posts:
import { prisma } from "../lib/prisma";
import { createServerFn } from "@tanstack/react-start";
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/")({
component: Home,
});
const getUsers = createServerFn({ method: "GET" }).handler(async () => {
return prisma.user.findMany({
include: {
posts: true,
},
});
});
function Home() {
return (
<div>
<h1>Posts</h1>
</div>
);
}
TanStack Start allows functions to run on load with loader functions in the createFileRoute
function. Fetch the users and their posts on load with this code:
import { prisma } from "../lib/prisma";
import { createServerFn } from "@tanstack/react-start";
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/")({
component: Home,
loader: () => {
return getUsers();
},
});
const getUsers = createServerFn({ method: "GET" }).handler(async () => {
return prisma.user.findMany({
include: {
posts: true,
},
});
});
function Home() {
return (
<div>
<h1>Posts</h1>
</div>
);
}
Store the response from the loader in the main component using Route.useLoaderData()
:
import { prisma } from "../lib/prisma";
import { createServerFn } from "@tanstack/react-start";
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/")({
component: Home,
loader: () => {
return getUsers();
},
});
const getUsers = createServerFn({ method: "GET" }).handler(async () => {
return prisma.user.findMany({
include: {
posts: true,
},
});
});
function Home() {
const users = Route.useLoaderData();
return (
<div>
<h1>Posts</h1>
</div>
);
}
3.3 Display the users and posts
Next, you'll update the home page to display the users and posts retrieved from your database.
Map over the users
and display them in a list along with their posts
:
import { createFileRoute } from "@tanstack/react-router";
import { createServerFn } from "@tanstack/react-start";
import prisma from "../../lib/prisma";
export const Route = createFileRoute("/")({
component: Home,
loader: () => {
return getUsers();
},
});
const getUsers = createServerFn({ method: "GET" }).handler(async () => {
return prisma.user.findMany({
include: {
posts: true,
},
});
});
function Home() {
const users = Route.useLoaderData();
return (
<div>
<h1>Posts</h1>
<ul>
{users.map((user) => (
<li key={user.id}>
{user.name}
<ul>
{user.posts.map((post) => (
<li key={post.id}>{post.title}</li>
))}
</ul>
</li>
))}
</ul>
</div>
);
}
This setup will display the posts on your page, fetched directly from your database.
Next steps
You've successfully integrated Prisma ORM with TanStack Start, creating a seamless full-stack application. Here are a few suggestions for what you can do next:
- Expand your Prisma models to handle more complex data relationships.
- Implement additional CRUD operations to enhance your application's functionality.
- Explore more features of Prisma and TanStack Start to deepen your understanding.
- Check out Prisma Postgres to see how you can scale your application.
More info
Stay connected with Prisma
Continue your Prisma journey by connecting with our active community. Stay informed, get involved, and collaborate with other developers:
- Follow us on X for announcements, live events and useful tips.
- Join our Discord to ask questions, talk to the community, and get active support through conversations.
- Subscribe on YouTube for tutorials, demos, and streams.
- Engage on GitHub by starring the repository, reporting issues, or contributing to an issue.