Dynamic coloring of SVG files

Hello, community!

I’ve been trying to find a better way of dynamically change the colors of an SVG. The ideia here is to keep those SVGs inside SVG files instead of having N components for each SVG. Now what I’ve been using is creating a component that receives name and color as a prop and renders the SVG inline for that name (imagine how many lines this component has while increasing the number of SVGs in my project). But I don’t know if that’s the best way of doing this, at least I haven’t found another way. How you guys are doing this? Do you think my approach is a good one?

P.S.: Every other topic close to this one is some years old, and didn’t fit my goal. Wonder if there’s any new library that handle this scenario.

1 Like

How about simply use CSS variable names instead? This way all you do is to read SVG file by it’s name and render it as-is in the template. That requires only few lines of code …

1 Like

Just putting the CSS color variable name in the SVG fill attribute inside the SVG file doesn’t work, when it imports the SVG, it comes as a static resource. Does it work for you?

Depending on the app and browser the support may be different, but in short you can even add style tag with you custom CSS and support for the @media queries, so you are able to add dark and light versions of same icon.

In Phoenix project you have to declare the variable in your CSS or SCSS file and use it within your SVG file var(--my-css-var-name). This needs to be rendered within DOM as favicon or img does not support “cross-document” styles. However you can still declare said CSS variables in the SVG file and then use as favicon or img.

1 Like

How about embedding your SVG in a HEEx template file? You can then import it as a component inside your liveviews or controllers with embed_templates/2 and these components can accept assigns. embed_templates docs

Example:

lib/my_app_web/components/icon.svg.heex

<svg class={["size-6", @color]} stroke="currentColor" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5">
  <path stroke-linecap="round" stroke-linejoin="round" d="M4.26 10.147a60.438 60.438 0 0 0-.491 6.347A48.62 48.62 0 0 1 12 20.904a48.62 48.62 0 0 1 8.232-4.41 60.46 60.46 0 0 0-.491-6.347m-15.482 0a50.636 50.636 0 0 0-2.658-.813A59.906 59.906 0 0 1 12 3.493a59.903 59.903 0 0 1 10.399 5.84c-.896.248-1.783.52-2.658.814m-15.482 0A50.717 50.717 0 0 1 12 13.489a50.702 50.702 0 0 1 7.74-3.342M6.75 15a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5Zm0 0v-3.675A55.378 55.378 0 0 1 12 8.443m-7.007 11.55A5.981 5.981 0 0 0 6.75 15.75v-1.5" />
</svg>

lib/my_app_web/live/home_live.ex

defmodule MyAppWeb.HomeLive do
  use MyAppWeb, :live_view

  embed_templates("*.svg", root: Application.app_dir(:my_app, "lib/my_app_web/components", suffix: "_svg")

  def render(assigns) do
    ~H"""
    <.icon_svg color="text-green-500" />
    """
  end

  # ...

end

Notice the use of the @color assign, along with stroke="currentColor" on the SVG.

4 Likes

Just had to find the correct way of setting the root path, but this worked great! Thanks a lot for this!

1 Like

Glad this helped! And I forgot to mention it in my first post, but welcome to the forum :slight_smile:

2 Likes