Payload Plugins
Pluginspayload-icons

Troubleshooting

Fix a fallback glyph, a solid-blob icon, a blank admin preview, or an empty requested-icons panel — symptom-first, with the exact strings to search for.

For AI / LLMs: View Markdown

Most payload-icons problems come down to a set that isn't active, a stroke-drawn SVG, or an import map that's out of date. Find your symptom below, then apply the fix.

SymptomCauseFix
Fallback warning glyph instead of your iconThe active set doesn't resolve the name you passed.Check three things in order: (1) a set is active, (2) it's published (not just saved as a draft), (3) the row name exactly matches the name you pass to <Icon> (both are kebab-cased). A name with no match always renders the fallback, never nothing.
Icon renders as a solid blob (an outline fills in — a circle becomes a disk)The uploaded SVG is stroke-drawn (fill="none" stroke="…", Lucide/Feather-style). The optimizer themes filled glyphs by rewriting their colors to currentColor, so a stroke icon's enclosed shapes fill solid.Upload solid fill-based glyphs instead (Noun Project-style filled paths). The upload detects the stroke signature and flags it in the doc's optimized report — check that field on the icon doc.
Blank admin icon preview (icon-preview component missing)The preview components register by string path and the import map is stale.Run pnpm payload generate:importmap, then restart the dev server.
Requested icons panel is empty in productionThe static scan reads your source, which isn't on disk at runtime.Run payload-icons-scan in your build to write icon-usage-manifest.json for the panel to read.
SVG upload rejectedPayload blocks SVG uploads by default (they're on its restricted-file-types list).Nothing to do — the icon collection sanitizes every file and opts in automatically via upload.allowRestrictedFileTypes. If you overrode upload, keep that flag set.

Building the <Icon> component

<Icon> is created from the createIcon factory, which takes a live Payload handle — the package never self-resolves your config. Build it once and import that module wherever you render icons:

src/components/Icon.ts
import config from '@payload-config'
import { getPayload } from 'payload'
import { createIcon } from '@pro-laico/payload-icons/components/Icon'

export const Icon = createIcon(getPayload({ config }))

See <Icon> for the full component contract.

Requested icons panel, in detail

The Requested icons panel draws from two sources, and each fails differently in production.

  • Static scan sees literal names only. It matches name="x", name={'x'}, and name={`x`}. A dynamic name={expr} is invisible to it — that's the runtime recorder's job (the iconRequest collection).

  • In dev the panel scans live, so nothing is set up or run. In production the source isn't on disk, so run payload-icons-scan during your build:

    payload-icons-scan            # scans ./src and ./app; -o sets the manifest path
  • In prod the panel reads icon-usage-manifest.json from cwd. Override the path with the ICON_USAGE_MANIFEST env var. Scanning another component? Pass --component Glyph.

  • On standalone / Vercel builds the manifest can go missing. A JSON file that's never imported isn't traced into the function bundle. Add it to outputFileTracingIncludes in next.config so the panel can read it there.

Behavior notes

These aren't bugs — they're how the plugin works.

  • The lookup name is the set entry's name, not the filename. Renaming an uploaded file changes nothing; the iconsArray row's name is the key your code references.
  • No revalidation is built in. A statically rendered page bakes the SVG at build time and won't reflect an edit or an active-set swap until the route revalidates. Install @pro-laico/payload-revalidate to make the whole surface self-healing, or wire revalidatePath / revalidateTag yourself. See Revalidation.
  • SVGs using transform / clip-path skip the geometry rewrite (it can't be applied safely) but are still sanitized before storage.

On this page