Combining SVG's with Svelte

Published on: 20200614

About SVG

SVG allows us, as the full name suggests, scalable vector graphics. Or in other words graphics that smoothly scale together with the user’s screen. This can be used for icons, backgrounds or decoration. Because SVG is an XML-like markup it can be easily combined together with HTML to embed these graphics into the browser.

This code for instance is a simple red circle:

<svg viewBox="0 0 100 100" height="100px" width="100px">
	<circle cx="50" cy="50" r="34" fill="red" />
</svg>

This is really great because it means we can treat it as a component in Svelte, all we have to do is create a new file with the SVG as the content, save it as MyGraphic.svelte and then import and render as we would do any other component:

<script>
	import MyGraphic from './MyGraphic.svelte';
</script>

<MyGraphic />

Passing properties to the SVG

But now that we have turned our SVG in a full fledged Svelte component it means we can also pass props to it and use them in the generation of our SVG.

<script>
	export let color = 'red';
</script>

<svg viewBox="0 0 100 100" height="100px" width="100px">
	<circle cx="50" cy="50" r="34" fill={color} />
</svg>
<script>
	import Circle from './Circle.svelte';
</script>

<Circle color="red" />
<Circle color="green" />
<Circle color="blue" />

Generating the SVG using logic

Nothing stops us now from adding some logic and leveraging more of Svelte’s toolbox to actually generate the SVG instead of using some static markup. We can do for instance:

<script>
	export let data = [];
	$: xwidth = data.length ? 100 / data.length : 100;
	$: yratio = data.length ? 100 / Math.max(...data) : 100;
</script>

<svg viewBox="0 0 100 100">
	{#each data as value, i}
		<rect y={100 - value * yratio} width={xwidth} x={i * xwidth} height={value * yratio} />
	{:else}
		<text x="50" y="50">No data provided</text>
	{/each}
</svg>

<style>
	/* Using CSS customer properties to match the colours to the site's */
	rect {
		fill: var(--primary-light);
		stroke: var(--primary);
	}
	text {
		fill: var(--white);
		dominant-baseline: center;
		font-size: 10px;
		text-anchor: middle;
	}
</style>

Adding reactivity to the mix

You can of course go further and add

  • 15
  • 25
  • 40
  • 78
  • 100