Using SVGs in a Rails Stack

There are plenty of reasons not to use Icon Fonts in your web applications, from accessibility issues to impact on load times. There are some good guides comparing Icon Fonts and SVGs on the web for more context.

We switched from using Icon Fonts to SVGs a while back and I'll explain how we use SVGs in our Rails stack.

Inline vs External SVGs

There are 2 main ways to use SVGs in your application. One is to inline them in your markup:

<div class="flex-shrink-0">
  <svg class="h-5 w-5 text-red-400" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
          <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/>
  </svg>
</div>

This is easy to do but adds to the size of your markup file and makes changing the icons difficult. If you want to change the "Edit" icon across the application, you'll need to make a change in every file where it appears.

Using External SVG files takes away the above problem, but has its own issues. As well as patchy browser compatibility, styling external SVGs can be tricky and doesn't always work as expected. Normally you would use an external SVG in a file like this:

<!-- SVG Sprite -->
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <symbol id="Icon-grid" viewBox="0 0 16 16">
        <path d="M14 6h-2c-0.55 0-1 0.45-1 1v2c0 0.55 0.45 1 1 1h2c0.55 0 1-0.45 1-1v-2c0-0.55-0.45-1-1-1zM14 9h-2v-2h2v2zM9 6h-2c-0.55 0-1 0.45-1 1v2c0 0.55 0.45 1 1 1h2c0.55 0 1-0.45 1-1v-2c0-0.55-0.45-1-1-1zM9 9h-2v-2h2v2zM4 6h-2c-0.55 0-1 0.45-1 1v2c0 0.55 0.45 1 1 1h2c0.55 0 1-0.45 1-1v-2c0-0.55-0.45-1-1-1zM4 9h-2v-2h2v2zM14 1h-2c-0.55 0-1 0.45-1 1v2c0 0.55 0.45 1 1 1h2c0.55 0 1-0.45 1-1v-2c0-0.55-0.45-1-1-1zM14 4h-2v-2h2v2zM9 1h-2c-0.55 0-1 0.45-1 1v2c0 0.55 0.45 1 1 1h2c0.55 0 1-0.45 1-1v-2c0-0.55-0.45-1-1-1zM9 4h-2v-2h2v2zM4 1h-2c-0.55 0-1 0.45-1 1v2c0 0.55 0.45 1 1 1h2c0.55 0 1-0.45 1-1v-2c0-0.55-0.45-1-1-1zM4 4h-2v-2h2v2zM14 11h-2c-0.55 0-1 0.45-1 1v2c0 0.55 0.45 1 1 1h2c0.55 0 1-0.45 1-1v-2c0-0.55-0.45-1-1-1zM14 14h-2v-2h2v2zM9 11h-2c-0.55 0-1 0.45-1 1v2c0 0.55 0.45 1 1 1h2c0.55 0 1-0.45 1-1v-2c0-0.55-0.45-1-1-1zM9 14h-2v-2h2v2zM4 11h-2c-0.55 0-1 0.45-1 1v2c0 0.55 0.45 1 1 1h2c0.55 0 1-0.45 1-1v-2c0-0.55-0.45-1-1-1zM4 14h-2v-2h2v2z"></path>
        </symbol>
</svg>

Then you'd call them in your markup like this:

<div>
  <svg class="Icon">
    <use xlink:href="/icon_sprite.svg#Icon-grid"></use>
  </svg>
</div>

Using this method you can style and animate your icons with CSS. However if you use a mix of Path and Fill SVG icons, you will find the styling difficult with external SVGs.

In my experience, using a Rails helper to turn external files into inline SVGs gives you the best of both worlds. To do this, I use the following helper:

module ApplicationHelper

  def embedded_svg(filename, options = {})
    assets = Rails.application.assets
    asset = assets.find_asset(filename)

    if asset
      file = asset.source.force_encoding("UTF-8")
      doc = Nokogiri::HTML::DocumentFragment.parse file
      svg = doc.at_css "svg"
      svg["class"] = options[:class] if options[:class].present?
    else
      doc = "<!-- SVG #{filename} not found -->"
    end

    raw doc
  end
end

Then in my markup, I use this helper (classes are from TailwindCSS)

<span class="ml-4 fill-current h-4 w-4 text-red-400">
  <%= embedded_svg('icon-grid') %>
</span>

Using this helper you can even replace the class that comes with the SVG:

<%= embedded_svg('icon-grid', class: 'my-class') %>

The result is an embedded inline SVG without the repetition.

As you can see, the helper method looks for the SVG files in the assets. This means you can put your SVGs under app/assets as individual files and benefit from asset cache busting, since the helper uses the find_asset method.

More Articles:

Try Cloud 66 for Free, No credit card required