How to Use Lucide Icons in Phoenix: A Complete Guide
Heroicons is a great library for using icons in your Phoenix project. However, sometimes you may need more icons. Lucide is another excellent library that offers a wider range of icons.
It's quite straightforward to add Lucide to your Phoenix project.
Add Lucide repository to mix.exs
defp deps do [ {:lucide, github: "lucide-icons/lucide", tag: "0.390.0", sparse: "icons", app: false, compile: false, depth: 1} ] end
We will support both Heroicons and Lucide icons. To manage this, we can reuse some code from the generated assets/tailwind.config.js
. Since the code is becoming a bit lengthy, we will create a new file, assets/tailwind.icon-components.js
, to handle the icons.
Create a new file assets/tailwind.icon-components.js
and add the following code
const fs = require('fs') const path = require('path') const plugin = require('tailwindcss/plugin') // svg style for icon components const svgStyle = ({ prefix, name, content, size }) => ({ [`--${prefix}-${name}`]: `url('data:image/svg+xml;utf8,${content}')`, '-webkit-mask': `var(--${prefix}-${name})`, mask: `var(--${prefix}-${name})`, 'background-color': 'currentColor', 'vertical-align': 'middle', display: 'inline-block', width: size, height: size, }) // Embeds Hero Icons (https://heroicons.com) into your app.css bundle // See your `CoreComponents.icon/1` for more information. exports.heroComponent = plugin(({ matchComponents, theme }) => { let iconsDir = path.join(__dirname, '../deps/heroicons/optimized') const icons = [ ['', '/24/outline'], ['-solid', '/24/solid'], ['-mini', '/20/solid'], ['-micro', '/16/solid'], ] const values = icons.reduce( (acc, [suffix, dir]) => fs.readdirSync(path.join(iconsDir, dir)).reduce((iconsAcc, file) => { const name = path.basename(file, '.svg') + suffix iconsAcc[name] = { name, fullPath: path.join(iconsDir, dir, file) } return iconsAcc }, acc), {}, ) matchComponents( { hero: ({ name, fullPath }) => { const content = fs .readFileSync(fullPath) .toString() .replace(/\r?\n|\r/g, '') let size = theme('spacing.6') if (name.endsWith('-mini')) { size = theme('spacing.5') } else if (name.endsWith('-micro')) { size = theme('spacing.4') } return svgStyle({ prefix: 'hero', name, content, size }) }, }, { values }, ) }) exports.lucideComponent = plugin(({ matchComponents, theme }) => { let iconsDir = path.join(__dirname, '../deps/lucide/icons') const values = fs.readdirSync(iconsDir).reduce((iconsAcc, file) => { if (file.endsWith('.svg')) { const name = path.basename(file, '.svg') iconsAcc[name] = { name, fullPath: path.join(iconsDir, file) } return iconsAcc } else { return iconsAcc } }, {}) matchComponents( { lucide: ({ name, fullPath }) => { const content = fs .readFileSync(fullPath) .toString() .replace(/\r?\n|\r/g, '') // Remove width and height attributes we only need viewBox .replace('width="24"', '') .replace('height="24"', '') const size = theme('spacing.6') return svgStyle({ prefix: 'lucide', name, content, size }) }, }, { values }, ) })
Update the assets/tailwind.config.js
to use the new plugins
// At the top add the import for the icons const { heroComponent, lucideComponent } = require('./tailwind.icon-components') // ... // remove the inline plugin for heroicon and add the two plugins we just created plugins: [ heroComponent, lucideComponent, ],
Modifying the Icon Function in core_components.ex
@doc """ Renders a [Heroicon](https://heroicons.com). Renders a [Lucide](https://lucide.dev/). Heroicons come in three styles – outline, solid, and mini. By default, the outline style is used, but solid and mini may be applied by using the `-solid` and `-mini` suffix. You can customize the size and colors of the icons by setting width, height, and background color classes. Icons are extracted from the `deps/heroicons`, `deps/lucide` directory and bundled within your compiled app.css by the plugin in your `assets/tailwind.icon-components`. ## Examples <.icon name="hero-x" /> <.icon name="lucide-squirrel" /> """ attr :id, :string, default: nil attr :name, :string, required: true attr :class, :string, default: nil def icon(%{name: "hero-" <> _} = assigns) do ~H""" <span id={@id} class={[@name, @class]} /> """ end def icon(%{name: "lucide-" <> _} = assigns) do ~H""" <span id={@id} class={[@name, @class]} /> """ end
And that's it! You've now added Lucide icons to your Phoenix project alongside Heroicons. Enjoy the flexibility and variety in your icon choices. Happy coding!