Skip to content

Instantly share code, notes, and snippets.

@BirknerAlex
Last active June 11, 2025 02:02
Show Gist options
  • Save BirknerAlex/dd63a499190ea42c08bd2c682df156f5 to your computer and use it in GitHub Desktop.
Save BirknerAlex/dd63a499190ea42c08bd2c682df156f5 to your computer and use it in GitHub Desktop.
Payload CMS Font Awesome Select Field
import { Field } from 'payload';
export const FontAwesomeField: Field = {
name: 'fontAwesomeIcon',
label: 'Icon',
type: 'text',
validate: (value) => {
if (!value) {
return 'Please select an icon';
}
return true;
},
admin: {
components: {
Field: '@/components/payload/FontAwesome#Select',
},
},
required: true,
hasMany: false,
localized: false,
};
'use client';
import * as React from 'react';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {
IconName,
IconPrefix,
IconProp,
} from '@fortawesome/fontawesome-svg-core';
import { SelectInput, useField, FieldLabel } from '@payloadcms/ui';
import { OptionObject } from 'payload';
import { library } from '@fortawesome/fontawesome-svg-core';
import { fas } from '@fortawesome/free-solid-svg-icons';
import { fab } from '@fortawesome/free-brands-svg-icons';
library.add(fas, fab);
export const fontAwesomeIconOptions = () => {
const options: OptionObject[] = [];
[fas, fab].forEach((iconPack) => {
Object.keys(iconPack).forEach((icon) => {
options.push({
label: icon,
value: iconPack[icon].prefix + '/' + iconPack[icon].iconName,
});
});
});
return options;
};
type FontAwesomeSelectComponentProps = {
path: string;
};
export const Select: React.FC<FontAwesomeSelectComponentProps> = ({ path }) => {
const { value, setValue } = useField<string>({ path });
let icon: IconProp = { prefix: 'fas', iconName: 'question' };
if (value) {
icon = value.split('/') as [IconPrefix, IconName];
}
return (
<div>
<SelectInput
path={path}
name={path}
value={value}
required={true}
label={'Icon'}
description={
'You can find all icons on the page https://fontawesome.com/icons'
}
hasMany={false}
options={fontAwesomeIconOptions()}
onChange={(e) => {
setValue(e?.value);
}}
/>
{value && icon && (
<span>
Icon Preview: <FontAwesomeIcon icon={icon} />
</span>
)}
</div>
);
};
@kylemcshea
Copy link

In tandem to this code you must also install your dependencies:

npm install @fortawesome/react-fontawesome @fortawesome/fontawesome-svg-core @fortawesome/free-solid-svg-icons @fortawesome/free-brands-svg-icons

@dreamy38
Copy link

image

Is this for payload v3?

@mommaroodles
Copy link

mommaroodles commented Apr 28, 2025

Hi @BirknerAlex - thank you for sharing this - I've been wanting to do this and I have managed to implement something, but I'm adding Icons as needed on a per block/component basis. My select field only gives me the Icon's name though and it would nice if instead of having to repeat the process every time, just having a centralized point to import Icons.

Your solution here looks like it could help me but I'm not quite sure how to implement, and I also wanted to know from you if the icons appears in the drop down list - so much better to have a visual of the icon.

So what I have done so far is the following and this is where I am possibly making mistakes:

I've copied the FontAwesome.tsx to my src/Components folder. The FontAwesome.ts is my src/utilities

Any possibility you can give me some pointers on the implementation please.

My simple implementation is as follows:

Icons Component

import * as Icons from 'lucide-react'

export const iconMap = {
  Timer: Icons.Timer, 
  Zap: Icons.Zap,
  ZoomIn: Icons.ZoomIn,
  PersonStanding: Icons.PersonStanding,
  DollarSign: Icons.DollarSign,
  MessagesSquare: Icons.MessagesSquare,
  Blocks: Icons.Blocks,
  Bot: Icons.Bot,
  Film: Icons.Film,
  MessageCircle: Icons.MessageCircle,
  Settings2: Icons.Settings2,

  // Add more icons here 
}

export type IconName = keyof typeof iconMap

export const availableIcons = Object.keys(iconMap).map((key) => ({
  label: key.replace(/([A-Z])/g, ' $1').trim(),
  value: key as IconName,
}))

export function RenderIcon({ name, className }: { name: IconName; className?: string }) {
  const Icon = iconMap[name]
  return Icon ? <Icon className={className} /> : null
}

and then in the relevant blocks config.tsx file - I have this as a field

{ name: 'icon', label: 'Icon', type: 'select', required: true, options: [...availableIcons], },

As you can see - I'm not using very many icons at the moment

@logemann
Copy link

logemann commented Jun 2, 2025

Hmm. When i put this into action, after starting the application, tons of code runs through my console and this message appears at the end:

The export groupHasName was not found in module [project]/node_modules/.pnpm/[email protected][email protected][email protected]/node_modules/payload/dist/exports/shared.js [app-client] (ecmascript) <exports>.
Did you mean to import tabHasName?
All exports of the module are statically known (It doesn't have dynamic exports). So it's known statically that the requested export doesn't exist.

No clue what this means. Regenerated importMaps without success.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment