# How do you integrate inline SVGs?

**URL:** <https://discourse.elm-lang.org/t/how-do-you-integrate-inline-svgs/1047>\
**Category:** Request Feedback\
**Created:** [March 28, 2018, 11:29pm UTC](https://discourse.elm-lang.org/t/how-do-you-integrate-inline-svgs/1047 "2018-03-28T23:29:23Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![Sebastian](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/sebastian/32/98_2.png) [@Sebastian](https://discourse.elm-lang.org/u/Sebastian)\
**Post date:** [March 28, 2018, 11:29pm UTC](https://discourse.elm-lang.org/t/how-do-you-integrate-inline-svgs/1047/1 "2018-03-28T23:29:23Z")

</div>

We use lots of SVGs for icons in our application. They have to be inline svgs so we can style them using CSS.

e.g. `<div><svg>...</svg></div>`

SVG as image source cannot be styled e.g. `<img src="logo.svg" />`

Our current process is to get an SVG from our designer and convert it to Elm code using [https://mbylstra.github.io/html-to-elm/](https://mbylstra.github.io/html-to-elm/) but I’m not happy with this because:

- There is this extra step of converting and cleaning the SVGs
- We cannot give this converted svgs back to our designer for modification, we need to keep a parallel library of icons in SVG format

So I’m wondering if anyone knows of a better process for working with SVGs in Elm, anything that we don’t know about?

---

<div class="post-metadata">

**Author:** ![Dan\_Abrams](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/dan_abrams/32/3171_2.png) [@Dan\_Abrams](https://discourse.elm-lang.org/u/Dan_Abrams)\
**Post date:** [March 28, 2018, 11:51pm UTC](https://discourse.elm-lang.org/t/how-do-you-integrate-inline-svgs/1047/2 "2018-03-28T23:51:25Z")

</div>

I love using SVGs too. Converting to Elm is great if you have to do dynamic things to them, but you can just use Svg [use](http://package.elm-lang.org/packages/elm-lang/svg/2.0.0/Svg#use).

I’ve made an Ellie that demonstrates [here](https://ellie-app.com/9v44hbR8wa1/0).

---

<div class="post-metadata">

**Author:** ![pateh](https://avatars.discourse-cdn.com/v4/letter/p/e5b9ba/32.png) [@pateh](https://discourse.elm-lang.org/u/pateh)\
**Post date:** [March 29, 2018, 12:51am UTC](https://discourse.elm-lang.org/t/how-do-you-integrate-inline-svgs/1047/3 "2018-03-29T00:51:46Z")

</div>

I think @luke has a great method for this in Ellie, check out [https://github.com/lukewestby/ellie/blob/master/client/src/Ellie/Ui/Icon.elm](https://github.com/lukewestby/ellie/blob/master/client/src/Ellie/Ui/Icon.elm)

There is a union type of all of the available icons, and then they are injected into the page [https://github.com/lukewestby/ellie/blob/master/client/src/Ellie/Ui/Icon/Loader.js](https://github.com/lukewestby/ellie/blob/master/client/src/Ellie/Ui/Icon/Loader.js)

In the view you end up with something like `div [Styles.infoIcon] [Icon.view Icon.Tag]`

---

<div class="post-metadata">

**Author:** ![Sebastian](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/sebastian/32/98_2.png) [@Sebastian](https://discourse.elm-lang.org/u/Sebastian)\
**Post date:** [March 29, 2018, 5:13am UTC](https://discourse.elm-lang.org/t/how-do-you-integrate-inline-svgs/1047/4 "2018-03-29T05:13:07Z")

</div>

Using `xlinkHref` looks like a really nice solution, thanks. will try this out.

---

<div class="post-metadata">

**Author:** ![dmy](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/dmy/32/702_2.png) [@dmy](https://discourse.elm-lang.org/u/dmy)\
**Post date:** [March 29, 2018, 7:21am UTC](https://discourse.elm-lang.org/t/how-do-you-integrate-inline-svgs/1047/5 "2018-03-29T07:21:58Z")

</div>

I concur.

I use the same method as @luke to display them (`Svg.xlinkHref`). The slight difference is that I build an svg sprite using [svg-sprite-loader](https://github.com/kisenka/svg-sprite-loader) that is chained with [svgo-loader](https://github.com/rpominov/svgo-loader) to optimize it (for Ellie, [svg-inline-loader](https://github.com/webpack-contrib/svg-inline-loader) is used).

My webpack3 config looks like:

```auto
      {
        test: /\.svg$/,
        use: [
          {
            loader: 'svg-sprite-loader',
            options: {}
          },
          'svgo-loader'
        ]
      }

```

I just added this in my `app.js` to generate and load the sprite:

```auto
// Build SVG sprite from icons
var req = require.context('./icons', true, /\.svg$/);
req.keys().forEach(req);

```

So to add an icon, I just copy it to the `/icons` directory, add a value to my `Icon` union type and a `case` branch to my `Icon.view` function, and that’s it (I could even use `toString` on the union type but I’m not sure how this will work in 0.19). It works great.

Edit: the main drawbacks are that the full sprite is always inserted in the page uncached, that unused icons are not removed, and that styling/animating is more limited than with inline SVGs, see the discussion below.

---

<div class="post-metadata">

**Author:** ![edkv](https://avatars.discourse-cdn.com/v4/letter/e/439d5e/32.png) [@edkv](https://discourse.elm-lang.org/u/edkv)\
**Post date:** [March 29, 2018, 10:21am UTC](https://discourse.elm-lang.org/t/how-do-you-integrate-inline-svgs/1047/6 "2018-03-29T10:21:28Z")

</div>

I remember there were some issues with styling SVGs that are embedded with the `use` element. For example if you have the following in your document:

```auto
<svg>
  <use xlink:href="#my-icon"></use>
<svg>

```

you can’t easily target an individual `path` inside that icon with CSS. There are some workarounds like using CSS variables, but I find it easier to just use inline SVG. I’m personally okay with converting icons to Elm by hand every time they are changed. In my experience that doesn’t happen very often.

---

<div class="post-metadata">

**Author:** ![kevinyank](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/kevinyank/32/241_2.png) [@kevinyank](https://discourse.elm-lang.org/u/kevinyank)\
**Post date:** [March 29, 2018, 12:45pm UTC](https://discourse.elm-lang.org/t/how-do-you-integrate-inline-svgs/1047/7 "2018-03-29T12:45:47Z")

</div>

At Culture Amp we use a custom Webpack loader based on [NoRedInk/elm-assets-loader](https://github.com/NoRedInk/elm-assets-loader), which we’d be happy to open-source if it would be useful (or I can just post it in a Gist – there’s not a lot to it) and [svg-sprite-loader](https://github.com/kisenka/svg-sprite-loader).

The result is that any SVGs that are referenced in our Elm app are pulled into our Webpack build. At runtime, JavaScript injects all of the icons into the document as a hidden collection of SVG symbols at the start of the document `body`. The SVGs in our views are rendered as `<svg><use xlink:href="#unique-symbol-id"/></svg>`, which can be styled with CSS. It works great!

---

<div class="post-metadata">

**Author:** ![kevinyank](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/kevinyank/32/241_2.png) [@kevinyank](https://discourse.elm-lang.org/u/kevinyank)\
**Post date:** [March 29, 2018, 12:53pm UTC](https://discourse.elm-lang.org/t/how-do-you-integrate-inline-svgs/1047/8 "2018-03-29T12:53:03Z")

</div>

Yes, the ability to style icons referenced in this way is somewhat limited. You can style the `svg` element with two properties: `color` and `fill`. The value of `color` can then be used inside the SVG as `currentColor` (we have our SVG optimiser – another webpack loader – configured to replace solid black with `currentColor`).

The value of `fill` is inherited by all descendant SVG elements and therefore can technically be used to skin the SVG too; however, our SVG authoring tool (Sketch) exports SVGs with a wrapper element, `<g fill="none">`, that prevents this `fill` property from propagating usefully into nested elements, so right now we’re unable to use this technique to style a second colour; however, we’ve found that a single stylable colour is all we need.

---

<div class="post-metadata">

**Author:** ![edkv](https://avatars.discourse-cdn.com/v4/letter/e/439d5e/32.png) [@edkv](https://discourse.elm-lang.org/u/edkv)\
**Post date:** [March 29, 2018, 2:43pm UTC](https://discourse.elm-lang.org/t/how-do-you-integrate-inline-svgs/1047/9 "2018-03-29T14:43:42Z")

</div>

What are the benefits of using SVG sprites over inline SVG? Sprites are definitely useful for server-side rendered apps where inline SVG can increase the size of HTML documents, but it seems to me that if you’re rendering on the client, inline SVG is the most powerful (and also the easiest) way to manage icons.

---

<div class="post-metadata">

**Author:** ![dmy](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/dmy/32/702_2.png) [@dmy](https://discourse.elm-lang.org/u/dmy)\
**Post date:** [March 29, 2018, 5:49pm UTC](https://discourse.elm-lang.org/t/how-do-you-integrate-inline-svgs/1047/10 "2018-03-29T17:49:26Z")

</div>

SVG sprites can still benefit from browser caching if they are used with an external reference (which is not what I do in my previous example), however IE browsers do not support this natively, so this requires using SVG For Everybody which is not perfect or some AJAX trickery.

Inline SVGs are clearly more powerful for styling and almost mandatory for animating them.

So overall I would say you’re right, but once you have a webpack config in place, I find it easier to add or update icons than with inline SVGs. Or is there a way to inject them automatically inline during build in elm generated code (including an svgo processing pass)?

Edit: it would certainly be possible to do this by modifying `elm-assets-loader` (most likely close to what @kevinyank described, but injecting directly the SVG after some processing instead of generating a sprite, but I don’t know a tool that already does it.)

---

<div class="post-metadata">

**Author:** ![Dan\_Abrams](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/dan_abrams/32/3171_2.png) [@Dan\_Abrams](https://discourse.elm-lang.org/u/Dan_Abrams)\
**Post date:** [March 29, 2018, 5:58pm UTC](https://discourse.elm-lang.org/t/how-do-you-integrate-inline-svgs/1047/11 "2018-03-29T17:58:05Z")

</div>

Would better tooling solve this? A Sketch plug-in to output elm svg for instance?

---

<div class="post-metadata">

**Author:** ![dmy](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/dmy/32/702_2.png) [@dmy](https://discourse.elm-lang.org/u/dmy)\
**Post date:** [March 29, 2018, 10:19pm UTC](https://discourse.elm-lang.org/t/how-do-you-integrate-inline-svgs/1047/12 "2018-03-29T22:19:39Z")

</div>

Thinking more about it, `elm-assets-loader` should be able to load the file and inject it in the elm generated code using `svg-inline-loader`. I have never tried because I use webpack3 and it is not yet compatible with it.

But I gave it a try using a webpack2 elm test project and it works, and chaining through `svgo-loader` works also.

Here is my webpack2 config part:

```auto
            rules: [{
                test: /\.elm$/,
                exclude: [/elm-stuff/, /node_modules/],
                use: [
                    {
                        loader: 'elm-assets-loader',
                        options: {
                            module: 'My.Assets',
                            tagger: 'AssetPath',
                            package: 'test/test'
                        }
                    },
                    'elm-webpack-loader'
                ]
            },
            {
                test: /\.svg$/i,
                use: [{
                    loader: 'svg-inline-loader',
                },
                {
                    loader: 'svgo-loader',
                    options: {
                        plugins: [
                            { removeTitle: true },
                            { convertColors: { shorthex: false } },
                            { convertPathData: false }
                        ]
                    }
                }]
            }]
        }

```

My `My.Assets` module is identical to the one in the elm-assets-loader documentation:

```auto
module My.Assets exposing (AssetPath(..), path, star)

import Html exposing (..)

type AssetPath
    = AssetPath String

star : AssetPath
star =
    AssetPath "./star.svg"

path : AssetPath -> String
path (AssetPath str) =
    str

```

and my test view is:

```auto
textHtml : String -> Html msg
textHtml t =
    div
        [ Json.Encode.string t
            |> Html.Attributes.property "innerHTML"
        , style
            [ ( "width", "24px" )
            , ( "height", "24px" )
            ]
        ]
        []

view : Model -> Html Msg
view model =
    div []
        [ textHtml <| My.Assets.path <| My.Assets.star
        , text "hello"
        ]

```

This part is a little dirty to be able to inject the `svg` node, but I don’t know a better way without modifying `elm-assets-loader`. However this most likely won’t work with Elm 0.19 given the recent patch that forbids `innerHTML`, is there another way?

Still, this works fine, the svg file is processed through `svgo` then correctly inserted, I get:

```auto
<div id="main">
    <div>
        <div style="width: 24px; height: 24px;">
            <svg xmlns="http://www.w3.org/2000/svg">
                <path d="..."></path>
            </svg>
        </div>hello</div>
</div>

```

Pretty cool!

Now if only `elm-assets-loader` was compatible with webpack3, but maybe someone has began to update it?

PS: I’m not sure why @luke did not use this as this is a NoRedInk project.

Edit: I think the proper way to avoid injecting the SVG node using the `innerHTML` property in Elm would be to modify `elm-assets-loader` to make it supporting tagged `Svg msg` values in addition to `String`. In this case it would use `innerHTML` itself in javascript. I don’t think this would be too dangerous as this is done during the build.

Edit2: there is a fork updated for webpack3/4: [https://github.com/alvivi/elm-assets-loader](https://github.com/alvivi/elm-assets-loader)

---

<div class="post-metadata">

**Author:** ![luke](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/luke/32/709_2.png) [@luke](https://discourse.elm-lang.org/u/luke)\
**Post date:** [March 30, 2018, 12:40am UTC](https://discourse.elm-lang.org/t/how-do-you-integrate-inline-svgs/1047/13 "2018-03-30T00:40:43Z")

</div>

> [@dmy](#):
>
> PS: I’m not sure why @luke did not use this as this is a NoRedInk project.

We stopped using Webpack at NRI about a year ago (a little before I joined), so this is actually the first time I’ve seen this loader! The method I chose is primarily driven by a desire to let the browser cache the contents of the SVG sprite since I almost never change it even if I update the application code. I didn’t really see much utility in going through Elm to inject the SVG document into the DOM, and I skipped SVGO because I find it to be difficult to configure and I can do a reasonable job by hand.

At NRI we have a Python script that crawls a directory of SVG files and optimizes them with SVGO and combines them into a string that gets embedded into a JS file. The JS file injects the SVG content into the DOM, and we load the JS file asynchronously. This works well with our larger team and our typical strategy of downloading individual SVG files for icons that were originally added through Sketch by our design team. The Elm API looks a lot like the one in Ellie, except that there’s more stuff in it to account for legacy `img` tag icons and to provide correct accessibility markup for different usecases.

I’ve been planning the next iteration of the icon system for both Ellie and for NRI, and I think we’re going to take [Chris Coyier’s most recent advice on the topic](https://css-tricks.com/pretty-good-svg-icon-system/) and just use inline SVG via elm-lang/svg. The SVG 2.0 spec indicates that `use` transcludes the content at the `href` via shadow DOM, which makes advanced styling stuff really frustrating. Plus, our designers edit Elm files all the time so making updates isn’t an issue.

---

<div class="post-metadata">

**Author:** ![dmy](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/dmy/32/702_2.png) [@dmy](https://discourse.elm-lang.org/u/dmy)\
**Post date:** [March 30, 2018, 6:20am UTC](https://discourse.elm-lang.org/t/how-do-you-integrate-inline-svgs/1047/14 "2018-03-30T06:20:22Z")

</div>

Thank you very much for this detailed answer and your insight.

> [@luke](#):
>
> We stopped using Webpack at NRI about a year ago (a little before I joined), so this is actually the first time I’ve seen this loader!

Maybe a transfer of `elm-assets-loader` to `elm-community` could be discussed if there is some interest there as it could still be very useful for those that use webpack (including to take Chris Coyier’s advice as can be seen)?

This could maybe make easier to get it updated to webpack3/4 and improved through contributions (edit: for example to merge with [GitHub - alvivi/elm-assets-loader: webpack loader for webpackifying asset references in Elm code](https://github.com/alvivi/elm-assets-loader) that is compatible with webpack3/4).

The only thing I am questioning in his advice is when he says:

> [@](#):
>
> #Advantage #1: No Build Process
> 
> You need no fancy tooling to make this work

It seems to me that this relies in his example on using PHP `<?php include("file.svg"); ?>` which is not available in all languages (maybe one day the Elm compiler will be able to parse directly SVG files?), and this eludes the question of the often needed processing after designers delivery.

Has anyone considered writing a python or node.js script that takes every SVG file in a directory, processes them through some configurable steps (typically svgo), then generates directly an Elm module (using `html-to-elm` or some kind of variation)?

PS: @Dan_Abrams, doing this through a Sketch plugin seems to restrict the usefulness unnecessarily.

---

<div class="post-metadata">

**Author:** ![dmy](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/dmy/32/702_2.png) [@dmy](https://discourse.elm-lang.org/u/dmy)\
**Post date:** [March 30, 2018, 9:41am UTC](https://discourse.elm-lang.org/t/how-do-you-integrate-inline-svgs/1047/15 "2018-03-30T09:41:11Z")

</div>

To complete my previous experimentation, I used [elm-svg-parser](http://package.elm-lang.org/packages/rnons/elm-svg-parser/latest) instead of the `innerHTML` property and this works fine (and should still work with 0.19 I think). I just replaced the view code by:

```auto
view : Model -> Html Msg 
view model =
    div []
        [ case SvgParser.parse <| My.Assets.path <| My.Assets.star of
            Ok svg ->
                svg 

            Err error ->
                text ("invalid svg: " ++ error)
        , text "hello"
        ]   

```

Of course the `Result` handling could be elsewhere, this is just a test.

So to answer initial @Sebastian’s question:

1. You can use today [elm-assets-loader](https://github.com/NoRedInk/elm-assets-loader), chain through [svg-inline-loader](https://github.com/webpack-contrib/svg-inline-loader) and [svgo-loader](https://github.com/rpominov/svgo-loader) to clean the SVG and have the SVG inlined properly with [elm-svg-parser](http://package.elm-lang.org/packages/rnons/elm-svg-parser/latest) (if you need webpack3/4 compatibility, use [alvivi/elm-assets-loader](https://github.com/alvivi/elm-assets-loader)).

2. [elm-svg-loader](http://package.elm-lang.org/packages/rnons/elm-svg-loader/latest) seems to offer a similar solution without using `elm-assets-loader`.

3. Use an SVG sprite solution with the drawbacks listed before.

Edit: I think that a script that processes all SVG from a directory through a configurable step (svgo), then generates an Elm module might be simpler than all of those, but I don’t know one already developed.

---

<div class="post-metadata">

**Author:** ![system](https://canada1.discourse-cdn.com/flex035/uploads/elm_lang/original/1X/50a05e53677a2c3b47776d7abd0f113eb50193a1.png) [@system](https://discourse.elm-lang.org/u/system)\
**Post date:** [April 29, 2018, 9:47am UTC](https://discourse.elm-lang.org/t/how-do-you-integrate-inline-svgs/1047/16 "2018-04-29T09:47:43Z")

</div>

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.
