Search code examples
htmlreactjssvg

How to render an external SVG file with the <use> tag?


I am trying to do something which I thought was a very simple task, but I just can't make it work. I have exactly copied about 8 other examples I found online, like this one, but to no avail.

I am actually working on a React project, but to make the example easier, let's reduce the situation to just this:

I have a folder called svg containing svg.html and icon.svg.

svg.html contains:

<html>
<head></head>

<body>

<svg>  
<use href="icon.svg" />
</svg>
</body>
</html>

icon.svg contains:

<svg width="19" height="18" viewBox="0 0 19 18" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M16.5 0H2.5C1.4 0 0.5 0.9 0.5 2V16C0.5 17.1 1.4 18 2.5 18H16.5C17.6 18 18.5 17.1 18.5 16V2C18.5 0.9 17.6 0 16.5 0ZM2.5 16V2H8.5V16H2.5ZM16.5 16H10.5V9H16.5V16ZM16.5 7H10.5V2H16.5V7Z" fill="#9F9E99"/>
</svg>

When I run the html, simply nothing at all is displayed. What I want to happen, is for the icon.svg to be displayed in the html. What am I doing wrong?


Solution

  • You can store multiple icon assets in an external svg file.

    Create an icon asset file using <symbol> elements

    A common practice is to wrap your icons in a <symbol> elements.

    It is crucial to give all your icons/symbols a unique ID.

    Now you can reference each icon like so:

    <svg>  
      <use href="icon.svg#symbolID" />
    </svg>
    

    Overriding icon styles

    Besides, you should keep your icon styles/style attributes as neutral as possible – so remove fill or style attributes – otherwise style overriding (e.g. inheriting colors from text or changing fills on hover) gets more complicated.

    body{
      font-size: 2em;
      font-family: sans-serif
    }
    
    .icn-svg{
      height:1em;
      width:auto;
      fill:currentColor;
    }
    
    .icn-svg-square{
      height:1em;
      width:1em;
    }
    <p style="color:red">
      <svg class="icn-svg" viewBox="0 0 19 18">
        <use href="#customIcon" /> 
      </svg>
      Custom icom
    </p>
    
    <p style="color:green">
      <svg class="icn-svg" viewBox="0 0 320 512">
        <use href="#icon-chess-pawn" /> 
      </svg>
      fontAwesome icon proportional
    </p>
    
    <p style="color:purple">
      <svg class="icn-svg icn-svg-square" >
        <use href="#icon-calendar" /> 
      </svg>
      fontAwesome icon (square)
    </p>
    
    
    <!-- external icion.svg -->
    <svg xmlns="http://www.w3.org/2000/svg" style="width:0;height:0;">
      <symbol id="customIcon" viewBox="0 0 19 18" xmlns="http://www.w3.org/2000/svg">
        <path d="M16.5 0H2.5C1.4 0 0.5 0.9 0.5 2V16C0.5 17.1 1.4 18 2.5 18H16.5C17.6 18 18.5 17.1 18.5 16V2C18.5 0.9 17.6 0 16.5 0ZM2.5 16V2H8.5V16H2.5ZM16.5 16H10.5V9H16.5V16ZM16.5 7H10.5V2H16.5V7Z" />
      </symbol>
      
      <symbol class="icon icon-chess-pawn" id="icon-chess-pawn" viewBox="0 0 320 512">
        <path d="M296 463.1H23.1c-13.25 0-23.1 10.75-23.1 24s10.75 24 23.1 24h272c13.25 0 23.1-10.75 23.1-23.1S309.3 463.1 296 463.1zM55.1 287.1L80 287.1v29.5c0 40.25-3.5 81.25-23.38 114.5h53.5C125.1 394.1 128 354.6 128 317.5v-29.5h64v29.5c0 37.13 2.875 77.5 17.88 114.5h53.5C243.5 398.7 240 357.7 240 317.5V287.1l24-.0001C277.3 287.1 288 277.3 288 263.1c0-13.25-10.75-24-23.1-24H241c23.75-21.88 38.1-53.12 38.1-87.1c0-9.393-1.106-19.05-3.451-28.86C272.3 105.4 244.9 32 159.1 32C93.75 32 40 85.75 40 151.1c0 34.88 15.12 66.12 39 88H55.1C42.75 239.1 32 250.7 32 263.1C32 277.3 42.75 287.1 55.1 287.1zM160 79.1c39.75 0 72 32.25 72 72S199.8 223.1 160 223.1S88 191.7 88 151.1S120.2 79.1 160 79.1z"></path>
      </symbol>
    
      <symbol class="icon icon-calendar" id="icon-calendar" viewBox="0 0 448 512">
        <path d="M152 64H296V24C296 10.75 306.7 0 320 0C333.3 0 344 10.75 344 24V64H384C419.3 64 448 92.65 448 128V448C448 483.3 419.3 512 384 512H64C28.65 512 0 483.3 0 448V128C0 92.65 28.65 64 64 64H104V24C104 10.75 114.7 0 128 0C141.3 0 152 10.75 152 24V64zM48 448C48 456.8 55.16 464 64 464H384C392.8 464 400 456.8 400 448V192H48V448z" />
      </symbol>
    
    </svg>

    We can't use external svgs in SO snippets. Just replace the inline references to something like icons.svg#customIcon when running on local server.

    Plnkr example

    In the above snippet, all fill attributes are removed.
    This way we can easily inherit text colors via fill: currentColor in a css rule like this:

    .icn-svg{
      height:1em;
      width:auto;
      fill:currentColor;
    }
    

    Symbols with individual viewBox settings

    Only needed, if you're dealing with different icon aspect ratios – if all icons are drawn relative to a square (like) viewBox – you can set the icons with and height directly.

    For a "proportional" icon layout – define an appropriate viewBox for each icon <symbol> element and copy these values for the parent <svg> elemnts containing your <use> reference element.

    External icon svgs should be hosted on same domain

    Major browsers are very strict – even if your icon svg is hosted on a cdn allowing cross origin access, it won't work, unless you're fetching and inlining the file first.

    As commented by Danny '365CSI' Engelman you could use his load-file native web component.

    You might also use keyamoon's "svgxuse" helper script.

    Important: you can only fetch/inject external svg icons if they are hosted on servers allowing cross origin access! (e.g image sharing platforms like svgshare don't send a suitable CORS header).

    Alternative: use a existing icon library

    Seriously, creating custom icons is great but also requires some research and quite a lot of testing/debugging.

    So if you're not overly excited about svg, you should better opt for an existing icon library like fontAwesome, material icons ... test icomoon, fontello included libraries/export functions (the latter ones can also export svg files) ... the list goes on and on.

    The point is: creating your own custom icon library is usually not very trivial – especially mixing icons from different sources is quite tricky.