DEV Community

Zoppatorsk
Zoppatorsk

Posted on

Simple, reusable Svelte Modal

Intro

So I noticed this post about make a React Modal so i thought I would post my take on a Svelte Modal but with more features and more reusable.

So this for this Modal you can display it and change text and title by just setting the values of a store.

You will also be able to close the modal by clicking outside the modal-box, pressing Escape key or pressing the OK-button.

This is a small variation of a component I created for my submission to the redis hackathon where it was used to display error messages.

Step by step

Start with open a terminal/console

Go to the directory you want the project

Run

npm create vite@latest
Enter fullscreen mode Exit fullscreen mode

Type a project name, in my case svelte-modal
Choose svelte as framework
Choose regular svelte
cmd

As instruction says, got to directory and run

npm install
Enter fullscreen mode Exit fullscreen mode

I am lazy and don't like writing a lot of CSS so for this I will use picocss
So now run

npm install @picocss/pico
Enter fullscreen mode Exit fullscreen mode

Start up the dev server with

npm run dev
Enter fullscreen mode Exit fullscreen mode

You should now see this in the browser.
starterpage

Let's get to code!
Go to the App.svelte file in the src folder. It contains the boilerplate for the page you just open in the browser.
Let's delete it all and replace with:

<script>
</script>

<main>
    <button>Show modal</button>
</main>
Enter fullscreen mode Exit fullscreen mode

Let's go to app.css and just delete all the contents as it's not needed.

Now, lets import pico.css
goto main.jsand add this

import '@picocss/pico/css/pico.min.css';
Enter fullscreen mode Exit fullscreen mode

in the top of the file.
Should now look like this:

import '@picocss/pico/css/pico.min.css';
import './app.css';
import App from './App.svelte';

const app = new App({
    target: document.getElementById('app'),
});

export default app;
Enter fullscreen mode Exit fullscreen mode

This modal will make use of a Svelte store. This store will hold the modals message, title and open/close state. By using a store means we will be able to trigger the showing of the modal from both Svelte components and regular Javascript code.

Let's create a new folder named stores inside the src/lib folder. Create a new file in the storesfolder named index.js

Put below code in there. If you want can set a default title and message to anything you want.

import { writable } from 'svelte/store';
export const modal = writable({ open: false, title: 'Default title', message: 'Default message' });
Enter fullscreen mode Exit fullscreen mode

Store is now done and ready to be used.
Let's create the actual modal component. I usually create a components folder but for this example we will just put the file in the 'src/lib' folder.

Create a file named Modal.svelte

We put below code in the file, I will explain what it does a bit later.

<script>
    import { modal } from '../lib/stores/';
    const close = () => ($modal.open = false);

    const handle_keydown = (e) => {
        if (e.key === 'Escape') return close();
    };
</script>

<svelte:window on:keydown|once={handle_keydown} />

<dialog open on:click|self|preventDefault={close}>
    <article>
        <h3>{$modal.title}</h3>
        <p>
            {$modal.message}
        </p>
        <footer>
            <button on:click|once={close}>OK</button>
        </footer>
    </article>
</dialog>

Enter fullscreen mode Exit fullscreen mode

Let's go back to App.svelte and wire things up so can try things out.

<script>
    import Modal from './lib/Modal.svelte';
    import { modal } from './lib/stores/';
</script>

<main>
    {#if $modal.open == true}
        <Modal />
    {/if}
    <button on:click={() => ($modal.open = true)}>Show modal</button>
</main>

Enter fullscreen mode Exit fullscreen mode

We now got this:

modal_opener
And if pressing the button:

modal

So we import the Modal component and the modal store
We put the Modal component inside a Svelte-if. $modal references the store, so we only show the component if $modal.open is true.

We now have a modal that will be closed if click outside the modal, if press escape button on keyboard or if pressing the modal ok-button. It can be displayed from anywhere just importing the store and set open to true and in the same way you can set the title and message to display different things, for example error messages.

Now let's dig into the code that makes it work. Seeing to the functionality it provides there is a surprisingly small amount of code.

Here is the Modal.svelte component again

<script>
    import { modal } from '../lib/stores/';
    const close = () => ($modal.open = false);

    const handle_keydown = (e) => {
        if (e.key === 'Escape') return close();
    };
</script>

<svelte:window on:keydown|once={handle_keydown} />

<dialog open on:click|self|preventDefault|once={close}>
    <article>
        <h3>{$modal.title}</h3>
        <p>
            {$modal.message}
        </p>
        <footer>
            <button on:click|once={close}>OK</button>
        </footer>
    </article>
</dialog>
Enter fullscreen mode Exit fullscreen mode

Import the store so can use it.

import { modal } from '../lib/stores/';
Enter fullscreen mode Exit fullscreen mode

Function that will close the modal by setting the store.

const close = () => ($modal.open = false);
Enter fullscreen mode Exit fullscreen mode

Handler for keydown that will be shown a bit further down, only call the close() function when escape is pressed.

const handle_keydown = (e) => {
        if (e.key === 'Escape') return close();
    };
Enter fullscreen mode Exit fullscreen mode

In Svelte we can use the special svelte:window tag here we use it to bind an eventlistener for keydown to the window object, same as you would be able to do in regular javascript.
It uses the special once DOM modifier. By using once the listener will delete itself after running once, it can be useful in many situations

<svelte:window on:keydown|once={handle_keydown} />
Enter fullscreen mode Exit fullscreen mode

In pico.css modals are easily created with element with an element inside. These are not special, just regular html-elemnts.

But let's analyze the below code. There is a on:click listener bound to the <dialog>element, so the semi-transparent area outside the modal-box. And it runs the close() function making the dialog close.

<dialog open on:click|self|preventDefault|once={close}>
Enter fullscreen mode Exit fullscreen mode

By using self the on:click will only fire if we explicit click anywhere on the <dialog> element, i.e. the background. Without this the on:click would also fire if clicked anywhere on the modal-box. Handy feature :)

preventDefault should not be needed but I put it there cause i wanted to show it. It prevents default behavior and is the same as using the preventDefault() in an eventhandler, this can be handy if you for example want to define an inline function for the on:click handler, like:

on:submit|preventDefault={(e)=>do something simple}
Enter fullscreen mode Exit fullscreen mode

Svelte is filled with smart stuff like this, check the documentation if your curios about other DOM modifiers.

Hope you learned something. This was tiring to write, I will stick to writing devlogs. ;)

Top comments (0)