FishSoup25

payload plugin icons

by FishSoup25
Custom fields
v3
^3.37.0
MIT

Overview

Summary generated from the README

Adds an icon picker field to Payload 3 that stores the provider and icon name. It discovers icons from the Lucide and Phosphor packages installed in your app and serves serialized SVGs from plugin endpoints, so provider modules stay out of client bundles. Requires Payload 3.37 or later, React 19, and a Next.js config wrapper.

Features

  • Searchable, paginated icon picker in the admin panel
  • Built-in Lucide and Phosphor providers with optional subsets and custom labels
  • Provider and icon preview in collection list cells
  • Typed field data and a client Icon component for frontend rendering
  • Server-side custom provider support through an IconProvider contract
  • Batched and cached icon preview requests

Use cases

  • Let editors choose icons for features, navigation items or cards in page content
  • Render editor-selected icons on a Next.js frontend without bundling icon libraries
  • Add an in-house icon library as a custom provider for the picker

From the author

Icon picker field for Payload CMS with Lucide and Phosphor providers.

Installation

npm i payload-plugin-icons

2

Stars

0

Forks

0

Open Issues

1 months ago

Last Updated

0

Weekly Downloads

v1.1.5

npm Version

170.7 KB

Unpacked Size

0

Dependencies

GitHub stars

Trends appear after 7 days of data. Daily snapshots have not started yet.

npm weekly downloads

Trends appear after 7 days of data. Daily snapshots have not started yet.

Each point is npm's rolling 7-day download count.

Health Score

57/100Good

Composite score based on GitHub activity, stars, npm downloads, publish recency, dependency count, and package size.

Badge for your README

