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!

Stay up to date

Sign up for the mailing list and get notified via email when new blog posts come out.