Inline SVG Icons in Your Rails App

Abstract 3D checkerboard in breezy summer colors with elements of sunlight filtering through.

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. 😺

Get UI & Product Engineering Insights for Rails Apps (and product updates!)

Published at . Last updated at . Have suggestions or improvements on this content? Do reach out.

UI components for Ruby on Rails apps

$ 129 one-time
payment

Get Access
  • One-time Payment

  • Access to the Entire Library

  • Built using ViewComponent

  • Designed with Tailwind CSS

  • Enhanced with Hotwire