Payload Directory health badge
Markdown
[![Payload Directory](https://payloaddirectory.dev/badge/1197210288-root.svg)](https://payloaddirectory.dev/plugins/1197210288-root)
HTML
<a href="https://payloaddirectory.dev/plugins/1197210288-root"><img src="https://payloaddirectory.dev/badge/1197210288-root.svg" alt="Payload Directory"></a>
Updated Aug 24, 2026Created Mar 31, 2026Published Aug 14, 2026

README

payload-plugin-icons

An icon picker field for Payload 3. It stores { provider, name }, discovers icons from the provider versions installed in your app, and renders provider-neutral serialized SVGs.

Sponsor

BrainyBuilds

payload-plugin-icons is sponsored by BrainyBuilds. Thank you for supporting the project!

Features

  • Lucide and Phosphor icon providers
  • Searchable, paginated admin picker
  • Provider and icon preview in collection cells
  • Typed field data and React rendering component
  • Server-side custom provider support
  • No Lucide or Phosphor modules in admin or frontend client bundles
  • Batched, cached picker previews

Installation

npm install payload-plugin-icons lucide-react @phosphor-icons/react

Requirements: Payload ^3.37.0, React 19, Lucide React >=0.400.0, and Phosphor React >=2.0.0.

Configuration

import { buildConfig } from 'payload'
import { createIconPlugin } from 'payload-plugin-icons'

const { iconField, iconPlugin } = createIconPlugin()

export default buildConfig({
  plugins: [iconPlugin],
  collections: [
    {
      slug: 'pages',
      fields: [
        iconField({
          name: 'icon',
          label: 'Icon',
          required: false,
        }),
      ],
    },
  ],
})

Configure next.config so provider packages are loaded natively only when an icon endpoint runs. (Lucide uses Node runtime loading because Next optimizes it by default; other providers are added to serverExternalPackages.)

import { withPayload } from '@payloadcms/next/withPayload'
import { withPayloadIcons } from 'payload-plugin-icons'

const nextConfig = {}

export default withPayload(
  withPayloadIcons(nextConfig),
  { devBundleServerPackages: false },
)

Standalone and Docker deployments

Next.js output tracing cannot discover the provider packages because the plugin loads them through opaque runtime imports. This does not affect next dev or a conventional next start deployment where the project's complete node_modules remains available. However, output: 'standalone' copies only files discovered by output tracing into .next/standalone.

Explicitly include each configured provider package when building a standalone deployment:

import { withPayload } from '@payloadcms/next/withPayload'
import type { NextConfig } from 'next'
import { withPayloadIcons } from 'payload-plugin-icons'

const nextConfig: NextConfig = {
  output: 'standalone',
  outputFileTracingIncludes: {
    '/*': [
      './node_modules/lucide-react/**/*',
      './node_modules/@phosphor-icons/react/**/*',
    ],
  },
}

export default withPayload(
  withPayloadIcons(nextConfig),
  { devBundleServerPackages: false },
)

If the plugin is configured with only one built-in provider, include only that provider's package. Add equivalent patterns for custom provider packages. Patterns are resolved from the Next.js project root; monorepos may also need Next.js's outputFileTracingRoot setting.

withPayloadIcons configures provider packages for native server loading, but opaque runtime imports still require these explicit tracing includes in standalone builds.

packageImport defaults to payload-plugin-icons. Set it when the package is exposed through a different monorepo alias:

createIconPlugin({ packageImport: '@acme/payload-icons' })

After changing the field configuration, regenerate Payload's import map and types:

payload generate:importmap
payload generate:types

Rendering an icon

Icon is a Client Component. It fetches a small serialized SVG definition from the plugin endpoint; provider packages stay on the server. Requests mounted in the same turn are automatically batched and definitions are shared in a module-level cache.

'use client'

import type { IconData } from 'payload-plugin-icons'
import { Icon } from 'payload-plugin-icons/client'

export function FeatureIcon({ icon }: { icon?: IconData | null }) {
  return <Icon aria-hidden className="feature-icon" icon={icon} size={24} />
}

SVG props and event handlers are applied to the outer <svg>. Phosphor's weight is included in the request and cache key.

Customization

Use a subset of the built-in providers or change their labels:

import { createIconPlugin, lucideProvider } from 'payload-plugin-icons'

const { iconField, iconPlugin } = createIconPlugin({
  providers: [lucideProvider({ label: 'Interface icons' })],
})

Field options:

  • name: stored field name
  • label: admin label
  • required: require an icon name
  • defaultProviderId: provider selected for new documents
  • providerIds: provider subset for this field
  • labelsById: field-specific provider labels
  • packageImport: field-specific package name or alias
  • admin: additional Payload field admin options

Custom providers

A custom provider implements the server-only IconProvider contract. Keep its package import inside loadCatalog / loadIcons, serialize only validated names from your catalog, and add the package name to withPayloadIcons.

import type { IconProvider } from 'payload-plugin-icons'
import { createIconPlugin, serializeIconComponent } from 'payload-plugin-icons'

const customProvider: IconProvider = {
  id: 'custom',
  label: 'Custom',
  packageName: '@acme/icon-library',
  async loadCatalog() {
    return { provider: this.id, version: '1', icons: [{ name: 'Home' }] }
  },
  async loadIcons({ names }) {
    const icons = await import('@acme/icon-library')
    return Object.fromEntries(names.flatMap((name) =>
      name === 'Home' ? [[name, serializeIconComponent(icons.Home)]] : [],
    ))
  },
}

const { iconField, iconPlugin } = createIconPlugin({
  providers: [customProvider],
})

Call withPayloadIcons(nextConfig, ['@acme/icon-library']) for this provider.

Troubleshooting

Icon endpoints return 500 only in standalone or Docker

If /api/payload-icons/:provider/catalog or /api/payload-icons/:provider/icons works in development but returns 500 from a container that copies only .next/standalone, the provider package was probably omitted by Next.js output tracing. The endpoint may report that it is unable to load the icon catalog or icons.

Add the provider package to outputFileTracingIncludes as shown in Standalone and Docker deployments, rebuild the image, and confirm that its directory exists under the deployed node_modules. A typical standalone image copies .next/standalone and .next/static; copying .next/static does not add missing server dependencies.

Development

npm ci
npm run dev

The development app uses a local SQLite database at dev/payload.db, so no external database is required. You can optionally copy dev/.env.example to dev/.env to set PAYLOAD_SECRET or override DATABASE_URL. The development admin runs at http://localhost:2515.

Useful commands:

  • npm run check: lint and create a clean package build
  • npm run generate:importmap: regenerate the development import map
  • npm run generate:types: regenerate development Payload types
  • npm run build:next: build the development application

dist is committed and verified in CI.

License

MIT

Tags

Similar plugins