htmlcsssvgastrojs

dynamic Icon component load one svg but not other


Using Astro v 4.8.0

having and reusbale Icon component which load icon which are under public/icons library and when I pass the name it include the file name and display the icon.

now issue is that one svg icon loads but other does not load and if we directly paste that

AstroIcon.astro file

---
interface Props {
  name: string
    title: string;
}

const { name, title } = Astro.props;
console.log({name, title})
---

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" aria-label={title}>
  <use href=`/icons/${name}.svg#${name}`  />
</svg>

and here is how using in Menu.astro

    <AstroIcon name="hamburger" title="this works"/>
    <AstroIcon name="arrow" title="this does not work"/>
    <p>direct svg insertion</p>
  <section>
    <svg
      xmlns="http://www.w3.org/2000/svg"
      fill="none"
      viewBox="0 0 24 24"
      stroke-width="1.5"
      stroke="currentColor"
      height="24"
      width="24"
    >
    <title>Arrow</title>
      <path stroke-linecap="round" stroke-linejoin="round" d="m4.5 19.5 15-15m0 0H8.25m11.25 0v11.25"></path>
    </svg>
</section>

icons/arrow.svg

<svg
  xmlns="http://www.w3.org/2000/svg"
  fill="none"
  viewBox="0 0 24 24"
  stroke-width="1.5"
  stroke="currentColor"
  height="24"
  width="24"
>
  <symbol id="arrow">
    <title>Arrow</title>
    <path stroke-linecap="round" stroke-linejoin="round" d="m4.5 19.5 15-15m0 0H8.25m11.25 0v11.25"></path>
  </symbol>
</svg>

using AstroIcon; first icon display but second icon does not display.

what could be the issue here? although we need to add <symbol> to get the value while referring the Astro and remove <symbool> while direct SVg insetion, that is only change I made.

Here is the reproducible working demo


Solution

  • Your icon actually renders but is invisible as it doesn't apply a stroke color and it is scaled incorrectly – due to mismatching or ignored viewBox values.

    You can fix this problem by moving the viewBox as well as the stroke attributes to the <symbol> or <path> element like so:

    svg {
      display: block;
      outline: 1px solid #ccc;
      overflow: visible;
    }
    <svg viewBox="0 0 24 24" aria-label="this is arrow" >
      <use href="#arrow" style="color:red" />
    </svg>
    
    <svg viewBox="0 0 100 100" aria-label="this is arrow" >
      <use href="#arrow" style="color:red" />
    </svg>
    
    <svg xmlns="http://www.w3.org/2000/svg">
      <symbol id="arrow" viewBox="0 0 24 24" >
        <title>Arrow</title>
        <path fill="none" stroke-width="1.5" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" d="m4.5 19.5 15-15m0 0H8.25m11.25 0v11.25"></path>
      </symbol>
    </svg>

    This way the <use> instance can inherit the stroke color from the parent text color and scale the 24x24 viewBox to the new dimension.

    In fact you don't need to scale the viewBox at all but just copy the symbol's 24x24 viewBox and change the layout size via width and height properties.

    Debugging tip: you can usually also inspect the content of the shadowDOM in dev tools (this option may sometimes be disabled by default).

    inspect shadow DOM

    If you can't expand the <use> node (and see any SVG child elements such as <path>) the problem is probably caused by an incorrect ID-reference or maybe a CORS-issue: external use references are particularly picky and allow only references from the same domain - even if cross-origin access is allowed.

    If you see any SVG elements in the DOM tree – there's probably something wrong regarding the presentation attributes – no fill or no stroke – so no visual result.