npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@mvriu5/payload-icon-picker

v1.0.2

Published

Payload plugin introducing an icon picker into the CMS.

Readme

Payload Icon Picker

Payload plugin for adding an icon picker field to the admin UI. The field stores a string, while editors can search and choose from the icons you register in the plugin config.

Installation

pnpm add @mvriu5/payload-icon-picker

Install the icon library you want to use in your project, for example:

pnpm add lucide-react

Usage

Register all icons once in your Payload config, then use iconField() wherever a Payload text field should become an icon picker.

import { postgresAdapter } from "@payloadcms/db-postgres"
import { lexicalEditor } from "@payloadcms/richtext-lexical"
import * as Icons from "lucide-react"
import { buildConfig } from "payload"
import { iconField, payloadIconPlugin } from "@mvriu5/payload-icon-picker"
import { lucideIconAdapter } from "@mvriu5/payload-icon-picker/adapters/lucide"

export default buildConfig({
    collections: [
        {
            slug: "posts",
            fields: [
                {
                    name: "title",
                    type: "text",
                    required: true,
                },
                iconField({
                    name: "icon",
                    label: "Icon",
                }),
            ],
        },
    ],
    plugins: [
        payloadIconPlugin({
            icons: lucideIconAdapter(Icons),
        }),
    ],
    db: postgresAdapter({
        pool: {
            connectionString: process.env.DATABASE_URL,
        },
    }),
    editor: lexicalEditor(),
    secret: process.env.PAYLOAD_SECRET,
})

Stored Value

iconField() creates a normal Payload text field. The selected icon is resolved to a string and stored in the database.

By default, the stored value is icon.value ?? icon.name. Adapters can prefix generated values so multiple icon libraries do not collide:

payloadIconPlugin({
    icons: lucideIconAdapter(Icons, {
        prefix: "lucide",
    }),
})

Selecting ArrowRight would store:

lucide:ArrowRight

You can still use resolveIcon if you need a custom final storage format.

About resolveIcon

When using payloadIconPlugin(), resolveIcon runs during Payload config setup. The resolved string is passed to the admin field as each icon's final value.

That means normal iconField() usage does not require passing resolveIcon to the admin component manually.

The resolveIcon prop on IconRenderer, IconField, and createIconResolver() is primarily for direct component usage, tests, or advanced cases where you bypass payloadIconPlugin(). If you use a custom resolveIcon for stored values, pass the same resolver when rendering or resolving those values outside the Payload admin UI.

Rendering Stored Icons

Use IconRenderer from the client export to render a stored icon string in your frontend.

import * as Icons from "lucide-react"
import { IconRenderer } from "@mvriu5/payload-icon-picker/client"
import { lucideIconAdapter } from "@mvriu5/payload-icon-picker/adapters/lucide"

const icons = lucideIconAdapter(Icons)

export function PostIcon({ icon }: { icon?: string }) {
    return <IconRenderer value={icon} icons={icons} className="post-icon" />
}

The renderer supports React icon components and adapter-generated SVG metadata. SVG output is sanitized before rendering.

If you need full control over rendering, use createIconResolver() to turn a stored string back into the registered icon.

import * as Icons from "lucide-react"
import { createIconResolver } from "@mvriu5/payload-icon-picker"
import { lucideIconAdapter } from "@mvriu5/payload-icon-picker/adapters/lucide"

const resolveStoredIcon = createIconResolver({
    icons: lucideIconAdapter(Icons),
})

const resolvedIcon = resolveStoredIcon("Home")

Icon Inputs

Use an adapter for supported icon libraries. Adapters convert React icon exports to serializable SVG metadata for the Payload admin UI.

import * as Icons from "lucide-react"
import { lucideIconAdapter } from "@mvriu5/payload-icon-picker/adapters/lucide"

payloadIconPlugin({
    icons: lucideIconAdapter(Icons),
})
import * as TablerIcons from "@tabler/icons-react"
import { tablerIconAdapter } from "@mvriu5/payload-icon-picker/adapters/tabler"

payloadIconPlugin({
    icons: tablerIconAdapter(TablerIcons),
})
import * as SimpleIcons from "@icons-pack/react-simple-icons"
import { simpleIconsAdapter } from "@mvriu5/payload-icon-picker/adapters/simple-icons"

payloadIconPlugin({
    icons: simpleIconsAdapter(SimpleIcons),
})
import * as Heroicons from "@heroicons/react/24/outline"
import { heroiconsAdapter } from "@mvriu5/payload-icon-picker/adapters/heroicons"

payloadIconPlugin({
    icons: heroiconsAdapter(Heroicons),
})
import * as PhosphorIcons from "@phosphor-icons/react"
import { phosphorIconAdapter } from "@mvriu5/payload-icon-picker/adapters/phosphor"

payloadIconPlugin({
    icons: phosphorIconAdapter(PhosphorIcons, {
        weight: "regular",
    }),
})
import * as Hugeicons from "@hugeicons/core-free-icons"
import { hugeiconsIconAdapter } from "@mvriu5/payload-icon-picker/adapters/hugeicons"

payloadIconPlugin({
    icons: hugeiconsIconAdapter(Hugeicons),
})

Adapters support prefix, include, exclude, and optional label/value formatters:

When registering icons from more than one library, use a unique prefix for every adapter. Many icon libraries export generic names such as Home, Search, or User; prefixes keep stored values unique and make it clear which library an icon came from.

import * as SimpleIcons from "@icons-pack/react-simple-icons"
import * as TablerIcons from "@tabler/icons-react"
import { simpleIconsAdapter } from "@mvriu5/payload-icon-picker/adapters/simple-icons"
import { tablerIconAdapter } from "@mvriu5/payload-icon-picker/adapters/tabler"

payloadIconPlugin({
    icons: [
        ...tablerIconAdapter(TablerIcons, {
            label: ({ defaultLabel, prefix }) => `${prefix}:${defaultLabel.replace(/^Icon/, "")}`,
            prefix: "tabler",
        }),
        ...simpleIconsAdapter(SimpleIcons, {
            prefix: "si",
        }),
    ],
})

With prefix, the adapter keeps name as the original library export name and sets value to prefix:name, for example tabler:IconHome.

Without prefixes, duplicate icon values can collide. In development, the plugin warns when multiple registered icons resolve to the same stored value.

Or pass explicit icon metadata:

payloadIconPlugin({
    icons: [
        {
            Icon: Icons.Home,
            keywords: ["house", "start"],
            label: "Home",
            name: "Home",
            value: "home",
        },
    ],
})

Field Options

iconField() accepts normal single-value Payload text field options, plus picker labels and per-field icon filters:

iconField({
    name: "icon",
    label: "Icon",
    required: true,
    placeholder: "Search icons",
    noResultsLabel: "No icons found",
    admin: {
        position: "sidebar",
    },
})

Use libraries to limit a field to icons from selected adapter prefixes:

iconField({
    name: "navigationIcon",
    label: "Navigation Icon",
    libraries: ["lucide"],
})

Use icons to allow only specific stored icon values:

iconField({
    name: "socialIcon",
    label: "Social Icon",
    icons: ["si:SiGithub", "si:SiDiscord"],
})

You can combine both options. The filters are additive, so this example allows all Lucide icons plus the explicit GitHub icon:

iconField({
    name: "featuredIcon",
    libraries: ["lucide"],
    icons: ["si:SiGithub"],
})

Development

The dev Payload config in dev/payload.config.ts registers the plugin with lucide-react, which is installed as a development dependency.

pnpm dev

Generate the Payload import map after changing admin components:

pnpm generate:importmap

Build the package:

pnpm build