In this article, you are going to see how easy and quickly you can create modal as a web component in Svelte. First, a quick intro to svelte, just a small paragraph in case you are unfamiliar with it.
TLDR Svelte
As it says in their docs Svelte is a tool for building fast web applications. Same as React or Vue, in fact, it is a mix of both, though what makes it different is that it is a compiler just like a typescript. And like typescript, it is compiled to vanilla javascript in the end. Being a compiler, it can add some syntax that is not native to javascript, though there is not much of it and it can be absorbed rather quick, the syntax is borrowed a lot from Vue. For a compiler, there is Rollup and Webpack setup. I personally used a loader with Webpack and here is a good template to start with. Although there are not a lot of tutorials, compared to other frameworks, you can go through their docs and interactive tutorials on their website, they are really good and fun to go through.
As here we are going to use web components, If you want to export them and you are using Webpack, you just need to add a set custom element to true under options for the loader or manually register tag element with customElements.define('tag-name', SvelteComponent).
1{2 test: /\.svelte$/,3 use: {4 loader: 'svelte-loader',5 options: {6 emitCss: true,7 hotReload: true,8 customElement: true9 }10 }11}
Here comes the modal
As we want to create modal as a web component, we need to be aware of some limitations. Because web components are using shadow dom for styling, we cannot style components from the outside, Styles can read custom properties so we can use them to modify style a bit. And one limitation of Svelte is that we cannot use built-in animations in web components, but we can create our own custom animations, and that feature of Svelte is really powerful.
Now that we understand a bit about Svelte, let’s start with what we want to achieve. We would like to have a modal that can be revealed with javascript, like something for which we used classes before. Also, we want to know when modal closed so we can do something in response to that, an example would be to add overflow hidden to the body when modal is visible, and when it closes we would like to remove the overflow from the body element. Maybe also to have some options for styling it.
Ok, let’s go through the properties first.
1export let show = false;2export let zindex = 1;3export let duration = 350;4export let background = 'rgba(0,0,0,0.5)';
As we can see, we have show attribute for the dynamic reveal of the modal. Duration is for animation and the other is for styling. Even though we cannot style web components from outside, we can add variety also though attributes.
1{#if show}2<div3 style="background-color: {background}; z-index:{zindex};"4 transition:fade="{{ transitionDuration: duration }}"5 bind:this={modalElement}6 class="modal"7 on:click|stopPropagation={modalOnClose}>8 <div class="container">9 <slot>Modal</slot>10 </div>11</div>12{/if}
Here is demonstrated how modal is revealed, and unlike React, we can still manipulate animation transition in and out even though remove an element from the render. For transition we define function callback fade and pass in property transitionDuration with the value of property duration with a default value of 350, which can be changed on the DOM element.
1import { quintIn } from 'svelte/easing';2...3 function fade(node, { transitionDuration }) {4 return {5 duration; transitionDuration,6 tick: time => {7 const eased = quintIn(time);8 node.style.opacity = eased;9 }10 };11 }
Inside the fade function, we receive a node element as the first argument and as a second object of any custom arguments we passed in. In our case a single argument of transitionDuration we used for the duration of the animation. From callback function, we returned object that is going to be used to create animation, with a duration of transition and tick function. Tick function has time parameter received from svelte and in that function we manipulate style of the element. The time variable goes from 0 to 1 in duration that we specified, in our case, it goes from 0 to 1 in 350ms. We passed time through quintIn function from Svelte, which offsets the time and acts similar to timing property in css animations. Svlete website has examples of all easing functions for animations that can be used. Animations are one of the most powerful features of Svelte and are something I am most excited about.
1let modalElement;23let event = new CustomEvent("close", {4 detail: {5 show,6 },7 composed: true8 }9);1011function modalOnClose(e) {12 if (e.target !== modalElement) {13 return;14}1516 // show will allways be false, though if we needed to send dynamic data this would update the value.17 event.detail.show = show;1819 // dispatch close event.20 e.target.dispatchEvent(event);2122 show = false;23}
Here we see modalElement variable, which became a modal node, through binding this of modal on top example. Modal is used as a wrapper for ant content we want. So we want to make sure that the user clicked on modal and not on any of the children and event just bubbled. So that is why we are comparing if the target clicked is a modal element. Now that we determined that modal is clicked we want to dispatch a custom close event, so a user can add an event listener to check when modal is closed. On custom events it is important to set composed to true, as this allows the event to pass through shadow dom boundary. In the end, we just close modal by setting show variable to false.
Summary
In this example, we can see, how Svelte could be used to quickly create small, self-contained parts of the UI. We can use them eather in DOM or in other frameworks. Svelte comes without any overhead, as it compiles to vanilla javascript and has really powerful animations features. Although it will not replace React, Vue or Angular, I feel it has a place its place in the world among them.
You can check source code and more web components on github repo.
Until next time, happy coding.