I want to integrate Lucide-icons into payloadcms.
To do this I want a autofill or searchable select field which shows the name and a preview of the icon from the lucide-icons icon pack.
It seems like this should be possible, but I'm not sure how to implement it. Are there any ready made examples of icon fields that integrate with icon packs?
Looks like a good starting point!
I'm just not sure how to build the custom component so that the select field autopopulates options with the lucide icon names.
I hope someone ends up making a plugin for such a field... If not I might give it a try soon™
Wow, that was a fast demonstration! Thanks a bunch!
I'll give it a try
how did you get lucide.icons added to the window variable btw?
I managed to get the select up, now I just need to make the value a combination of a SVG and the name of the icon
Implementation 👇
import { Field } from 'payload/types';
import { Option } from 'payload/dist/fields/config/types';
import deepMerge from '../utilities/deepMerge';
import { createIcons, icons } from 'lucide';
export function generateLucideIconOptions(): Option[] {
const lucideIconOptions: Option[] = [];
Object.keys(icons).forEach((icon) => {
lucideIconOptions.push({
label: icon,
value: icon,
});
});
console.debug('lucideIconOptions', lucideIconOptions);
return lucideIconOptions;
}
export function lucideIcon({ overrides = {} } = {}): Field {
const lucideIcon: Field = {
name: 'lucideIcon',
type: 'select',
options: generateLucideIconOptions(),
};
return deepMerge(lucideIcon, overrides);
}
Usage 👇
import { CollectionConfig } from 'payload/types';
import { lucideIcon } from '../fields/lucideIcon';
export const Icons: CollectionConfig = {
slug: 'icons',
labels: {
singular: 'Icon',
plural: 'Icons',
},
fields: [lucideIcon()],
};
Good idea!
Thanks for all the help
Do you still have time to help with the ui field implementation?
I haven't messed around with ui fields yet, so not sure where to start
The documentation is a bit scarce:
https://payloadcms.com/docs/fields/uiGreat, thanks!
So I've figured out that I need to create a Custom Component Field.
My idea is that I can just copy the one used for
type: 'select'
, and allow the addition of this type as an option:
export type OptionObjectWithIcon = {
label: Record<string, string> | string;
value: string;
icon: string; // svg code as a string
};
Then if the type is
OptionObjectWithIcon
I'll just render the svg inside the select's option
I have no idea how to do it in practice though, so I've been trying to get ChatGPT to do it for me to no avail:
https://chat.openai.com/share/cd94d7b8-b639-432a-9acc-b2e9060f72caThey have a custom select? 😮
The built in select field is of type
SelectField
:
export type OptionObject = {
label: Record<string, string> | string;
value: string;
};
export type Option = OptionObject | string;
export type SelectField = FieldBase & {
type: 'select';
options: Option[];
hasMany?: boolean;
admin?: Admin & {
isClearable?: boolean;
isSortable?: boolean;
};
};
Agreed, but if we can just override the original one so that it accepts my type (
OptionObjectWithIcon
) as an option, and renders the SVG if
option.icon
is not empty I think that would be better since we hopefully keep the same styling instead of having to re-style a new component
I get this error:
[15:29:35] ERROR (payload): There were 1 errors validating your Payload config
[15:29:35] ERROR (payload): 1: Collection "products" > Field "categories" > "value" does not match any of the allowed types
[nodemon] app crashed - waiting for file changes before starting...
not sure why though
I can share all the relevant files if you want
Folder structure in attached image for reference
I'm not even sure I'm trying to get the value anywhere
Don't feel the urge to respond right away, but I'll just post this while I still remember it 😛
I think it's complaining about the value inside categoriesOptions here:
// collections/Products.ts
import { CollectionConfig } from 'payload/types';
import CustomSelectFieldWithIcon, {
OptionObjectWithIcon,
} from '../fields/selectFieldWithIcon'; // Adjust the path if needed
// Define the options for the Categories field as custom OptionObject[] type
const categoriesOptions: OptionObjectWithIcon[] = [
{
label: {
en: 'Electronics',
fr: 'Électronique', // You can include labels for other languages as well
},
value: 'electronics',
icon: '<svg>...</svg>', // Replace with your SVG icon as a string
},
{
label: {
en: 'Clothing',
fr: 'Vêtements',
},
value: 'clothing',
icon: '<svg>...</svg>',
},
// Add more options as needed
];
export const Products: CollectionConfig = {
slug: 'products',
fields: [
// Your other fields...
{
name: 'categories',
label: 'Categories',
type: 'select',
options: categoriesOptions, // Use the custom OptionObject type for options
admin: {
// Optional admin configuration
components: {
Field: CustomSelectFieldWithIcon, // Use the custom field component
},
},
},
// Your other fields...
],
};
This is what I have working^
I want to add an icon for each select option. How do I do that?
Oooo, nice! Gonna try it out ASAP
I see. That example was great since it taught me which "components" I need to make to create a custom component and make use of it
I'm wondering how I can pass parameters to the index.tsx file though. So that I can choose the
options
content myself
I'm not really experienced with React btw.
So please let me know if I'm asking questions about React and not Payload 🙂
This is what I get when I run the example code btw:
How do I pass in the props though? Is there any documentation on that?
like this?
export function CustomSelect({
path,
options = ['icon-1', 'icon-2'],
}): React.FC<any> {
return (
<div>
<Select path={path} name={path} options={options} />
<RenderIcon path={path} />
</div>
);
}
The props to the CustomSelect function
Alright, I think I kinda understand. I'll try for a bit and get back to you if I'm completely stuck.
Thanks a lot for the help!
Shouldn't this work?
import { Field } from 'payload/types';
import { Option } from 'payload/dist/fields/config/types';
import { createIcons, icons } from 'lucide';
import { CustomSelect } from './CustomSelect';
export function generateLucideIconOptions(): Option[] {
const lucideIconOptions: Option[] = [];
Object.keys(icons).forEach((icon) => {
lucideIconOptions.push({
label: icon,
value: icon,
});
});
return lucideIconOptions;
}
export const IconSelectorField: Field = {
name: 'lucidIcon',
type: 'text',
admin: {
components: {
Field: CustomSelect({ options: generateLucideIconOptions() }),
},
},
};
^
It happens when my CustomSelect code is like this
If I change it to this I get a different error
I made it!
Not as generic as I'd wanted, but I finally made it work
My implementation:
Thank you both chris and Jarrod for the help!
Result:
No, I sadly didn't make one. Maybe a image to text service can be used to get the code though?
Feel free to make a gist if you'd like. Consider the code open-sourced 🙂
Awesome!
If it's not too much to ask, could you upload some screenshots of how it looks? 🙂
This would be a super cool idea, I love the lucide icons
I'm not a React guy, but since Payload uses React components, this would probably be a good fit
https://lucide.dev/guide/packages/lucide-reactAnd then built via custom component / field
Ah that shoiuld be pretty easy!
for instance, say it was plain javascript
const iconSelect = document.createElement('select')
Object.keys(window.lucide.icons).forEach((icon) => {
const opt = document.createElement('option')
opt.value = icon
opt.innerHTML = icon
iconSelect.appendChild(opt)
})
document.body.appendChild(iconSelect)
That creates a select list of every icon name from the Lucide package
(the main package exports
icons
as a keyed list
So now you can create a custom select field in payload
and BOOM it shall work
If you still need help, I can help you write it
But give it a go and show me what you come up with
Good luck! And yeah, if you get really stuck, let me know, I would start with.....
Creating a basic Select field type (Before creating a custom field that displays the actual icon, etc)
Note: In the JSFIDDLE example I shared, the
lucide.icons
object is a keyed by the icon names, where each value is the SVG of that icon. I'm simply getting the key names, not the SVG, so youll want to loop over each key/value, maybe with Object.entries.
When you use the CDN version of the library, it adds lucide as a property to window, like most CDN libraries
The ESM version should have it available under the modules default export
or one of the exports
This page has some information about that
https://lucide.dev/guide/packages/lucideYou will likely install lucide via your package mananger instead
And then import it right into your component
import { createIcons, icons } from 'lucide'
at which point, icons should be the same as
lucide.icons
in my example
YOO
Nicely done!
😄
Note: Rendering a ton of SVGs could cause render blocking
You may only want to display the icon of the selected value
and add maybe a link to the live icon list on lucide
I can help out in a little bit! I'm working on finishing a task at work but will be avail around Noon to check back 😄
Ah that would be cool! Though, does option support non-text?
Maybe their custom select dos
does*
Well im saying, the built in one
This one looks nice:
https://react-select.com/homehmmm
And what is the current issue with getting the icon to display?
Hmm, if we get to the point where I need to check the files then OK, but lets try to debug some more
So the error complains
Collection "products" > Field "categories" > "value"
When are you getting the value of the categories field
setting*
And what value type does it expect?
It's likely just through general validation
Give me a little bit and I'll be back on after lunch to review this more in depth
Like this
Basically the underlying field is a text field, because the payload config is static and you will not know the icon keys beforehand. So you render a custom react component that
rendersa select field, but the actual value saved is not an enum.
Yes, that is what you should get. Now you can style it however you want, you will need to generate the options for lucid icons and use those instead of my placeholders.
what do you mean? What props?
in that component, you need to create the options object to pass into the select component
sounds like you guys talked above about how to curate a list of icon keys
that is what you would pass into the select component
and you can run that code right within this component. You will likely want to create some state with React (setting the value inside a useEffect) and then pass the state value through to the Select
Sounds great! No problem
Nice! A little css and they could be right next to each other 😁
Do you have a working gist for this? it would be nice
Because I just found this thread:
https://gist.github.com/BirknerAlex/dd63a499190ea42c08bd2c682df156f5I made one but with Font Awesome Icons, but should be easy adjustable for every other icon pack.
Star
Discord
online
Get dedicated engineering support directly from the Payload team.