Skip to content

Apply SVG filters on HTML using Vue.js

This article was written over 18 months ago and may contain information that is out of date. Some content may be relevant but please refer to the relevant official documentation or available resources for the latest information.

SVG is a powerful format, based on XML and is well known for its scalability.

This image format is usually known for its application on logos, icons and graphs, but in this article, we are going to use it with the help of VueJs to create a dynamic styled HTML container.

Prologue

The idea of this project occurred to me while listening to Syntax FM podcast with Sara Soueidan. I never had a chance to use SVG before and I was astonished to hear the countless possibilities that lie within this image format, and I wanted to see it with my own eyes.

The article is going to walk through all the steps necessary to produce a full working component. A full version of the code can be found in this codepen:

vuejs-svg-component-with-filter-and-html-slot-bnejy

The CSS filter property already offers us the possibility to apply a great set of filters to our elements, and even load SVG filter directly with the use of the URL filter function. But in this article we are going to use plain SVGs.

The component

Our first step requires the creation of a Vue component:

<template>
</template>

<script>
export default {};
</script>

Next, we will add a very simple SVG within our Template tags. Our object will include four main parts: basic structure, shape, filter and foreignObject.

Basic structure

First we will create the structure of the SVG. For the purpose of our example we are going to create a simple SVG with a fixed size ViewBox:

<svg 
    width="600" 
    height="600" 
    viewBox="0 0 250 250" 
    xmlns="http://www.w3.org/2000/svg">
</svg>

Shape

SVGs are well known for the multitude of simple shape elements available within its standards. If you have worked with HTML before, SVG elements will look quite familiar, as they exhibit the same features of a normal HTML element, with the addition of specific XML attributes.

In our example we are going to create a simple Polygon. This shape is described as:

Polygons are made of straight lines, and the shape is "closed" (all the lines connect up).

https://www.w3schools.com/graphics/svg_polygon.asp

The next code will define a polygon made of 4 lines and filled with the colour blue:

<polygon points="5,5 225,15 205,180 10,195" fill="blue"/>

Filter

There are a great number of filter primitives that can be used within an SVG document. Their power comes from the possibility to merge multiple primitives together, to create very complex results, and the ability for the filters to be applied to a single shape/ object within an SVG document. The actual definition of filters is:

The filter SVG element defines a custom filter effect by grouping atomic filter primitives. It is never rendered itself, but must be used by the filter attribute on SVG elements, or the filter CSS property for SVG/HTML elements.

https://developer.mozilla.org/en-US/docs/Web/SVG/Element/filter

In our example we are going to apply two different filters: feTurbulence and feDisplacementMap. This will provide a distorted effect to all elements in which it is applied.

<filter id="turbulence">
  <feTurbulence
    type="turbulence"
    baseFrequency="0.20"
    numOctaves="2"
    result="turbulence"></feTurbulence>
  <feDisplacementMap
    in2="turbulence"
    in="SourceGraphic"
    scale="25"
    xChannelSelector="R"
    yChannelSelector="G"></feDisplacementMap>
</filter>

As described above, the filter needs to be used within another object with the use of the filter attribute. In our case, we will have to apply the ID of turbulence to our polygon:

  <polygon
    points="5,5 225,15 205,180 10,195"
    fill="blue"
    filter="url(#turbulence)"></polygon>

A rendered version of our component will look like this:

image alt text

HTML object

Our SVG is finally getting shape. The last step required involves the use of the foreignObject element. This element will allow us to include normal HTML elements within our SVG.

Our first step requires us to add the element within the SVG. This is going to be added alongside the shape, but it is going to be completely independent to it. This means that the filter that we have applied to the polygon will bleed into this SVG element.

<foreignObject
  x="5%"
  y="10%"
  width="90%"
  height="90%">
  ...HTML CODE HERE
</foreignObject>

Now that we have defined our element, we are able to add any HTML we want within its tags. It is important to note that this approach may produce unwanted behaviour across different browsers, and on site performance.

Because of the nature of SVG, all elements within foreignObject element are going to be readable by a screen reader and can be used for accessibility purposes.

For the purpose of our example, we are going to add a few simple elements within our SVG:

<foreignObject
  x="5%"
  y="10%"
  width="90%"
  height="90%">
    <div style="color:red" xmlns="http://www.w3.org/1999/xhtml">
      <p>The HTML works..</p>
    </div>
</foreignObject>

TIPS: We are able to use any CSS declaration within the SVG itself and we can even use an existing declaration. For example, we would have defined the <p> to be green globally, this declaration would have been applied to our element too.

Our component will render like this:

image alt text

Make it dynamic

The component above works perfectly, but it is still static. In the following section we are going to make it more dynamic by using features available within the VueJs framework.

Slot

First, we are going to modify the code, so that the content of the foreignObject is actually going to be a slot. These are very useful when you want to provide the flexibility of passing any HTML to the component.

<foreignObject x="5%" y="10%" width="90%" height="90%">
 <div style="color:red" xmlns="http://www.w3.org/1999/xhtml">
   <slot>
     <p>The HTML works..</p>
   </slot>
 </div>
</foreignObject>

Then we are going to pass some HTML in the parent component that is using our newly introduced slot:

<finalComponent>
 <img 
    src="./assets/logo.png" 
    width="25px" 
    height="25px" 
    alt="Example logo" />
 <h1>My heading</h1>
 <p>This is my paragraph</p>
</finalComponent>

Running this code in the browser will produce the following design:

image alt text

Summary

Our fully working VueJs SVG component is now completed. The code can be found at the following codesandbox: vueJs Svg component with HTML slot

Using SVG within a framework, could be a bit tricky, as reactivity and virtual DOM, produce unwanted results, and this example may not be used in production, without the right set of tests.

The article just covers a couple of examples and introduces a few topics, but the possibilities are endless. We could provide a set of shapes as props, introduce SVG animations and finally create a set of filters that can easily be applied to the passed HTML.

I hope this proof of concept turns out to be helpful, and I look forward to seeing different permutations and applications of this approach online.

References

SyntaxFM: https://syntax.fm/show/154/svgs-with-sara-soueidan

SVG definition: https://en.wikipedia.org/wiki/Scalable_Vector_Graphics

CSS filters: https://css-tricks.com/almanac/properties/f/filter/

SVG filters explained: https://tympanus.net/codrops/2019/01/15/svg-filters-101/

SVG filters: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/filter

VUE slots: https://vuejs.org/v2/guide/components-slots.html

This Dot is a consultancy dedicated to guiding companies through their modernization and digital transformation journeys. Specializing in replatforming, modernizing, and launching new initiatives, we stand out by taking true ownership of your engineering projects.

We love helping teams with projects that have missed their deadlines or helping keep your strategic digital initiatives on course. Check out our case studies and our clients that trust us with their engineering.

Let's innovate together!

We're ready to be your trusted technical partners in your digital innovation journey.

Whether it's modernization or custom software solutions, our team of experts can guide you through best practices and how to build scalable, performant software that lasts.

Prefer email? hi@thisdot.co