# Payload CMS + Next.js, Done Right

*Originally published at* [*techpotions.com*](https://techpotions.com/lab/how-to-use-payload-cms-nextjs)*.*

If you want a backend that feels like you're still writing React but gives you a full admin panel, authentication, and a database API out of the box, you **use Payload CMS inside your Next.js app**. That's the whole pitch. Not as a separate service. Not connected via HTTP from another VPS. It runs in your own `/app` folder. This guide gets you from `npx create-payload-app` to a working admin UI, with the database decisions and deployment traps I wish someone had flagged for me when I first paired the two.

## How to use Payload CMS inside an existing Next.js project

The official scaffold is the safest starting point, even when you already have a Next.js app. Run the create command *inside* your project root, and it negotiates the merge.

```shell
npx create-payload-app@latest
```

The CLI asks three questions that matter:

1.  **Name / folder**: accept the current directory name.
    
2.  **Add to existing Next.js app?**: select "Yes". Payload will splice itself into your project without overwriting your pages.
    
3.  **Database**: pick `MongoDB`, local or Atlas, or `Postgres`, with Drizzle ORM. I lean toward Postgres for relational data because Payload 3.0's Drizzle adapter treats it as a first-class citizen.
    

After the CLI finishes, you'll see new directories:

*   `payload/`, the CMS config, collections, and any custom components.
    
*   `payload-types.ts`, auto-generated TypeScript types mirroring your collection schemas. This ends arguments about the shape of API responses.
    

The installer also does something subtle: it updates your `tsconfig.json` paths alias so `@payload-config` points to your CMS entry. That single alias is why imports stay clean across server and client boundaries.

### Manual installation when you want full control

If the merge wizard makes you nervous, install by hand. The [Payload docs](https://payloadcms.com/docs/getting-started/installation#manual-installation) list the steps, but I'll compress them to what actually matters:

```shell
npm install payload @payloadcms/next @payloadcms/db-mongodb \
  @payloadcms/richtext-lexical sharp
```

Then create `payload.config.ts` at the project root:

```typescript
import { buildConfig } from 'payload'
import { mongooseAdapter } from '@payloadcms/db-mongodb'
import { lexicalEditor } from '@payloadcms/richtext-lexical'

export default buildConfig({
  collections: [],
  globals: [],
  editor: lexicalEditor(),
  db: mongooseAdapter({
    url: process.env.DATABASE_URI || 'mongodb://localhost/payload',
  }),
  secret: process.env.PAYLOAD_SECRET || 'dev-secret-never-use-in-prod',
})
```

Two files wire it into Next.js.

`app/(payload)/admin/[[...slug]]/page.tsx`:

```tsx
import { RootPage } from '@payloadcms/next/views'
import config from '@payload-config'

const Page = () => <RootPage config={config} />
export default Page
```

`app/(payload)/api/[[...slug]]/route.ts`:

```typescript
import { REST } from '@payloadcms/next/routes'
import config from '@payload-config'

export const { GET, POST, PATCH, DELETE } = REST(config)
```

That's it. `next dev` now serves your admin UI at `/admin` and a REST, plus GraphQL, API at `/api`. No separate server process, no second port.

## Pick the database that matches your hosting plan

Payload needs a persistent database, and this decision affects whether you can deploy on Vercel at all.

| Database | Adapter Package | Best Fit |
| --- | --- | --- |
| MongoDB | `@payloadcms/db-mongodb` | Quick prototyping, existing Atlas cluster |
| Postgres, Drizzle | `@payloadcms/db-postgres` | Relational data, SQL shops, PlanetScale-compatible migrations |
| SQLite, Turso/LibSQL | `@payloadcms/db-sqlite` | Edge-light projects, embedded apps |

Vercel serverless functions disconnect from the database between cold starts, which means MongoDB Atlas connection pooling works fine, but self-hosted Postgres on a VPS from [our web services stack](https://techpotions.com/services/web) gives you fewer timeout surprises. If you deploy anywhere outside Vercel, a Docker container on Hetzner, a Railway instance, or Fly.io, every database above works without extra gymnastics.

## Build your first collection, and why the admin panel reacts like a component

Payload collections are the equivalent of a database table plus its admin UI. Define one in `payload/collections/Posts.ts`:

```typescript
import { CollectionConfig } from 'payload'

export const Posts: CollectionConfig = {
  slug: 'posts',
  fields: [
    { name: 'title', type: 'text', required: true },
    { name: 'content', type: 'richText' },
    { name: 'publishedDate', type: 'date', admin: { position: 'sidebar' } },
  ],
}
```

Register it in `payload.config.ts` inside the `collections` array, and the admin panel instantly shows a new sidebar entry labeled "Posts." This isn't a separate build step, Payload reads your config at runtime and renders the React admin UI directly. That means any custom field component you write is just a React component, not a plugin or extension in a foreign templating language.

When you create a post in the admin panel, data flows to your database. Retrieve it from any server component or route handler:

```typescript
import config from '@payload-config'
import { getPayload } from 'payload'

const payload = await getPayload({ config })
const posts = await payload.find({ collection: 'posts', limit: 10 })
```

The `payload-types.ts` file generated alongside your config ensures `posts.docs` is typed down to the field level. No more guessing whether `publishedDate` is a string or a Date object.

## Keep Payload fast when you self-host

A [recent self-hosted deployment walkthrough](https://www.buildwithmatija.com/blog/deploy-payload-cms-nextjs-16-self-hosted) identifies a sharp edge: Payload and Next.js share a Node process, so the CMS admin panel and your public frontend compete for resources under heavy traffic. When self-hosting, do three things:

1.  **Set** `sharp` **as the image processor.** Payload falls back to a slower pure-JS library otherwise. `sharp` is native code, and sharp images make the admin media browser feel fast.
    
2.  **Increase Node memory**: `NODE_OPTIONS=--max-old-space-size=4096` prevents the admin UI from crashing during bulk uploads.
    
3.  **Run the Payload admin on a subdomain** with a reverse proxy, Nginx or Caddy, if you expect moderate admin traffic alongside a public site. This keeps the `/admin` route group isolated at the server level.
    

## How we fit into this stack

At techpotions, we build web applications where the content layer is tightly coupled to AI features, in-app notifications driven by CMS content, context-aware internal tools, editable prompt templates that non-developers manage. Payload becomes the interface between engineers and domain experts. [Our web service](https://techpotions.com/services/web) hands you that setup: Payload configured inside Next.js, database wired for your deployment target, custom collection blueprints that match your data model rather than a generic blog schema, and admin components that feel native to your team's workflow. If that sounds closer to what you need than a blank scaffold, [let's talk](https://techpotions.com/services/web).

## FAQ

### Will Payload CMS slow down my Next.js frontend?

Payload only runs on the server. The client bundle does not include the CMS, and the admin panel is code-split under the `/(payload)` route group. Your public pages don't ship a single byte of admin JavaScript, only the API client you consciously import.

### Can I use Payload with the Next.js App Router and Server Components?

Yes. Payload 3.0 leverages the App Router natively. You query collections directly inside React Server Components with `getPayload`, as shown above, and you never expose a REST endpoint unless you choose to.

### Do I have to self-host, or can I deploy on Vercel?

You can deploy on Vercel with a serverless-compatible database like MongoDB Atlas or Turso. But Payload's admin UI opens a long-lived WebSocket for live preview, which Vercel's serverless model drops eventually. For production, a containerized deployment on Railway, Fly.io, or a plain VPS matches Payload's runtime better.
