Atomic Payload
Plugins

@pro-laico/seed

Fill a fresh project with sample content in one click: a SEED DATABASE button in the admin and a guarded POST /api/seed endpoint behind it.

@pro-laico/seed gives you a one-click way to populate a brand-new project with the content that makes Atomic Payload feel alive: a starter design set, a default icon set, header and footer, sample pages, and site metadata. It adds a SEED DATABASE button to the admin dashboard and the guarded POST /api/seed endpoint behind it, so you can go from an empty database to a working site without writing any setup data by hand.

@pro-laico/seed is a Tool package, a building block of the Atomic Payload stack, meant to be used alongside @pro-laico/core and the other @pro-laico/* packages (most easily via the atomic-payload template), not on its own.

Seeding is destructive: it clears the collections it manages and recreates them from scratch. Run it on a fresh project, not over content you want to keep. By default the endpoint allows any authenticated user to run it, so lock it down with the authorize option in multi-user or production apps.

Installation

pnpm add @pro-laico/seed
npm install @pro-laico/seed
yarn add @pro-laico/seed

payload, next, react, and @payloadcms/ui are peers you already have in a Payload + Next.js app. The admin button uses Payload's UI components.

Setup

@pro-laico/core comes bundled with this plugin — it's a dependency, not a separate install — and provides the shared fields, hooks, and utilities this plugin builds on. See @pro-laico/core → Setup.

Adding seeding to your own Payload + Next.js project.

Add the plugin to your Payload config

With nothing passed, the plugin uses the bundled Atomic Payload seed. It's common to gate it behind an environment variable so it only registers where you want it:

import { buildConfig } from 'payload'
import { seedPlugin } from '@pro-laico/seed'

export default buildConfig({
  plugins: [
    seedPlugin({
      enabled: process.env.INCLUDE_SEED === 'true',
    }),
  ],
})

This does two things:

  • Mounts a POST /api/seed endpoint that runs the seed function. It's guarded: it returns 403 unless the request comes from an authenticated user (and, when you pass an authorize predicate, unless that predicate also approves them).
  • Adds a BeforeDashboard banner to the admin dashboard with a SEED DATABASE button that POSTs to that endpoint and shows a toast as it runs.

Register the admin button in the import map

The dashboard banner is an admin React component that Payload loads through its import map. After enabling the plugin, regenerate the map so Payload can find it:

pnpm payload generate:importmap

Without this step the endpoint still works, but the SEED DATABASE button won't appear on the dashboard. (If you'd rather not show the button at all, set includeBeforeDashboard: false and call the endpoint yourself.)

Lock it down for production

Out of the box the endpoint allows any authenticated user to seed. That's fine for a solo project, but risky once other people have logins. Pass an authorize predicate to restrict who may run the destructive seed:

seedPlugin({
  enabled: process.env.INCLUDE_SEED === 'true',
  authorize: (user) => user.roles?.includes('admin') ?? false,
})

The predicate runs after Payload's auth check and receives the authenticated user; return true to allow the seed.

Run the seed

Open the admin dashboard and click SEED DATABASE in the welcome banner. The button POSTs to /api/seed, the bundled seed clears and recreates its collections, and a toast confirms when it's done. Your site then has content to render.

The atomic-payload template already wires everything up: the plugin is in the config and the admin button is in the import map, so the SEED DATABASE button is ready on the dashboard.

Click SEED DATABASE

Open the admin dashboard. The welcome banner has a SEED DATABASE button; click it to populate a fresh project with the starter design set, icon set, header/footer, sample pages, and site metadata. A toast confirms when it finishes, and you can then visit the site to see the content.

Lock it down for production

The template registers the plugin as a plain seedPlugin(), so seeding is always available. Because the seed is destructive, pass enabled: false (or an authorize predicate) in src/plugins/index.ts once your project has real content you don't want overwritten.

seedPlugin({ enabled: false })

Options

seedPlugin(options?) accepts:

Prop

Type

The seed function accepts an optional second argument, slugConfig (a SeedSlugConfig), for remapping which collection and global slugs the bundled seed writes to. It isn't a plugin option of its own; you reach it by wrapping the seed option (above). Every key is optional and defaults to the slug the Atomic Payload collections use:

See Slugs for how those collections and globals are addressed across the stack.

All options at their defaults, as a working starting point:

import { seedPlugin } from '@pro-laico/seed'

seedPlugin({
  enabled: true,
  // seed defaults to the bundled Atomic Payload seed; pass your own SeedFn to change it
  endpointPath: '/seed',
  // authorize defaults to "any authenticated user"; pass a predicate to restrict further
  includeBeforeDashboard: true,
})

Environment variables

The plugin reads no environment variables. Control registration with the enabled option instead: pass false (or gate it behind your own env var) to skip the endpoint and the admin button.

Exports

Grouped by what they do: the plugin and its options, the seeding function, and the admin banner.

The plugin

Export

Type

seedPluginplugin
The plugin itself. Mounts the POST /api/seed endpoint and (by default) the SEED DATABASE admin banner. Default and named export.

Parameters

options?:SeedPluginOptionsSee the Options table above. Everything is optional: enabled, seed, endpointPath, authorize, includeBeforeDashboard.

Returns

PluginA Payload config plugin you add to buildConfig({ plugins: [...] }).

Example

import { buildConfig } from 'payload'import { seedPlugin } from '@pro-laico/seed'export default buildConfig({plugins: [  // gate it behind an env var so it only registers where you want it  seedPlugin({ enabled: process.env.INCLUDE_SEED === 'true' }),],})

Location

@pro-laico/seed
SeedPluginOptionstype
The TypeScript type for the plugin's options.

Location

@pro-laico/seed

Seeding

Export

Type

seedfunction
The bundled default seed. Clears and recreates a starter design set, icon set, header/footer, sample pages, and site metadata. This is what the endpoint runs unless you pass your own seed option. It deliberately does not run inside a database transaction (the first write to a fresh collection creates its namespace, which a transaction forbids), so it clears-and-recreates and is safely re-runnable instead of relying on rollback.

Parameters

args:{ payload: Payload; req: PayloadRequest }The Payload instance and the request, threaded through every write for request context (the writes run with overrideAccess: true as trusted server-side calls).
slugConfig?:SeedSlugConfigOptional per-slug overrides (pages, header, footer, forms, icon, iconSet, designSet, shortcutSet, siteMetaDataGlobal). Each is a string; any you omit falls back to the matching Atomic Payload slug. See the SeedSlugConfig keys under Options.

Returns

Promise<void>Resolves once the database has been seeded; it writes through Payload rather than returning the created documents. If a write fails it throws (the endpoint catches it and returns 500); there is no transactional rollback.

Example

import type { Endpoint } from 'payload'import { seed } from '@pro-laico/seed'// Your own guarded endpoint that reuses the bundled seed (this is the shape the// plugin mounts at POST /api/seed). req carries the Payload instance and user.export const customSeedEndpoint: Endpoint = {path: '/seed',method: 'post',handler: async (req) => {  if (!req.user) return Response.json({ error: 'Action forbidden.' }, { status: 403 })  // reuse the bundled seed, but write pages into a custom slug  await seed({ payload: req.payload, req }, { pages: 'my-pages' })  return Response.json({ success: true })},}

Location

@pro-laico/seed
SeedSlugConfigtype
Optional collection/global slug overrides for the bundled seed(), letting it write to non-default slugs. Every key is an optional string. See the SeedSlugConfig keys under Options.

Location

@pro-laico/seed

Admin

Export

Type

BeforeDashboardcomponent
The admin dashboard banner with the SEED DATABASE button. Loaded through Payload's import map, so reference it by its subpath rather than importing it at config time.

Location

@pro-laico/seed/admin/beforeDashboard

On this page