# Next.js (/docs/guides/frameworks/nextjs)

Location: Guides > Frameworks > Next.js

Introduction [#introduction]

This guide shows you how to use Prisma with Next.js, a fullstack React framework. You'll learn how to create a [Prisma Postgres](/postgres) instance, set up Prisma ORM with Next.js, handle migrations, and deploy your application to Vercel.

You can find a [deployment-ready example on GitHub](https://github.com/prisma/prisma-examples/blob/latest/orm/nextjs).

Quick start [#quick-start]

Run one command to scaffold a Next.js project with Prisma ORM and Prisma Postgres ready to go:

  

#### npm

```bash
npm create prisma@latest -- --template next
```

#### pnpm

```bash
pnpm create prisma@latest --template next
```

#### yarn

```bash
yarn create prisma@latest --template next
```

#### bun

```bash
bun create prisma@latest --template next
```

Or follow the steps below to set it up manually.

Prerequisites [#prerequisites]

* [Node.js](https://nodejs.org) v20.19+, v22.12+, or v24.0+
* A Vercel account (if you want to deploy your application)

1. Set up your project [#1-set-up-your-project]

From the directory where you want to create your project, run `create-next-app` to create a new Next.js app that you will be using for this guide.

  

#### npm

```bash
npx create-next-app@latest nextjs-prisma
```

#### pnpm

```bash
pnpm dlx create-next-app@latest nextjs-prisma
```

#### yarn

```bash
yarn dlx create-next-app@latest nextjs-prisma
```

#### bun

```bash
bunx --bun create-next-app@latest nextjs-prisma
```

You will be prompted to answer a few questions about your project. Select all of the defaults.

> [!NOTE]
> For reference, those are:
> 
> * TypeScript
> * ESLint
> * Tailwind CSS
> * No `src` directory
> * App Router
> * Turbopack
> * No customized import alias

Then, navigate to the project directory:

```bash
cd nextjs-prisma
```

2. Install and Configure Prisma [#2-install-and-configure-prisma]

2.1. Install dependencies [#21-install-dependencies]

To get started with Prisma, you'll need to install a few dependencies:

  

#### npm

```bash
npm install prisma tsx @types/pg --save-dev
```

#### pnpm

```bash
pnpm add prisma tsx @types/pg --save-dev
```

#### yarn

```bash
yarn add prisma tsx @types/pg --dev
```

#### bun

```bash
bun add prisma tsx @types/pg --dev
```

  

#### npm

```bash
npm install @prisma/client @prisma/adapter-pg dotenv pg
```

#### pnpm

```bash
pnpm add @prisma/client @prisma/adapter-pg dotenv pg
```

#### yarn

```bash
yarn add @prisma/client @prisma/adapter-pg dotenv pg
```

#### bun

```bash
bun add @prisma/client @prisma/adapter-pg dotenv pg
```

> [!NOTE]
> If you are using a different database provider (MySQL, SQL Server, SQLite), install the corresponding driver adapter package instead of `@prisma/adapter-pg`. For more information, see [Database drivers](/orm/core-concepts/supported-databases/database-drivers).

Once installed, initialize Prisma in your project:

  

#### npm

```bash
npx prisma init --output ../app/generated/prisma
```

#### pnpm

```bash
pnpm dlx prisma init --output ../app/generated/prisma
```

#### yarn

```bash
yarn dlx prisma init --output ../app/generated/prisma
```

#### bun

```bash
bunx --bun prisma init --output ../app/generated/prisma
```

> [!NOTE]
> `prisma init` creates the Prisma scaffolding and a local `DATABASE_URL`. In the next step, you will create a Prisma Postgres database and replace that value with a direct `postgres://...` connection string.

This will create:

* A `prisma` directory with a `schema.prisma` file.
* A `prisma.config.ts` file for configuring Prisma.
* A `.env` file containing a local `DATABASE_URL` at the project root.

The `app/generated/prisma` output directory for the generated Prisma Client will be created when you run `prisma generate` or `prisma migrate dev` in a later step.

Create a Prisma Postgres database and replace the generated `DATABASE_URL` in your `.env` file with the `postgres://...` connection string from the CLI output:

  

#### npm

```bash
npx create-db
```

#### pnpm

```bash
pnpm dlx create-db
```

#### yarn

```bash
yarn dlx create-db
```

#### bun

```bash
bunx --bun create-db
```

2.2. Define your Prisma Schema [#22-define-your-prisma-schema]

In the `prisma/schema.prisma` file, add the following models:

```prisma title="prisma/schema.prisma"
generator client {
  provider = "prisma-client"
  output   = "../app/generated/prisma"
}

datasource db {
  provider = "postgresql"
}

model User { // [!code ++]
  id    Int     @id @default(autoincrement()) // [!code ++]
  email String  @unique // [!code ++]
  name  String? // [!code ++]
  posts Post[] // [!code ++]
} // [!code ++]
 // [!code ++]
model Post { // [!code ++]
  id        Int     @id @default(autoincrement()) // [!code ++]
  title     String // [!code ++]
  content   String? // [!code ++]
  published Boolean @default(false) // [!code ++]
  authorId  Int // [!code ++]
  author    User    @relation(fields: [authorId], references: [id]) // [!code ++]
} // [!code ++]
```

This creates two models: `User` and `Post`, with a one-to-many relationship between them.

2.3 Add dotenv to prisma.config.ts [#23-add-dotenv-to-prismaconfigts]

To get access to the variables in the `.env` file, they can either be loaded by your runtime, or by using `dotenv`.
Include an import for `dotenv` at the top of the `prisma.config.ts`

```typescript title="prisma.config.ts"
import "dotenv/config"; // [!code ++]
import { defineConfig, env } from "prisma/config";

export default defineConfig({
  schema: "prisma/schema.prisma",
  migrations: {
    path: "prisma/migrations",
  },
  datasource: {
    url: env("DATABASE_URL"),
  },
});
```

2.4. Run migrations and generate Prisma Client [#24-run-migrations-and-generate-prisma-client]

Now, run the following command to create the database tables:

  

#### npm

```bash
npx prisma migrate dev --name init
```

#### pnpm

```bash
pnpm dlx prisma migrate dev --name init
```

#### yarn

```bash
yarn dlx prisma migrate dev --name init
```

#### bun

```bash
bunx --bun prisma migrate dev --name init
```

Then generate Prisma Client:

  

#### npm

```bash
npx prisma generate
```

#### pnpm

```bash
pnpm dlx prisma generate
```

#### yarn

```bash
yarn dlx prisma generate
```

#### bun

```bash
bunx --bun prisma generate
```

2.5. Seed the database [#25-seed-the-database]

Add some seed data to populate the database with sample users and posts.

Create a new file called `seed.ts` in the `prisma/` directory:

```typescript title="prisma/seed.ts"
import { PrismaClient, Prisma } from "../app/generated/prisma/client";
import { PrismaPg } from "@prisma/adapter-pg";
import "dotenv/config";

const adapter = new PrismaPg({
  connectionString: process.env.DATABASE_URL,
});

const prisma = new PrismaClient({
  adapter,
});

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 `prisma.config.ts`:

```typescript title="prisma.config.ts"
import "dotenv/config";
import { defineConfig, env } from "prisma/config";

export default defineConfig({
  schema: "prisma/schema.prisma",
  migrations: {
    path: "prisma/migrations",
    seed: `tsx prisma/seed.ts`, // [!code ++]
  },
  datasource: {
    url: env("DATABASE_URL"),
  },
});
```

Finally, run `prisma db seed` to seed your database with the initial data we defined in the `seed.ts` file.

Run the seed script:

  

#### npm

```bash
npx prisma db seed
```

#### pnpm

```bash
pnpm dlx prisma db seed
```

#### yarn

```bash
yarn dlx prisma db seed
```

#### bun

```bash
bunx --bun prisma db seed
```

And open Prisma Studio to inspect your data:

  

#### npm

```bash
npx prisma studio
```

#### pnpm

```bash
pnpm dlx prisma studio
```

#### yarn

```bash
yarn dlx prisma studio
```

#### bun

```bash
bunx --bun prisma studio
```

2.6 Set up Prisma Client [#26-set-up-prisma-client]

Now that you have a database with some initial data, you can set up Prisma Client and connect it to your database.

At the root of your project, create a new `lib` directory and add a `prisma.ts` file to it.

```bash
mkdir -p lib && touch lib/prisma.ts
```

Now, add the following code to your `lib/prisma.ts` file:

```typescript title="lib/prisma.ts" showLineNumbers
import { PrismaClient } from "../app/generated/prisma/client"; // [!code ++]
import { PrismaPg } from "@prisma/adapter-pg"; // [!code ++]
// [!code ++]
const globalForPrisma = global as unknown as {
  // [!code ++]
  prisma: PrismaClient; // [!code ++]
}; // [!code ++]
// [!code ++]
const adapter = new PrismaPg({
  // [!code ++]
  connectionString: process.env.DATABASE_URL, // [!code ++]
}); // [!code ++]
// [!code ++]
const prisma =
  globalForPrisma.prisma ||
  new PrismaClient({
    // [!code ++]
    adapter, // [!code ++]
  }); // [!code ++]
// [!code ++]
if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma; // [!code ++]
// [!code ++]
export default prisma; // [!code ++]
```

This file creates a Prisma Client and attaches it to the global object so that only one instance of the client is created in your application. This helps resolve issues with hot reloading that can occur when using Prisma ORM with Next.js in development mode.

You'll use this client in the next section to run your first queries.

3. Query your database with Prisma ORM [#3-query-your-database-with-prisma-orm]

Now that you have an initialized Prisma Client, a connection to your database, and some initial data, you can start querying your data with Prisma ORM.

In this example, you'll make the "home" page of your application display all of your users.

Open the `app/page.tsx` file and replace the existing code with the following:

```tsx title="app/page.tsx"
export default async function Home() {
  return (
    <div className="min-h-screen bg-gray-50 flex flex-col items-center justify-center -mt-16">
      <h1 className="text-4xl font-bold mb-8 font-[family-name:var(--font-geist-sans)] text-[#333333]">
        Superblog
      </h1>
      <ol className="list-decimal list-inside font-[family-name:var(--font-geist-sans)]">
        <li className="mb-2">Alice</li>
        <li>Bob</li>
      </ol>
    </div>
  );
}
```

This gives you a basic page with a title and a list of users. However, that list is static with hardcoded values. Let's update the page to fetch the users from your database and make it dynamic.

```tsx title="app/page.tsx"
import prisma from "@/lib/prisma";

export default async function Home() {
  const users = await prisma.user.findMany();
  return (
    <div className="min-h-screen bg-gray-50 flex flex-col items-center justify-center -mt-16">
      <h1 className="text-4xl font-bold mb-8 font-[family-name:var(--font-geist-sans)] text-[#333333]">
        Superblog
      </h1>
      <ol className="list-decimal list-inside font-[family-name:var(--font-geist-sans)]">
        {users.map((user) => (
          <li key={user.id} className="mb-2">
            {user.name}
          </li>
        ))}
      </ol>
    </div>
  );
}
```

You are now importing your client, querying the `User` model for all users, and then displaying them in a list.

Now your home page is dynamic and will display the users from your database.

3.1 Update your data (optional) [#31-update-your-data-optional]

If you want to see what happens when data is updated, you could:

* update your `User` table via a SQL browser of your choice
* change your `seed.ts` file to add more users
* change the call to `prisma.user.findMany` to re-order the users, filter the users, or similar.

Just reload the page and you'll see the changes.

4. Add a new Posts list page [#4-add-a-new-posts-list-page]

You have your home page working, but you should add a new page that displays all of your posts.

First create a new `posts` directory in the `app` directory and create a new `page.tsx` file inside of it.

```bash
mkdir -p app/posts && touch app/posts/page.tsx
```

Second, add the following code to the `app/posts/page.tsx` file:

```tsx title="app/posts/page.tsx"
import prisma from "@/lib/prisma";

export default async function Posts() {
  return (
    <div className="min-h-screen bg-gray-50 flex flex-col items-center justify-center -mt-16 text-[#333333]">
      <h1 className="text-4xl font-bold mb-8 font-[family-name:var(--font-geist-sans)]">Posts</h1>
      <ul className="font-[family-name:var(--font-geist-sans)] max-w-2xl space-y-4">
        <li>My first post</li>
      </ul>
    </div>
  );
}
```

Now `localhost:3000/posts` will load, but the content is hardcoded again. Let's update it to be dynamic, similarly to the home page:

```tsx title="app/posts/page.tsx"
import prisma from "@/lib/prisma";

export default async function Posts() {
  const posts = await prisma.post.findMany({
    include: {
      author: true,
    },
  });

  return (
    <div className="min-h-screen bg-gray-50 flex flex-col items-center justify-center -mt-16 text-[#333333]">
      <h1 className="text-4xl font-bold mb-8 font-[family-name:var(--font-geist-sans)]">Posts</h1>
      <ul className="font-[family-name:var(--font-geist-sans)] max-w-2xl space-y-4">
        {posts.map((post) => (
          <li key={post.id}>
            <span className="font-semibold">{post.title}</span>
            <span className="text-sm text-gray-600 ml-2">by {post.author.name}</span>
          </li>
        ))}
      </ul>
    </div>
  );
}
```

This works similarly to the home page, but instead of displaying users, it displays posts. You can also see that you've used `include` in your Prisma Client query to fetch the author of each post so you can display the author's name.

This "list view" is one of the most common patterns in web applications. You're going to add two more pages to your application which you'll also commonly need: a "detail view" and a "create view".

5. Add a new Posts detail page [#5-add-a-new-posts-detail-page]

To complement the Posts list page, you'll add a Posts detail page.

In the `posts` directory, create a new `[id]` directory and a new `page.tsx` file inside of that.

```bash
mkdir -p "app/posts/[id]" && touch "app/posts/[id]/page.tsx"
```

This page will display a single post's title, content, and author. Just like your other pages, add the following code to the `app/posts/[id]/page.tsx` file:

```tsx title="app/posts/[id]/page.tsx"
import prisma from "@/lib/prisma";

export default async function Post({ params }: { params: Promise<{ id: string }> }) {
  return (
    <div className="min-h-screen bg-gray-50 flex flex-col items-center justify-center -mt-16">
      <article className="max-w-2xl space-y-4 font-[family-name:var(--font-geist-sans)]">
        <h1 className="text-4xl font-bold mb-8 text-[#333333]">My first post</h1>
        <p className="text-gray-600 text-center">by Anonymous</p>
        <div className="prose prose-gray mt-8">No content available.</div>
      </article>
    </div>
  );
}
```

As before, this page is static with hardcoded content. Let's update it to be dynamic based on the `params` passed to the page:

```tsx title="app/posts/[id]/page.tsx"
import prisma from "@/lib/prisma";
import { notFound } from "next/navigation";

export default async function Post({ params }: { params: Promise<{ id: string }> }) {
  const { id } = await params;
  const post = await prisma.post.findUnique({
    where: { id: parseInt(id) },
    include: {
      author: true,
    },
  });

  if (!post) {
    notFound();
  }

  return (
    <div className="min-h-screen bg-gray-50 flex flex-col items-center justify-center -mt-16">
      <article className="max-w-2xl space-y-4 font-[family-name:var(--font-geist-sans)]">
        <h1 className="text-4xl font-bold mb-8 text-[#333333]">{post.title}</h1>
        <p className="text-gray-600 text-center">by {post.author.name}</p>
        <div className="prose prose-gray mt-8">{post.content || "No content available."}</div>
      </article>
    </div>
  );
}
```

There's a lot of changes here, so let's break it down:

* You're using Prisma Client to fetch the post by its `id`, which you get from the `params` object.
* In case the post doesn't exist (maybe it was deleted or maybe you typed a wrong ID), you call `notFound()` to display a 404 page.
* You then display the post's title, content, and author. If the post doesn't have content, you display a placeholder message.

It's not the prettiest page, but it's a good start. Try it out by navigating to `localhost:3000/posts/1` and `localhost:3000/posts/2`. You can also test the 404 page by navigating to `localhost:3000/posts/999`.

6. Add a new Posts create page [#6-add-a-new-posts-create-page]

To round out your application, you'll add a "create" page for posts. This will let you write your own posts and save them to the database.

As with the other pages, you'll start with a static page and then update it to be dynamic.

```bash
mkdir -p app/posts/new && touch app/posts/new/page.tsx
```

Now, add the following code to the `app/posts/new/page.tsx` file:

```tsx title="app/posts/new/page.tsx"
import Form from "next/form";

export default function NewPost() {
  async function createPost(formData: FormData) {
    "use server";

    const title = formData.get("title") as string;
    const content = formData.get("content") as string;
  }

  return (
    <div className="max-w-2xl mx-auto p-4">
      <h1 className="text-2xl font-bold mb-6">Create New Post</h1>
      <Form action={createPost} className="space-y-6">
        <div>
          <label htmlFor="title" className="block text-lg mb-2">
            Title
          </label>
          <input
            type="text"
            id="title"
            name="title"
            placeholder="Enter your post title"
            className="w-full px-4 py-2 border rounded-lg"
          />
        </div>
        <div>
          <label htmlFor="content" className="block text-lg mb-2">
            Content
          </label>
          <textarea
            id="content"
            name="content"
            placeholder="Write your post content here..."
            rows={6}
            className="w-full px-4 py-2 border rounded-lg"
          />
        </div>
        <button
          type="submit"
          className="w-full bg-blue-500 text-white py-3 rounded-lg hover:bg-blue-600"
        >
          Create Post
        </button>
      </Form>
    </div>
  );
}
```

This form looks good, but it doesn't do anything yet. Let's update the `createPost` function to save the post to the database:

```tsx title="app/posts/new/page.tsx"
import Form from "next/form";
import prisma from "@/lib/prisma";
import { revalidatePath } from "next/cache";
import { redirect } from "next/navigation";

export default function NewPost() {
  async function createPost(formData: FormData) {
    "use server";

    const title = formData.get("title") as string;
    const content = formData.get("content") as string;

    await prisma.post.create({
      data: {
        title,
        content,
        authorId: 1,
      },
    });

    revalidatePath("/posts");
    redirect("/posts");
  }

  return (
    <div className="max-w-2xl mx-auto p-4">
      <h1 className="text-2xl font-bold mb-6">Create New Post</h1>
      <Form action={createPost} className="space-y-6">
        <div>
          <label htmlFor="title" className="block text-lg mb-2">
            Title
          </label>
          <input
            type="text"
            id="title"
            name="title"
            placeholder="Enter your post title"
            className="w-full px-4 py-2 border rounded-lg"
          />
        </div>
        <div>
          <label htmlFor="content" className="block text-lg mb-2">
            Content
          </label>
          <textarea
            id="content"
            name="content"
            placeholder="Write your post content here..."
            rows={6}
            className="w-full px-4 py-2 border rounded-lg"
          />
        </div>
        <button
          type="submit"
          className="w-full bg-blue-500 text-white py-3 rounded-lg hover:bg-blue-600"
        >
          Create Post
        </button>
      </Form>
    </div>
  );
}
```

This page now has a functional form! When you submit the form, it will create a new post in the database and redirect you to the posts list page.

You also added a `revalidatePath` call to revalidate the posts list page so that it will be updated with the new post. That way everyone can read the new post immediately.

Try it out by navigating to `localhost:3000/posts/new` and submitting the form.

7. Deploy your application to Vercel (Optional) [#7-deploy-your-application-to-vercel-optional]

The quickest way to deploy your application to Vercel is to use the [Vercel CLI](https://vercel.com/docs/cli).

First, install the Vercel CLI:

  

#### npm

```bash
npm install -g vercel
```

#### pnpm

```bash
pnpm add -g vercel
```

#### yarn

```bash
yarn global add vercel
```

#### bun

```bash
bun add --global vercel
```

Then, run `vercel login` to log in to your Vercel account.

```bash
vercel login
```

Before you deploy, you also need to tell Vercel to make sure that the Prisma Client is generated. You can do this by adding a `postinstall` script to your `package.json` file.

```json title="package.json"
{
  "name": "nextjs-prisma",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev --turbopack",
    "build": "next build",
    "postinstall": "prisma generate",
    "start": "next start",
    "lint": "next lint"
  },
  "prisma": {
    "seed": "tsx prisma/seed.ts"
  },
  "dependencies": {
    "@prisma/adapter-pg": "^6.2.1",
    "@prisma/client": "^6.2.1",
    "next": "15.1.4",
    "pg": "^8.13.1",
    "react": "^19.0.0",
    "react-dom": "^19.0.0"
  },
  "devDependencies": {
    "@eslint/eslintrc": "^3",
    "@types/node": "^20",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "eslint": "^9",
    "eslint-config-next": "15.1.4",
    "postcss": "^8",
    "prisma": "^6.2.1",
    "tailwindcss": "^3.4.1",
    "tsx": "^4.19.2",
    "typescript": "^5"
  }
}
```

After this change, you can deploy your application to Vercel by running `vercel`.

```bash
vercel
```

After the deployment is complete, you can visit your application at the URL that Vercel provides. Congratulations, you've just deployed a Next.js application with Prisma ORM!

8. Next steps [#8-next-steps]

Now that you have a working Next.js application with Prisma ORM, here are some ways you can expand and improve your application:

* Add authentication to protect your routes
* Add the ability to edit and delete posts
* Add comments to posts
* Use [Prisma Studio](/studio) for visual database management

For more information:

* [Prisma ORM documentation](/orm)
* [Prisma Client API reference](/orm/prisma-client/setup-and-configuration/introduction)
* [Next.js documentation](https://nextjs.org/docs)

## Related pages

- [`Astro`](https://www.prisma.io/docs/guides/frameworks/astro): Learn how to use Prisma ORM in an Astro app
- [`Elysia`](https://www.prisma.io/docs/guides/frameworks/elysia): Learn how to use Prisma ORM in an Elysia app
- [`Hono`](https://www.prisma.io/docs/guides/frameworks/hono): Learn how to use Prisma ORM in a Hono app
- [`NestJS`](https://www.prisma.io/docs/guides/frameworks/nestjs): Learn how to use Prisma ORM in a NestJS app
- [`Nuxt`](https://www.prisma.io/docs/guides/frameworks/nuxt): A step-by-step guide to setting up and using Prisma ORM and Prisma Postgres in a Nuxt app