Tabler Icons is one of the most popular icon libraries. This article shows how to integrate the icon library into Phoenix projects. We will track the Tabler Icons source repository using Mix and use the Tailwind CSS plugin feature to create an icon component.
Introduction
In almost every web application you will need icons to represent different actions or states. There are many icon libraries available, but one of the most popular is Tabler Icons. Tabler Icons is a set of over 5000 free, MIT-licensed, high quality SVG icons. The icons are maintained by Paweł Kuna and come in two versions: filled and outlined.
This article shows how to integrate Tabler Icons into an existing Phoenix project.
Tracking the Tabler Icons source repository
The first step is to track the Tabler Icons source repository using Mix. This will allow us to easily update the icons in our project when new icons are added or existing icons are updated.
To track the Tabler Icons source repository, we need to add the following to the deps
function in the mix.exs
file:
# mix.exs
{:tabler_icons,
github: "tabler/tabler-icons",
sparse: "icons",
app: false,
compile: false,
depth: 1}
This will add the Tabler Icons repository as a dependency to our project. The sparse
option is used to only download the icons
directory from the repository. We set app
to false
because we don’t want to read the app file. We also set compile
to false
because we don’t want to compile the icons. We just want to download the icons so we can use them later in our Tailwind CSS config. To increase the speed of the download, we set the depth
option to 1
to only load the latest commit.
If you have not seen the above options before, you can find a detailed explanation of them in the Mix documentation.
After adding the dependency, we need to run mix deps.get
to download the icons from the Tabler Icons repository. The icons will be downloaded to the deps/tabler_icons/icons
directory.
Updating the Tailwind CSS config
Next, we need to update thetailwind.config.js
. We create a custom plugin that generates the CSS classes for the icons.
Reading the SVG files
The first step is to make the plugin read the SVG files from the deps/tabler_icons/icons
directory.
module.exports = {
// ...
plugins: [
plugin(function () {
const iconsDir = path.join(__dirname, "../deps/tabler_icons/icons")
const values = {}
const icons = [
["", "/outline"],
["-filled", "/filled"],
]
icons.forEach(([suffix, dir]) => {
fs.readdirSync(path.join(iconsDir, dir)).forEach(file => {
const name = path.basename(file, ".svg") + suffix
values[name] = { name, fullPath: path.join(iconsDir, dir, file) }
})
})
})
]
}
The above code reads the SVG files from the deps/tabler_icons/icons
directory and creates an object with the icon names and their full paths. This way, we can easily reference the icons later.
The values
object will look like this:
{
"user": { name: "user", fullPath: "/path/to/deps/tabler_icons/icons/outline/user.svg" },
"user-filled": { name: "user-filled", fullPath: "/path/to/deps/tabler_icons/icons/filled/book.svg" },
// ...
}
We append the suffix -filled
to filled icon names to distinguish between the filled and outlined versions of the icons. Since outline should be the default, we don’t append any suffix to the outline icons.
Generating the CSS classes
Next, we need to get the plugin to generate the CSS classes for the icons. We want to add the CSS for elements that contain a tabler-*
class. For example, if we have an element with a tabler-user
class, we want to add the CSS for the user icon. To do this, we use the matchComponent
function provided by Tailwind CSS.
module.exports = {
// ...
plugins: [
plugin(function ({ matchComponents, theme }) {
const values = {}
// read icons and add to values object
matchComponents({
"tabler": ({ name, fullPath }) => {
const content = fs.readFileSync(fullPath).toString().replace(/\r?\n|\r/g, "")
return {
[`--tabler-${name}`]: `url('data:image/svg+xml;utf8,${content}')`,
"-webkit-mask": `var(--tabler-${name})`,
"mask": `var(--tabler-${name})`,
"mask-repeat": "no-repeat",
"background-color": "currentColor",
"vertical-align": "middle",
"display": "inline-block",
"width": theme("spacing.5"),
"height": theme("spacing.5")
}
}
}, { values })
})
]
}
This code matches items with the tabler-*
. It extracts the name and full path of the icon from the values
object created earlier. It then reads the contents of the SVG file and generates the CSS classes for the icon. The CSS classes set the icon as the element’s background image and set the width and height of the element to theme("spacing.5")
. This way, we can easily control the size of the icons using Tailwind’s CSS spacing utilities.
Remove width and height from the SVG
The icons provided by the Tabler Icons library have width and height attributes set in the SVG files. We need to remove these attributes so that we can control the size of the icons using Tailwind CSS.
We already have a regex that removes all line breaks and carriage returns from the path string. We can extend this to also remove the width and height attributes from the SVG files.
const content = fs.readFileSync(fullPath).toString()
.replace(/\r?\n|\r/g, "")
.replace(/width="[^"]*"/, "")
.replace(/height="[^"]*"/, "");
The final plugin code will look like this:
module.exports = {
// ...
plugins: [
plugin(function ({ matchComponents, theme }) {
const iconsDir = path.join(__dirname, "../deps/tabler_icons/icons")
const values = {}
const icons = [
["", "/outline"],
["-filled", "/filled"],
]
icons.forEach(([suffix, dir]) => {
fs.readdirSync(path.join(iconsDir, dir)).forEach(file => {
const name = path.basename(file, ".svg") + suffix
values[name] = { name, fullPath: path.join(iconsDir, dir, file) }
})
})
matchComponents({
"tabler": ({ name, fullPath }) => {
const content = fs.readFileSync(fullPath).toString()
.replace(/\r?\n|\r/g, "")
.replace(/width="[^"]*"/, "")
.replace(/height="[^"]*"/, "");
return {
[`--tabler-${name}`]: `url('data:image/svg+xml;utf8,${content}')`,
"-webkit-mask": `var(--tabler-${name})`,
"mask": `var(--tabler-${name})`,
"mask-repeat": "no-repeat",
"background-color": "currentColor",
"vertical-align": "middle",
"display": "inline-block",
"width": theme("spacing.5"),
"height": theme("spacing.5")
}
}
}, { values })
})
]
}
Build an icon component
Now that we have the CSS classes for the icons ready, we can create an icon component that makes it easy to use the icons in our Phoenix project.
defmodule MyAppWeb.CoreComponents do
use Phoenix.Component
attr :name, :string, required: true
attr :class, :string, default: nil
def icon(%{name: "tabler-" <> _} = assigns) do
~H"""
<span class={[@name, @class]} />
"""
end
end
This component takes the name of the icon as an argument and renders a span
element with the icon name as a class. We also allow the user to pass additional classes.
We can use this component in our templates like this:
<.icon name="tabler-user" class="bg-blue-600" />
Tailwind CSS will generate the appropriate CSS classes for the icon based on the plugin we built in the previous step, and the icon will be displayed with a blue background.
Conclusion
In this article, we have shown how to integrate Tabler Icons into a Phoenix project. We tracked the Tabler Icons source repository using Mix, built an icon component that makes it easy to use the icons in markup, and used the Tailwind CSS plugin feature to add the appropriate CSS.
Credits
I would like to credit the Phoenix team for the inspiration for this article. They are already using the same approach to integrate Heroicons into Phoenix projects. I just adapted it to work with Tabler Icons. For more information, you can check out the Phoenix Asset Management guide.