A DatoCMS field editor plugin that lets editors pick an icon from the Untitled UI icon set — or, optionally, type a digit-only label (e.g. 01) instead. The chosen value is stored as a JSON-serialized object inside a single string field, ready for your frontend to render.
{"icon": "Star01", "number": "01", "useNumber": false}. Your frontend is responsible for parsing the value and resolving icon to a React component.fieldTypes: ['string']).@untitledui/icons at build time. If your frontend is pinned to a different version of @untitledui/icons, picked names may not exist there — keep the versions in lockstep.Install via the DatoCMS marketplace:
Untitled UI icon picker and install it.Then, on any single-line string field where you want the picker:
The plugin has a global setting under Settings → Plugins → Untitled UI icon picker → Settings:
01) instead of picking an icon. Disabled by default.The plugin stores a JSON-serialized object in the string field:
{ "icon": "Star01", "number": "01", "useNumber": false}icon — the picked icon's component name from @untitledui/icons, or null.number — the typed digit-only label, or null.useNumber — which value the editor chose to display: true ⇒ render number, false ⇒ render icon.Both icon and number can be present at the same time — useNumber is the source of truth for what to render. An empty/unset field is null, not an empty object.
In your frontend, parse the stored value and render either the icon or the number label:
import * as Icons from '@untitledui/icons';import type { FC, SVGProps } from 'react';
type IconComponent = FC<SVGProps<SVGSVGElement> & { size?: number }>;const registry = Icons as unknown as Record<string, IconComponent>;
type IconValue = { icon: string | null; number: string | null; useNumber: boolean;};
const parseIconValue = (raw: string | null | undefined): IconValue | null => { if (!raw) return null; try { return JSON.parse(raw) as IconValue; } catch { return null; }};
export const Icon = ({ value, size = 24,}: { value: string | null | undefined; size?: number;}) => { const parsed = parseIconValue(value); if (!parsed) return null; if (parsed.useNumber && parsed.number) return <span>{parsed.number}</span>; if (parsed.icon) { const Cmp = registry[parsed.icon]; if (Cmp) return <Cmp size={size} />; } return null;};nvm usenpm installnpm run dev # vite dev server on :5173npm run build # builds dist/ — what gets shipped to the marketplacenpm run lintnpm run typecheckTo test the plugin against a real DatoCMS project before publishing, add it as a private plugin in that project's settings pointing at your local dev server URL.
Releases are cut from main via the Release GitHub Action (workflow_dispatch), which uses release-it to bump the version, generate CHANGELOG.md, tag, and npm publish. The DatoCMS marketplace picks up new versions automatically within an hour.
MIT © November Five