@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/seednpm install @pro-laico/seedyarn add @pro-laico/seedpayload, 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/seedendpoint that runs the seed function. It's guarded: it returns403unless the request comes from an authenticated user (and, when you pass anauthorizepredicate, unless that predicate also approves them). - Adds a
BeforeDashboardbanner 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:importmapWithout 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
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/seedSeedPluginOptionstype
Location
@pro-laico/seedSeeding
Export
Type
seedfunction
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/seedSeedSlugConfigtype
seed(), letting it write to non-default slugs. Every key is an optional string. See the SeedSlugConfig keys under Options.Location
@pro-laico/seedAdmin
Export
Type
BeforeDashboardcomponent
Location
@pro-laico/seed/admin/beforeDashboardRelated
@pro-laico/richtext
A ready-to-use rich-text block for Atomic Payload: a Lexical editor for writing formatted content in the admin and a renderer that turns it into clean React on your site.
@pro-laico/zap
Zod with Atomic Payload extensions: the shared schema layer that lets every @pro-laico package describe its data once and have those shapes flow into your generated Payload types.