I have about 80 custom SVG icons that I'm importing into a Svelte front-end app. Building on https://github.com/sveltejs/template, it's built with Rollup and includes Typescript, Tailwind, and all the modern goodies.
The dilemma is how to add the icons into the app. As SVGs, the icons are short XML text strings that do not exceed 2kB.
foo.svg
into public/assets/icons
.<Icon type="foo' />
that displays an the icon using <img src="foo.svg>
.This approach means that the icons are not part of the code.
Benefits: icons can be dynamically loaded by frontend code on demand. No need to bundle all icons into app code.
Cons: slow page load if there are a lot of new icons, and the browser has to fetch a dozen 1kB files. Deploying the app as a PWA means we need to manually tell it to cache the icons beforehand.
import Home from './icons/home.svg';
Here, the icons are bundled as text strings with the app itself.
Benefits: Icons are delivered as part of the app bundle. Caching is unnecessary. Possible to dynamically modify some of the icon code e.g. colors, viewBox, etc on load.
Cons: It's unnecessary to include all icons in the app to reduce time to first byte. Can't do bundle splitting, etc. without adding more complexity. Makes the rendering slower because Javascript code needs to first turn a string into an SVG instead of just loading an image.
Now, in any Svelte component where you want to use an SVG: Import the file, while assigning it to a variable. Bind the variable you assign it to, as src={myVar} on an image element.
SVG images can be written directly into the HTML document using the <svg> </svg> tag. To do this, open the SVG image in VS code or your preferred IDE, copy the code, and paste it inside the <body> element in your HTML document. If you did everything correctly, your webpage should look exactly like the demo below.
Scalable Vector Graphics, or SVGs, are a generally a good choice for use as icons on your website, because they are vector graphics. Vector graphics can be scaled to any size without losing quality. The files are small and compress well, so they won't make your website slow to load.
You can just change the file extension to .svelte
and import an SVG as a normal component.
The following approach has these advantages:
Have a dedicated Icon.svelte component setup like this:
<script>
export let name;
export let width = "1rem";
export let height = "1rem";
export let focusable = false;
let icons = [
{
box: 24,
name: "save",
svg: `<g stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><path d="M17 21v-8H7v8"/><path d="M7 3v5h8"/></g>`
},
{
box: 32,
name: "trash",
svg: `<path d="M12 12h2v12h-2z" /><path d="M18 12h2v12h-2z" /><path d="M4 6v2h2v20a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V8h2V6zm4 22V8h16v20z" /><path d="M12 2h8v2h-8z" />`
}
];
let displayIcon = icons.find((e) => e.name === name);
</script>
<svg
class={$$props.class}
{focusable}
{width}
{height}
viewBox="0 0 {displayIcon.box} {displayIcon.box}">{@html displayIcon.svg}</svg>
You can then use it like so:
<Icon name="trash" class="this-is-optional" />
another way is to use a symbol defs file (ex: icons.svg) in your public folder. then in your code do something like this :
Icon.svelte
<script>
export let name;
export let width = "1.5rem";
export let height = "1.5rem";
export let focusable = false;
</script>
<svg class={$$props.class} {focusable} {width} {height}>
<use href={`/icons.svg#${name}`} />
</svg>
icons.svg
<svg aria-hidden="true" style="position: absolute; width: 0; height: 0; overflow: hidden;" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<symbol id="icon-warning" viewBox="0 0 20 20">
<path d="M19.511 17.98l-8.907-16.632c-0.124-0.215-0.354-0.348-0.604-0.348s-0.481 0.133-0.604 0.348l-8.906 16.632c-0.121 0.211-0.119 0.471 0.005 0.68 0.125 0.211 0.352 0.34 0.598 0.34h17.814c0.245 0 0.474-0.129 0.598-0.34 0.124-0.209 0.126-0.469 0.006-0.68zM11 17h-2v-2h2v2zM11 13.5h-2v-6.5h2v6.5z">
</path>
</symbol>
</defs>
</svg>
App.svelte
<Icon name="icon-warning" />
this way you use one http call to load svg file. and then just use the part you need in markup.
Using like {@html SVG_CODE}
for rendering a svg code from another component or variable is a Bad choice . This sometimes gives risk of XSS attack . Specially when the image comes from external source.
check : https://github.com/sveltejs/svelte/issues/2545
Svelte doesn't perform any sanitization of the expression inside {@html ...} before it gets inserted into the DOM. In other words, if you use this feature it's critical that you manually escape HTML that comes from sources you don't trust, otherwise you risk exposing your users to XSS attacks.(source)
I think using plugin like rollup-plugin-svelte-svg would be a better choice
npm i -D rollup-plugin-svelte-svg
export default {
plugins: [
svelteSVG({
// optional SVGO options
// pass empty object to enable defaults
svgo: {}
}),
],
...
}
<script>
import Logo from "./logo.svg";
</script>
<Logo width=20 />
.svelte
extension .for example ,
icon.svg
file contains :
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M14.243 5.757a6 6 0 10-.986 9.284 1 1 0 111.087 1.678A8 8 0 1118 10a3 3 0 01-4.8 2.401A4 4 0 1114 10a1 1 0 102 0c0-1.537-.586-3.07-1.757-4.243zM12 10a2 2 0 10-4 0 2 2 0 004 0z" clip-rule="evenodd"></path></svg>
icon.svelte
file<script></script>
<style></style>
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M14.243 5.757a6 6 0 10-.986 9.284 1 1 0 111.087 1.678A8 8 0 1118 10a3 3 0 01-4.8 2.401A4 4 0 1114 10a1 1 0 102 0c0-1.537-.586-3.07-1.757-4.243zM12 10a2 2 0 10-4 0 2 2 0 004 0z" clip-rule="evenodd"></path></svg>
icon.svelte
component<script>
import Icon from "$lib/icon.svelte"
</script>
<style></style>
<Icon />
then in icon.svelte
<script></script>
<style></style>
<svg class="{$$props.class}" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M14.243 5.757a6 6 0 10-.986 9.284 1 1 0 111.087 1.678A8 8 0 1118 10a3 3 0 01-4.8 2.401A4 4 0 1114 10a1 1 0 102 0c0-1.537-.586-3.07-1.757-4.243zM12 10a2 2 0 10-4 0 2 2 0 004 0z" clip-rule="evenodd"></path></svg>
icon.svelte
component<script>
import Icon from "$lib/icon.svelte"
</script>
<style></style>
<Icon class="h-6 w-6" />
A working solution is to hold the SVGs in a separate IconService.js:
export const chevron_down = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"feather feather-chevron-down\"><polyline points=\"6 9 12 15 18 9\"></polyline></svg>";
export const chevron_right = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\" class=\"feather feather-chevron-right\"><polyline points=\"9 18 15 12 9 6\"></polyline></svg>";
This can easily be imported and used within svelte @html function.
<script>
import {chevron_down, chevron_right} from 'IconService';
</script>
{@html chevron_down}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With