How do I display SVG image in Rails?
Asked Answered
U

11

23

I have a svg image on /app/asssets/images/symbols.svg with this as contents.

<svg xmlns="http://www.w3.org/2000/svg"><symbol id="icon-search" viewBox="0 0 18 18"><path fill="currentColor" d="M12.8 11.4c.9-1.2 1.4-2.7 1.4-4.3C14.2 3.2 11 0 7.1 0S0 3.2 0 7.1c0 3.9 3.2 7.1 7.1 7.1 1.6 0 3.1-.5 4.3-1.4l5.2 5.2 1.4-1.4-5.2-5.2zM2 7.1C2 4.3 4.3 2 7.1 2s5.1 2.3 5.1 5.1-2.3 5.1-5.1 5.1S2 9.9 2 7.1z"/></symbol><symbol id="icon-view-default" viewBox="0 0 18 18"><g fill="currentColor"><path d="M8 11H0V0h8v11zM18 5h-8V0h8v5zM8 18H0v-5h8v5zM18 18h-8V7h8v11z"/></g></symbol><symbol id="icon-view-alt" viewBox="0 0 18 18"><g fill="currentColor"><path d="M8 8H0V0h8v8zM18 8h-8V0h8v8zM8 18H0v-8h8v8zM18 18h-8v-8h8v8z"/></g></symbol><symbol id="icon-view-full" viewBox="0 0 18 18"><g fill="currentColor"><path d="M18 8H0V0h18v8zM18 18H0v-8h18v8z"/></g></symbol><symbol id="icon-facebook" viewBox="0 0 9 19"><path fill="currentColor" d="M6.7 3.6H9V0H6.3c-3.3.1-4 2.1-4 4.1V6H0v3.5h2.2V19h3.4V9.5h2.8L8.9 6H5.6V4.9c0-.7.4-1.3 1.1-1.3z"/></symbol><symbol id="icon-twitter" viewBox="0 0 17.9 15"><path fill="currentColor" d="M17.9 1.8c-.3.2-1.2.5-2 .6.5-.3 1.3-1.3 1.5-2.1-.5.3-1.7.8-2.3.8C14.4.5 13.5 0 12.4 0c-2 0-3.7 1.7-3.7 3.8 0 .3 0 .6.1.8-2.8-.1-6-1.5-7.8-4-1.1 2-.2 4.2 1.1 5-.4 0-1.2-.1-1.6-.4 0 1.3.6 3.1 2.9 3.7-.5.4-1.3.3-1.6.3.1 1.1 1.6 2.6 3.3 2.6-.6.7-2.6 2-5.1 1.6 1.7 1 3.7 1.6 5.8 1.6 6 0 10.6-5 10.3-11.1v-.1c.6-.4 1.3-1.1 1.8-2z"/></symbol><symbol id="icon-pinterest" viewBox="0 0 18 18"><path fill="currentColor" d="M2.3 0h13.4C17 0 18 .9 18 2.3v13.4c0 1.4-1 2.3-2.3 2.3H2.3C1 18 0 17.1 0 15.7V2.3C0 .9 1 0 2.3 0zm10.8 2c-.4 0-.8.4-.8.8v1.9c0 .4.4.8.8.8h2c.4 0 .8-.4.8-.8V2.8c.1-.4-.3-.8-.8-.8h-2zM16 7.6h-1.6c.2.5.2 1 .2 1.5 0 3-2.5 5.4-5.6 5.4-3.1 0-5.6-2.4-5.6-5.4 0-.5.1-1.1.2-1.5H2v7.6c0 .4.3.7.7.7h12.5c.4 0 .7-.3.7-.7l.1-7.6zM9 5.5C7 5.5 5.4 7 5.4 9c0 1.9 1.6 3.5 3.6 3.5s3.6-1.6 3.6-3.5c0-2-1.6-3.5-3.6-3.5z"/></symbol><symbol id="icon-tumblr" viewBox="0 0 11 19"><path fill="currentColor" d="M8.6 15.9c-1.6 0-2.3-1.3-2.3-2.2V7.8h4.1v-3h-4V.1S4.7 0 3.5 0C3.5 4.1 0 4.9 0 4.9v2.8h2.3v7C2.3 17.1 4.4 19 7 19s4-1.1 4-1.1v-3.1c0 .1-.8 1.1-2.4 1.1z"/></symbol><symbol id="icon-menu" viewBox="0 0 274.5 224.5"><g fill="currentColor"><path d="M274 53.8H.5V.5H274v53.3zM274 138.9H.5V85.6H274v53.3zM274 224H.5v-53.3H274V224z"/></g></symbol><symbol id="bar" viewBox="0 0 274.5 54.3"><path fill="currentColor" d="M274 53.8H.5V.5H274v53.3z"/></symbol><symbol id="arrow" viewBox="0 0 38 14"><path fill="currentColor" d="M31 0l-1.4 1.4L34.2 6H0v2h34.2l-4.6 4.6L31 14l7-7-7-7z"/></symbol><symbol id="refresh" viewBox="0 0 19 16"><path fill="currentColor" d="M9.4 6.2l5.9 4.4L19 4.4l-1.7-1L15.7 6c-.9-3.5-4-6-7.7-6-4.4 0-8 3.6-8 8s3.6 8 8 8c2.1 0 4.1-.8 5.6-2.3l-1.4-1.4C11.1 13.3 9.6 14 8 14c-3.3 0-6-2.7-6-6s2.7-6 6-6c3 0 5.4 2.2 5.9 5l-3.3-2.4-1.2 1.6z"/></symbol></svg>

