The SVG format is a great way to add icons to your app. Its vector limitations force you to keep them simple while still being able to change some attributes, like fill- and border (stroke) color.
Widely supported for almost 20 years in all major browsers, there’s really no reason to use anything else (image sprites anyone? 😂)
In Rails there are currently two ways to display SVG’s:
- using
image_tag
; - directly add into the view (inline).
Using the image_tag
is just like any other image in rails like so: image_tag "svg/home.svg"
.
This is nice. It keeps the code clean and all icons neatly organized in app/assets/svg
. It has one big disadvantage though. You cannot change its attributes, like fill
or strokeWidth
.
Bummer! 🫤
To get that functionality back, you can inline your SVG’s like so
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-8 p-1.5 bg-blue-50 text-blue-600 rounded-full">
<path stroke-linecap="round" stroke-linejoin="round" d="m2.25 12 8.954-8.955c.44-.439 1.152-.439 1.591 0L21.75 12M4.5 9.75v10.125c0 .621.504 1.125 1.125 1.125H9.75v-4.875c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21h4.125c.621 0 1.125-.504 1.125-1.125V9.75M8.25 21h8.25" />
</svg>
(icon taken from Heroicons)
Great—that’s a good looking icon! But having SVG’s like this in your views is messy (already have to deal with those hundreds of utility classes! 🤓).
Why not take the best of both worlds: write a little Ruby to inline SVG files!
Create a helper:
# app/helpers/icon_helper.rb
module IconHelper
def icon(name)
File.read(Rails.root.join("app", "assets", "svg", "#{name}.svg")).html_safe
end
end
Now in your views you can write:
icon "home"
Clean, right?! But there is no option to pass any attributes, like class
for example. Let’s extend the helper.
module IconHelper
def icon(name, css: nil)
icon_path = File.read(Rails.root.join("app", "assets", "svg", "#{name}.svg"))
Nokogiri::HTML::DocumentFragment.parse(icon_path)
.at_css("svg")
.tap { _1["class"] = css }
.to_html
.html_safe
end
end
Now you can pass along (utility) classes to your icon:
icon "home", css: "size-8 p-1.5 bg-blue-50 text-blue-600 rounded-full"
Amazeballs! 🍭
There’s a gem for that!
Of course OSS to the rescue. 🛟 Recently I published a little Rails gem to do all this for you. It’s icon library agnostic, meaning you can use any icon library you want (opposed to other icon gems that limit to one library).
Currently it has first-party support for Feather Icons, Heroicons, Lucide, Phosphor, Tabler and custom animated icons too.
Check it out at GitHub. 😺