Skip to content

Instantly share code, notes, and snippets.

@tony-rowan
Last active May 10, 2026 21:04
Show Gist options
  • Select an option

  • Save tony-rowan/a9d2b61db134c573b4cd644b66a27d4b to your computer and use it in GitHub Desktop.

Select an option

Save tony-rowan/a9d2b61db134c573b4cd644b66a27d4b to your computer and use it in GitHub Desktop.
A fully encapsulated component to handle inlining SVGs
# frozen_string_literal: true
class IconComponent < ViewComponent::Base
class IconNotFoundError < StandardError; end
attr_reader :icon, :size, :options
# Chnage default size class based on your project
def initialize(icon, size: "size-6", **options)
@icon = icon
@size = size
@options = options
end
def call
return render_icon_not_found if icon_not_found?
svg = parsed_svg
set_classes!(svg)
set_aria_attributes!(svg, options[:aria])
set_data_attributes!(svg, options[:data])
set_base_attributes!(svg, base_attributes)
set_role!(svg)
svg.to_xml.strip.html_safe
end
private
def icon_name
icon.to_s
end
def class_list
[size, options[:class]].compact_blank.join(" ")
end
def base_attributes
options.except(:class, :aria, :data)
end
def set_classes!(svg)
svg.set("class", (class_list.split + ["icon-#{icon_name.dasherize}"]).join(" "))
end
def set_aria_attributes!(svg, aria_attributes)
aria_attributes&.each do |attribute, value|
svg.set("aria-#{attribute.to_s.parameterize(preserve_case: true).dasherize}", value)
end
end
def set_data_attributes!(svg, data_attributes)
data_attributes&.each do |attribute, value|
svg.set("data-#{attribute.to_s.parameterize(preserve_case: true).dasherize}", value)
end
end
def set_base_attributes!(svg, attributes)
attributes&.each do |attribute, value|
svg.set(attribute.to_s.parameterize(preserve_case: true).dasherize, value)
end
end
def set_role!(svg)
svg.set("role", svg.get("role").presence || (svg.get("aria-label").blank? ? "presentation" : "img"))
end
def icon_not_found?
!File.exist?(icon_asset_path)
end
def render_icon_not_found
if Rails.env.local?
raise IconNotFoundError, "Could not find icon: #{icon_name}"
else
"(Icon Not Found)"
end
end
def parsed_svg
Oga.parse_xml(File.read(icon_asset_path)).at_css("svg")
end
# Update this to suit your project
def icon_asset_path
@_icon_asset_path ||= Rails.root.join("app/assets/images/icons/#{icon_name}.svg").to_s
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment