5

To import and use svg file in sveltekit I refer to this article https://riez.medium.com/svelte-kit-importing-svg-as-svelte-component-781903fef4ae By the way, when I finally input the code

<svelte:component this={Logo} />

I got the error like below

<svelte:component this={...}> is not a valid SSR component. You may need to review your build config to ensure that dependencies are compiled, rather than imported as pre-compiled modules

I wish someone help me with this problem.

mingxingwang
  • 169
  • 4
  • 17
  • I solve this problem temporarily by using img tag. Such as `````` But I still want to solve this component issue because it is useful in array rendering and so on. – mingxingwang Apr 30 '21 at 16:34
  • it is not possible to change color using tag. But svg file's color can be changed using just background-color and mask with an empty
    . More info [https://stackoverflow.com/questions/24933430/img-src-svg-changing-the-styles-with-css]
    – Swarup Nov 03 '22 at 10:22

4 Answers4

5

There is a SvelteKit plugin for inlining SVG:s. You can use the plugin in three different ways:

  1. As a Svelte Component
  2. As URL (e.g for use in an img tag)
  3. As raw text (e.g for use in {@html rawText})

https://www.npmjs.com/package/@poppanator/sveltekit-svg

Poppanator
  • 59
  • 1
3

Looking through the article it seems to be solving a problem that doesn't exist in a way that is much more elaborate than needed.

In Svelte you can make a .svelte component that only contains SVG markup (inline), then use the svelte:component tag as you would with any other content.

Parent.svelte

<script>
  import Circle from './Circle.svelte'
</script>

<svelte:component this={Circle} />

Circle.svelte

<svg viewbox="0 0 200 200">
  <circle cx="100" cy="100" r="20"/>
</svg>

Here's a REPL showing how to switch between components that only have SVG in them.

You can even add stuff to the SVG components to make them dynamic since it's just markup like shown in this REPL.

JHeth
  • 7,067
  • 2
  • 23
  • 34
  • I think it is only for svelte. But I used the sveltekit and I think it is the problem of config. Your answer can't solve the problem. Because it was the method I used before. And it didn't act and I want to know the method how to solve it. – mingxingwang May 02 '21 at 01:51
  • I used this in Svelte Kit and it still works. – JHeth May 02 '21 at 18:46
  • Here's a repo you can clone and run showing this method 100% does work with Svelte-kit https://github.com/JHethDev/svg-ssvelte-kit SVG is just markup, I can't imagine how your Svelte-kit config would prevent you from using markup in a Svelte file. – JHeth May 02 '21 at 19:03
3

In svelte-kit You can fix it by trying

<img src={YourSVGComponent} />

It worked for me.

Roopz
  • 31
  • 1
3

The Issue

I know this is an old question, but unfortunately, I also run into the same bug, and the answer here doesn't help a lot.

  1. using a <img> is great if the svg styles are self-contained, but once you need to size or colour them relative to the parent element (like currentColor this stop working)
  2. creating a new Svelte file can be great, but I prefer that my assets are in one folder as normal.
    why?
    • for readable purposes: .svg/.png/.jpg for assets. and real Components with .svelte
    • performance purposes: https://stackoverflow.com/a/75191716/17716837
    • time consuming: for every asset to have to add: you need manually create a new file .svelte, and paste the .svg code. this is easy for 2/3 of icons but what happens if you have 50/80+ icons? is time-consuming.
  3. using an external library for this isn't necessary,
    because we can implement this by ourselves using less code and native vite/svelte code.

    So let's do it below

Solution

As I said, guys don't install that library because sveltekit+vite handles that natively easily (unless you need to)

  1. now in lib/ create a folder called assets/ where you will insert all your .svg/.png/.jpg (not inside static/ for performance reasons, because if inside lib vite bundler will handle caching)

  2. let's say we want to import myIcon.svg:

<script>
  import myIcon from "$lib/assets/myIcon.svg?raw"
</script>

{@html myIcon}

?raw is returning us the text inside the file (see https://vitejs.dev/guide/features.html#static-assets)
instead of the URL link, like it was happening before (for default assets return have ?url in vite)

As you see, use 2 lines of native svelte code to make it work fine.

this works also when you nest your svg inside another and you are using currentColor

in most cases this @html is great,
But if your images come from input then don't use @html or you can be hacked
(or use it but with the input values sanitized)


EDIT:

  • if you want to make this like a component library use this code for your component:
<script>
  export let src;

  $: isSvg = !!src.endsWith(".svg");                             // we use $: because you may dynamically change the src so the component needs to be reactive. if you are sure that it won't be changed it will be great to use instead `const`
  $: svgRoute = isSvg ? src.replace(".svg", ".svg?raw") : null;  // you can also do `${src}?raw`
</script>

{#if isSvg}
  {#await import(svgRoute)}
    <div>loading...</div>       <!-- default fallback, can be a loading spinner or anything else that the user will see while waiting for the loading -->
  {:then value}
    {@html value.default}       <!-- the svg code will be inject here -->
  {/await}
{:else}
  <img src={Gsrc} />            <!-- if the src is only a normal photo then use native html `<img>`'s src attribute -->
{/if}
  • if you want to simplify even more this import myIcon from "$lib/assets/myIcon.svg?raw" you can use my other answer on that topic

How can I simplify my imports when using SvelteKit with Vite bundler, like $lib/?

so by using that component it will be like this:

<script>
  import Icon from "$lib/Icon.svelte"
</script>

<Icon src="$assets/foo.svg" />
<Icon src="$assets/bar.svg" />
<Icon src="$assets/hello.png" />

<div class="text-blue-500">
  <Icon src="$assets/nested.svg" />
</div>

EDIT: ⚠️ fix of latest vitejs's bug

lately the above solution won't work anymore when using the npm run build by throwing this error:

TypeError: Failed to fetch dynamically imported module

https://github.com/vitejs/vite/issues/11804

so use this instead for the svg part

<script lang="ts">
  export let src: string;

  $: isSvg = !!src.endsWith(".svg");         
  $: svgRoute = `${src}?raw`;

  function fetchSvg(svgRoute: string) {
    return new Promise((resolve) => {
      fetch(svgRoute)
      .then(response => response.text())
      .then(svg => resolve(svg))
    })
  }
</script>

{#if isSvg}
   {#await fetchSvg(svgRoute)}
     <div>loading...</div>
   {:then value}
     {@html value}
   {/await}
{:else}
  <img src={Gsrc} />
{/if}

Laaouatni Anas
  • 4,199
  • 2
  • 7
  • 26