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
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"
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
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") %>
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
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:
- Vite Rails (GitHub)
- jamesmartin/inline_svg (GitHub)
- SVG in HTML Introduction (MDN)
- A Pretty Good SVG Icon System (CSS-Tricks)
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!