icons-only
Minimal example: @pro-laico/icons in isolation, with the Icon and IconSet collections and a small page that renders them.
A minimal Atomic Payload template that exercises the @pro-laico/icons plugin on its own, with no design sets or forms wired in. It is the Icon and IconSet collections plus a small Next.js page that renders them, and it also demonstrates the requested-icons usage panel and runtime miss tracking.
What it shows
This example demonstrates @pro-laico/icons in isolation:
iconsPlugin({ iconSetOptions: { livePreviewUrl, usagePanel: true }, trackRequests: true })registered inpayload.config.tsadds theiconandiconSetcollections to the admin UI.usagePanel: trueputs a "Requested icons" panel on the IconSet edit view, andtrackRequests: trueregisters the hiddeniconRequestcollection and makes<Icon>record names that fail to resolve at runtime.- The seed creates two icon sets: a "Demo Icon Set" (active) and a "Second Demo Set", each holding the same six SVG icons (arrow-right, check, cog, heart, star, x). It is auth-gated to logged-in admins. Re-running the seed replaces the sample data: it clears every
iconSet, deletes each sampleicon(and its file), and re-uploads, so it always lands on a clean, current state. /renders everyIcondoc inline by inlining itssvgStringfield, and renders eachIconSetas a labelled grid.- The Seed and Reset buttons are server actions that call
seedIcons/resetIconsdirectly (gated bypayload.auth), not a fetch to/api/seed. ThePOST /api/seedandPOST /api/resetREST routes still exist. - The "Requested icons" panel: open an IconSet in the admin and it diffs the names the app requests against the set's icons. The demo page renders an
IconChildTilewithname="y", which the seeded set does not define, so "y" shows as missing. WithtrackRequestson, visiting the page also records "y" as a live runtime miss, which appears in the panel with a count, and a Clear runtime requests button wipes those records. TheiconRequestcollection that backs this is hidden from the admin nav. - Live preview is wired for
iconSetedits: open any iconSet in the admin, click the Live Preview tab, and edits propagate to the home page in real time via<LivePreviewListener>and the/next/previewroute handler.
Scaffold it
npx @pro-laico/create-atomic-payload my-icons --template icons-onlypnpm dlx @pro-laico/create-atomic-payload my-icons --template icons-onlyThen set up and run it:
cp .env.example .env # set long PAYLOAD_SECRET + PREVIEW_SECRET (DATABASE_URI is optional)
cp gitignore.template .gitignore
pnpm install
pnpm generate:types # generates src/payload-types.ts + augment
pnpm generate:importmap # populates src/app/(payload)/admin/importMap.js
pnpm generate:icons # scans src for icon names, writes icon-usage-manifest.json
pnpm devpnpm generate:icons scans src for the app's icon component and writes the names it requests into icon-usage-manifest.json, which the "Requested icons" panel reads. The scan looks for <Icon> tags by default; this demo renders icons through its own IconChildTile component, so its script passes --component IconChildTile (see package.json). It runs automatically before pnpm build (via the prebuild script), so you only need to run it by hand to populate the panel in dev. The manifest is generated and gitignored, so it is not committed.
The demo ships with the SQLite adapter (@payloadcms/db-sqlite) wired to a local file at ./icons-only.db, with no database server required. Swap to Postgres or MongoDB by changing the import + db: call in src/payload.config.ts and installing the matching @payloadcms/db-* package.
How it works
- Open
http://localhost:3000/adminand create your first user. - Go back to
http://localhost:3000. The seed button is now visible: click it to create the sample icons and sets. - The page now renders the icons. Refresh after editing in the admin to see updates.
A few details worth knowing:
- The seed endpoint reads each set's SVG files from
src/seed/icons/andsrc/seed/icons-two/at request time, then uses Payload's local API (payload.create) to feed the SVG bytes directly via thefileargument, so the upload pipeline (including the icons plugin'sformatSVGHook) runs end-to-end. - The page uses
dangerouslySetInnerHTMLto inline each icon'ssvgStringfield. Because the icons are uploaded SVGs that pass throughformatSVGHook, the strings are already optimized and stripped of script tags by SVGO. - Workspace dependency direction: this template depends on
@pro-laico/icons, which in turn pulls in@pro-laico/coreand@pro-laico/atomic.
What to look at
src/
payload.config.ts # buildConfig: the only place iconsPlugin runs
instrumentation.ts # registerPayloadConfig for the cache getters
collections/users.ts # auth collection (required by Payload)
seed/
sampleIcons.ts # reads the SVG folders into icon-set manifests
icons/ # SVGs for the active "Demo Icon Set"
icons-two/ # SVGs for the "Second Demo Set"
app/
(frontend)/
layout.tsx # plain HTML layout, no design system
page.tsx # demo page: lists icons + sets, seed button
CodeBlock.tsx # Shiki-highlighted snippets on the page
next/preview/route.ts # live-preview entry route handler
next/exit-preview/route.ts # exits live-preview / draft mode
(payload)/
layout.tsx # Payload admin RootLayout
custom.scss # admin overrides (empty)
admin/
importMap.js # `pnpm generate:importmap` populates this
[[...segments]]/
page.tsx # Payload admin entry
not-found.tsx
api/
[...slug]/route.ts # Payload REST endpoint
seed/route.ts # POST /api/seed → seeds the sample sets
reset/route.ts # POST /api/reset → clears the seeded dataThe icon-usage manifest is written to icon-usage-manifest.json at the project root (generated by pnpm generate:icons, gitignored).