Now on a html template which I found on the net the images are shown with :

  <svg>
      <use xlink:href="assets/img/symbols.svg#bar"></use>
    </svg>

How can I make this work on rails.

I tried :

 <use xlink:href="images/symbols.svg#bar"></use>

and the svg-inline gem

but on both I see no images on my webpage.

Can anyone help me figure out how to make this work ?

Unexpressed answered 2/5, 2016 at 16:14 Comment(0)
C
44

Displaying SVGs (scalable vector graphics) in a Rails app

Specifically include the SVG filetype in asset compilation

production.rb 

  config.assets.precompile += %w( '.svg' )  

  # Must include to get inline SVGs to work in deploy
  config.assets.css_compressor = :sass

Create a helper to display the SVG

myhelper.rb

def show_svg(path)
  File.open("app/assets/images/#{path}", "rb") do |file|
    raw file.read
  end
end

Invoke the SVG processing helper in your view

<%= show_svg('symbols.svg') %>

From your question I'm not 100% clear about your implementation, but these steps should get you to the point where your SVG image is visible. Once you have the SVG visible, you can apply CSS.

Creek answered 2/5, 2016 at 22:16 Comment(5)
what if you have an svg sprite of icons? and in development environment?Diaconate
@StevenAguilar I'd suggest you post this as a new question. I'm not doing RoR much these days.Creek
this does not seem to work with SVGs uploaded using active storageImplode
Someone edited the answer; check with them if they broke it :(.Creek
Why css_compressor should be set to :sass? That's an CSS setting.Fannyfanon
H
11

Try the inline_svg gem, it adds a helper method for in-lining SVG from a file directly into your template

Hilversum answered 7/8, 2017 at 20:37 Comment(0)
C
9

You can insert svg inline in Rails without any additional gems, using something like this:

<%= render inline: Rails.root.join('public/icon.svg').read %>

If you need clicking the svg to do something, like make a POST request, wrap the entire thing in a link_to with a do, like this:

<%= link_to some_path(@something), method: :post do %>
    <%= render inline: Rails.root.join('public/icon.svg').read %>
<% end %>
Carroll answered 21/5, 2021 at 3:28 Comment(0)
H
7

You can display SVGs in the same way as images.

For example in haml:

%img{:src => "/images/image1.svg"}

hope it helps!

Hanafee answered 9/4, 2017 at 19:30 Comment(2)
True, this works in Chrome, Firefox and Edge (latest versions as of today). But Safari won't display svg images served by the rails img helper.Forepleasure
(From memory) I think Safari 15 corrected this.Serilda
S
4

I've been trying different solutions for this, and ultimately landed on just putting the svg doc in an html.erb partial. Since svg is valid html, it works fine to just move it from assets to views.

<%# app/views/home/index.html.erb %>

<%= render partial: "svgs/some_svg.html.erb"

Given

app
| views
| | home
| | | index.html.erb
| | svgs
| | | some_svg.html.erb
Sugar answered 5/3, 2020 at 3:33 Comment(0)
D
3

Similar to nvm's method, I have a helper to help build the asset path for the use tag and ensure the parent svg tag has appropriate attributes

def external_svg(identifier, attributes = {})
  # identifier: <file name without extension>#<fragment id>
  file_name, fragment = identifier.split('#')
  file_name += '.svg'
  attributes.merge!(xmlns: 'http://www.w3.org/2000/svg')
  content_tag :svg, attributes do
    tag.use href: "#{image_path(file_name)}##{fragment}"
  end
end

In a view your SVG asset would be called thus: external_svg 'symbols#icon-search', class: 'icon', with the resulting html:

<svg xmlns="http://www.w3.org/2000/svg" class="icon">
  <use href="<asset pipeline path>/symbols.svg#icon-search" />
</svg>

As externally referenced SVGs are now well supported, you shouldn't need any SVG specific gems for this to work.

Worth noting the advantages (caching) and disadvantages (CSS styling limitations) when using this approach. See this Twitter thread

Doublespace answered 29/7, 2021 at 3:15 Comment(0)
P
2

For ActiveStorage based on Elvn response I've found that there's a better way

If you have an item object with a has_one_attached :logo on it:

<%= show_svg(item.logo_blob) %>

Then the helper is:

    def show_svg(blob)
      blob.open do |file|
        raw file.read
      end
    end
Pyrology answered 23/4, 2021 at 2:56 Comment(2)
this works for me, for the activestorage with minio! thanks!Carabin
How would I go about adding classes to this?Elater
D
1

Its straightforward:

First, add the svg to the asset precompile list in assets.rb

application.config.assets.precompile +=
    %w(<your other files> symbol-defs.svg)

Then reference it in your erb/haml files as shown below:

<svg class="icon gr-menu">
  <use xlink:href="<%= asset_path('symbol-defs') %>#gr-menu"></use>
</svg>
Doggy answered 29/1, 2017 at 20:48 Comment(1)
Thanks @nvn. This works to some extent, but there are limitations, as detailed at css-tricks.com/svg-use-with-external-reference-take-2Gaily
J
1

Create a helper: (content is different in each case, custom your own one)

def icon(icon, css_class: "")
  content_tag(:svg, class: "icon icon_#{icon} #{css_class}") do
      content_tag(:use, nil, 'xlink:href' => "#icon_#{icon}")
  end
end

Use it like this:

<%= icon 'arrow-menu' , css_class: 'arrow-breadcrumb' %>
Jacinda answered 9/3, 2018 at 14:39 Comment(0)
K
0

If you can use inline_svg gem. It's old but still valid solution well maintained

If you cannot (e.g. company policy) here is what the gem does in nutshell:

# app/helpers/svg_helper.rb
module SvgHelper
  SVGFileNotFoundError = Class.new(StandardError)

  def inline_svg_tag(path, options = {})
    path = Rails.root.join("app/assets/images/#{path}.svg")
    File.exist?(path) || raise(SVGFileNotFoundError, "SVG icon file does not exist: #{path}")
    svg_file_content = File.binread(path)

    if options.any?
      doc = Nokogiri::XML::Document.parse(svg_file_content)
      svg = doc.at_css("svg")
      svg["height"] = options[:height] if options[:height]
      svg["width"] = options[:width] if options[:width]
      svg["class"] = options[:class] if options[:class]
      svg_file_content = doc.to_html.strip
    end

    raw svg_file_content
  end
end

Full article + RSpec code at https://blog.eq8.eu/til/inline-svg-in-ruby-on-rails.html

Kowal answered 15/1, 2024 at 17:49 Comment(0)
P
-1

I really don't know how parse the SVG files; but this same thing can be use to the SVG files. You can then use the asset_path helper in the SVG file; but the catch is by default rails parse CSS, JS and ERB. In that way the link inside the SVG can work with the asset pipeline. Maybe if you try to change the name from 'symbols.svg' to 'symbols.svg.erb' it will parse the file to get the correct url.

You problem is that the asset pipeline is not be used in accord to rails.

and this is how you could parse (preprocess) svg files:

The default matcher for compiling files includes application.js, application.css and all non-JS/CSS files (this will include all image assets automatically) from app/assets folders including your gems: [ Proc.new { |filename, path| path =~ /app/assets/ && !%w(.js .css).include?(File.extname(filename)) }, /application.(css|js)$/ ]

More info about the helpers methods

2.3.2 CSS and Sass

When using the asset pipeline, paths to assets must be re-written and sass-rails provides -url and -path helpers (hyphenated in Sass, underscored in Ruby) for the following asset classes: image, font, video, audio, JavaScript and stylesheet.

image-url("rails.png") becomes url(/assets/rails.png)
image-path("rails.png") becomes "/assets/rails.png".

The more generic form can also be used:

asset-url("rails.png") becomes url(/assets/rails.png)
asset-path("rails.png") becomes "/assets/rails.png"

2.3.3 JavaScript/CoffeeScript and ERB

If you add an erb extension to a JavaScript asset, making it something such as application.js.erb, you can then use the asset_path helper in your JavaScript code: $('#logo').attr({ src: "<%= asset_path('logo.png') %>" });

This writes the path to the particular asset being referenced.

Similarly, you can use the asset_path helper in CoffeeScript files with erb extension (e.g., application.js.coffee.erb): $('#logo').attr src: "<%= asset_path('logo.png') %>"

You can check: http://guides.rubyonrails.org/asset_pipeline.html#coding-links-to-assets

Probably answered 2/5, 2016 at 16:52 Comment(2)
Thanks, I tried what you said : <use xlink:href= <%= asset_url("images/symbols.svg.erb#bar") %>></use> but still no icon. It looks to me that rails cannot handle the #bar partUnexpressed
makes no difference. What I said earlier. It's looks to me that the #bar cannot be handledUnexpressed

© 2022 - 2025 — McMap. All rights reserved.