rails

Inline SVGs with Rails and Vite

Here’s one way to add Vite support to the popular inline_svg gem, plus a way to implement your own helper and skip the gem dependency altogether.

The inline_svg gem is an easy and powerful tool for rendering SVGs in Rails. To my surprise, as of May 2023 it doesn’t yet support Vite out of the box.

Extending inline_svg to add Vite support is pretty straightforward, though. Here’s how to do it.

Define a custom file loader

There are two ways Vite assets can be resolved:

  • In test and production environments, assets are copied into the Rails public directory and served from there.
  • In development, assets are watched and processed by a dedicated dev server, which listens for asset requests on port 3036 by default.

To extend inline_svg and make it aware of Vite, you’ll need to define a module with a named method that reads a requested SVG from those locations.

module ViteInlineSvgFileLoader
  class << self
    def named(filename)
      vite = ViteRuby.instance
      vite_asset_path = vite.manifest.path_for(filename)

      if vite.dev_server_running?
        fetch_from_dev_server(vite_asset_path)
      else
        Rails.public_path.join(vite_asset_path.sub(%r{^/}, "")).read
      end
    end

    private

    def fetch_from_dev_server(path)
      config = ViteRuby.config
      dev_server_uri = URI("#{config.protocol}://#{config.host_with_port}#{path}")
      response = Net::HTTP.get_response(dev_server_uri)
      raise "Failed to load inline SVG from #{dev_server_uri}" unless response.is_a?(Net::HTTPSuccess)

      response.body
    end
  end
end
app/lib/vite_inline_svg_file_loader.rb
Given the name of an SVG file, the named function uses Vite to resolve it and return the contents of the file. This satisfies inline_svg’s custom file loader contract.

Install and configure inline_svg

To use the inline_svg gem, first install it:

gem "inline_svg"
Gemfile
Add inline_svg and run bundle install.

Then it is just a matter of configuring inline_svg to use the custom file loader:

InlineSvg.configure do |config|
  require Rails.root.join("app/lib/vite_inline_svg_file_loader")
  config.asset_file = ViteInlineSvgFileLoader
end
config/initializers/inline_svg.rb
Tell inline_svg to use Vite when resolving SVG assets.

And now you can use inline SVGs in your Rails views like this:

<%= inline_svg_tag("images/logo.svg") %>
This renders the Vite asset located in app/frontend/images/logo.svg.

Is the inline_svg gem always necessary?

The inline_svg gem is nice, but depending on your needs it might be overkill.

If you’re trying to minimize gem dependencies and don’t require arbitrary nokogiri-powered transformations of SVG files, you can skip the gem and write your own inline_svg_tag helper method instead.

This implementation is basic but is often enough to get the job done:

module InlineSvgHelper
  def inline_svg_tag(filename, title: nil)
    svg = ViteInlineSvgFileLoader.named(filename)
    svg = svg.sub(/\A<svg/, '<svg role="img"')
    svg = svg.sub(/\A<svg.*?>/, safe_join(['\0', "\n", tag.title(title)])) if title.present?

    svg.strip.html_safe
  end
end
app/helpers/inline_svg_helper.rb
This helper adds role=img to the <svg> element and also accepts an optional title argument for dynamically inserting a <title>. These are important for improving accessibility.

As of May 2023, the Tabler SVG icons on the mattbrictson.com site are all being rendered using this technique.

Further reading

In 2021, GitHub user @Intrepidd proposed adding Vite support directly to the inline_svg gem itself. Their code was not merged, but it is still available for reference in PR #128.

Here’s some more background about Vite and inline SVGs:

By the way, the code in this post is included in mattbrictson/rails-template. Specify the --javascript vite option when generating a new project to hit the ground running with Rails 7 and Vite!

Share this? Copy link

Feedback? Email me!

Hi! 👋 I’m Matt Brictson, a software engineer in San Francisco. This site is my excuse to practice UI design, fuss over CSS, and share my interest in open source. I blog about Rails, design patterns, and other development topics.

Recent articles

RSS
View all posts →

Open source projects

mattbrictson/rails-template

App template for Rails 7 projects; best practices for TDD, security, deployment, and developer productivity. Now with optional Vite integration! ⚡️

1,055
Updated 1 month ago

mattbrictson/tomo

A friendly CLI for deploying Rails apps ✨

360
Updated 20 days ago

More on GitHub →