Using SVG's in Twig Templates

Published: Feb 25 2022

A good approach to using your SVG's in a Symfony App while preserving the ability to manipulate them with CSS.

There are a number of ways to implement SVG's, just checkout this article on CSS Tricks for an in-depth look into SVG's. The easiest way that I have found to implement SVG's is to use the SVG code directly in my HTML templates. This keeps strict Content Security Policy's happy, preserves the ability to cache the assets, and more importantly - allows you to manipulate the SVG's with CSS while keeping your code DRY. Which leads me to my next point, the criteria I have for using SVG's.

  • Ability to manipulate the SVG with CSS
  • Maintain Strict Content Security Policy's
  • Easy Maintenance
  • Provide a good experience to my viewers

As mentioned in the linked CSS Tricks article, when implementing SVG's with an <image> tag or background-image: CSS property, you loose the ability to manipulate the SVG with CSS. So both of those methods are out the window for me. We could implement the SVG as an HTML <object> - but that would require us to use inline CSS to manipulate the image. That for me is a no-go because I like to keep my styling assets separate for my template assets. Lastly you can use data: URI's to embed your SVG's via <image>, background-image, or <object> tags. But that also requires you to implement a CSP Policy that allows data: as scheme source which is a very bad idea. It would create a potential attack vector for bad actors with malicious intent. Also using data URI's to embed your CSS adds more complexity to maintenance because now you have to base64 encode your SVG's.

So that brings me to inline SVG's. There are a couple of downsides to this approach but we'll talk more about that later. I've created a public repository of the source for this demo on GitHub rushlow-development/svg-in-templates. Feel free to check it out. Word to the wise, the demo app is just that - a demo. I would not attempt to use that code base in production without further modifications...

The SVG:

// templates/svg/user-profile.html.twig

<svg class="person-svg" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
    <path class="person-svg-background" d="M12 2C6.47715 2 2 6.47715 2 12C2 17.5228 6.47715 22 12 22C17.5228 22 22 17.5228 22 12C22 6.47715 17.5228 2 12 2Z"/>
    <path class="person-svg-head" d="M12.0001 6C10.3433 6 9.00012 7.34315 9.00012 9C9.00012 10.6569 10.3433 12 12.0001 12C13.657 12 15.0001 10.6569 15.0001 9C15.0001 7.34315 13.657 6 12.0001 6Z"/>
    <path class="person-svg-torso" d="M17.8948 16.5528C18.0356 16.8343 18.0356 17.1657 17.8948 17.4473C17.9033 17.4297 17.8941 17.4487 17.8941 17.4487L17.8933 17.4502L17.8918 17.4532L17.8883 17.46L17.8801 17.4756C17.874 17.4871 17.8667 17.5004 17.8582 17.5155C17.841 17.5458 17.8187 17.5832 17.7907 17.6267C17.7348 17.7138 17.6559 17.8254 17.5498 17.9527C17.337 18.208 17.0164 18.5245 16.555 18.8321C15.623 19.4534 14.1752 20 12.0002 20C8.31507 20 6.76562 18.4304 6.26665 17.7115C5.96476 17.2765 5.99819 16.7683 6.18079 16.4031C6.91718 14.9303 8.42247 14 10.0691 14H13.7643C15.5135 14 17.1125 14.9883 17.8948 16.5528Z"/>
</svg>

Style Sheet:

//assets/styles/app.scss

.small-profile {
    width: 75px;
    height: 75px;
}

.large-profile {
    width: 125px;
    height: 125px;
}

.signed-in {
    .person-svg {
        .person-svg-head,
        .person-svg-torso {
            fill: #c8f0b8;
        }
    }
}

.person-svg {
    .person-svg-background {
        fill: #4296FF;
    }

    .person-svg-head,
    .person-svg-torso
    {
        fill: #152C70;
    }
}

The Template:

// templates/base.html.twig

<body>
        ....
        <div class="small-profile">
            {{ include('svg/user-profile.html.twig') }}
        </div>
        <div class="large-profile">
            {{ include('svg/user-profile.html.twig') }}
        </div>
        <div class="large-profile signed-in">
            {{ include('svg/user-profile.html.twig') }}
        </div>
    </body>

The above code snippets will produce:

User Profile SVG's

As you can see we have 3 different elements that use the same SVG code but are rendered differently because of the CSS that was applied to each element. This is why I love using SVG's in my templates!

The downside to this approach? The SVG itself is roughly ~1KB in size which is not huge by any means. But imagine you have a very large table and you are wanting to display a status icon for each row. That 1KB SVG is being duplicated within the HTML for each row and therefor increasing the overall file size of the HTML document by 1KB per row. After a while, that can really bloat your overall over-the-wire bandwidth and potentially cause poor performance for your target audience. For the most part, I have not worried to much about this as that use case has not applied to my situation. But if it was something I was concerned about, I would probably look into a better cache-able solution - like converting the SVG's into actual PNG's and throwing them up on a CDN.