Fluid SVGs with Vue 3

Have your SVGs adjust to their parents width.

2018-03-09
2022-08-02
frontend
vue 3
svg
demo

A fluid SVG is one that can grow as far as its parent allows it to.
The trick lies in how we define the <svg> element.

Only set the viewBox attribute, but don’t set a height nor width on it.
Thus, it’ll preserve its aspect ratio, growing and shrinking accordingly.

This is the svg component src/components/svg/SvgBox.vue:

<template>
  <svg viewBox="0 0 1000 1000" class="bg-stone-300">
    <rect
      x="100"
      y="100"
      width="300"
      height="300"
      class="fill-amber-600 stroke-black stroke-2"
    ></rect>
  </svg>
</template>

This is the container src/components/svg/SvgWrapper.vue:

<script lang="ts" setup>
import { computed } from 'vue'

const props = defineProps<{
  width: string
}>()

const widthPercent = computed(() => `${props.width}%`)
</script>

<template>
  <div class="svg-wrapper">
    <slot></slot>
  </div>
</template>

<style scoped>
.svg-wrapper {
  width: v-bind('widthPercent');
}
</style>

It just contains a <div> with a configurable width — that’s how the slider controls it.
The SvgBox will shrink and grow as much as permitted by the wrapper’s width.

The code for the mini-demo above is just this:

<script setup lang="ts">
import { ref } from 'vue'

import SvgBox from '@/components/svg/SvgBox.vue'
import SvgWrapper from '@/components/svg/SvgWrapper.vue'

const width = ref('50')
</script>

<template>
  <div>Current parent <strong>div</strong> width: {{ width }}%</div>

  <input v-model="width" type="range" />

  <SvgWrapper :width="width">
    <SvgBox></SvgBox>
  </SvgWrapper>
</template>

That’s it! :grin: