Modal Window in Svelte
Tags: svelte, webdev
Published: 2021-09-04 00:04:03

**[Svelte](https://svelte.dev/)** is my favorite compiler for developing web apps.  

Sometimes I try to use other tools such as Web frameworks like **React** and **Angular**, but I tend to use **Svelte** eventually since I like it very much.  

In this post, I write about implementing **Modal Window** which I often use when creating websites and apps.

## 1. Create a new project

It's easy.  

```bash
npx degit sveltejs/template modal-app
cd modal-app
npm install
```

## 2. Edit `App.svelte`

In `App.svelte`, delete the default code and write a simple HTML element.

```html
<!-- src/App.svelte -->
<button>Open Modal</button>
```

## 3. Create `Modal.svelte`

Next, create `Modal.svelte` in the same hierarchy as `App.svelte`.

And add HTML elements and styling in it for the modal window.   

```html
<!-- src/Modal.svelte -->
<div id="background"></div>
<div id="modal">
    <p>This is a modal window.</p>
</div>

<style>
    #background {
        position: fixed;
        z-index: 1;
        top: 0;
        left: 0;
        width: 100vw;
        height: 100vh;
    }

    #modal {
        position: fixed;
        z-index: 2;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        background: #fff;
        filter: drop-shadow(0 0 20px #333);
    }
</style>
```

**`<div id="modal">...</div>`** is the body of the modal window.  

When a modal window is displayed, **`<div id="background"></div>`** provides a transparent background for closing the modal window when you click outsite the modal. In short, this is for detecting the mouse click.  

## 3. Import `Modal.svelte` in `App.svelte`

Import the `Modal.svelte` in `App.svelte` and add the component to the bottom of `<button...`.

```html
<!-- src/App.svelte -->
<script>
    import Modal from './Modal.svelte';
</script>

<button>Open Modal</button>
<Modal />
```

Then the page looks like this:

![screenshot_1](https://blog.hdks.org/images/Modal-Window-in-Svelte/screenshot_1.png)

## 4. Add a state variable and property.

Add the variable named **`isOpenModal`** in `<script>` tag.  
Also add the property named **`isOpenModal`** in `<Modal>` element.

```html
<!-- src/App.svelte -->
<script>
    import Modal from './Modal.svelte';

    let isOpenModal = false;
</script>

    <button>Open Modal</button>
    <Modal isOpenModal={isOpenModal} />
</script>
```

In `Modal.svelte`, add the property named `isOpenModal`.  

```html
<!-- src/Modal.svelte -->
<script>
    export let isOpenModal;
</script>

<!-- ... -->
```

Then you can use the state variable `isOpenModal` represents if it's opening or closing.

## 5. Switch Open/Close

Create the functions switch the state of `isOpenModal` and connect it to `<button>`.

```html
<!-- src/App.svelte -->
<script>
    import Modal from './Modal.svelte';

    let isOpenModal = false;

    function openModal() {
        isOpenModal = true;
    }

    function closeModal() {
        isOpenModal = false;
    }
</script>

<button on:click={openModal}>Open Modal</button>
<Modal isOpenModal={isOpenModal} />
```

The button will call the `openModal` function when clicked. (But it still doesn't work for now.)  

Let's edit the **Modal** component to make this work.  

```html
<!-- src/Modal.svelte -->
<script>
    // ...
</script>

<div id="background" style="--display: {isOpenModal ? 'block' : 'none'};"></div>
<div id="modal" style="--display: {isOpenModal ? 'block' : 'none'};">
    <p>This is a modal window.</p>
</div>

<style>
    #background {
        display: var(--display);
        // ...
    }

    #modal {
        display: var(--display);
        // ...
    }
</style>
```

Added the [CSS Custom Properties](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties) to **style** props in each element.  

Thanks to this, when `isOpenModal` is **true**, modal is displayed, and when it is **false**, modal is hidden.  
But it still does not work correctly for now because we have not implemented the process of closing the modal yet.  

Let's do it.  

## 6. Dispathcer

**`createEventDispathcer`** helps pass data between parent and child components.  

```html
<!-- src/Modal.svelte -->
<script>
    import { createEventDispathcer } from 'svelte';

    export let isOpenModal;

    const dispatch = createEventDispathcer();

    function closeModal() {
        isModalOpen = false;
        dispatch('closeModal', { isOpenModal });
    }
</script>

<div id="background" style="--display: {isOpenModal ? 'block' : 'none'}" on:click={closeModal}></div>
<!-- ... -->
```

In addition to `dispatch`, added `on:click` event in `<div id="background">` element.  
When you click outsite the modal, the modal is closed.  

Finally, add **`closeModal`** function to **`<Modal ... />`** in `App.svelte`.  

```html
<!-- src/App.svelte -->

<!-- ... -->

<Modal isOpenModal={isOpenModal} on:closeModal={closeModal} />
```

Clicking the "Open Modal" button will display the modal, and clicking outside the modal will close it.  

![screenshot_2](https://blog.hdks.org/images/Modal-Window-in-Svelte/screenshot_2.gif)

## 7. Conclusion

This is the entire code.  

```html
<!-- src/App.svelte -->
<script>
    import Modal from './Modal.svelte';

    let isOpenModal = false;

    function openModal() {
        isOpenModal = true;
    }

    function closeModal() {
        isOpenModal = false;
    }
</script>

<button on:click={openModal}>Open Modal</button>
<Modal isOpenModal={isOpenModal} on:closeModal={closeModal} />
```

<br />
<br />

```html
<!-- src/Modal.svelte -->
<script>
    import { createEventDispatcher } from 'svelte';
	
    const dispatch = createEventDispatcher();
	
    export let isOpenModal;
	
    function closeModal() {
        isOpenModal = false;
        dispatch('closeModal', { isOpenModal });
    }
</script>

<div id="background" style="--display: {isOpenModal ? 'block' : 'none'};" on:click={closeModal}></div>
<div id="modal" style="--display: {isOpenModal ? 'block' : 'none'};">
    <p>This is a modal window.</p>
</div>

<style>
    #background {
        display: var(--display);
        position: fixed;
        z-index: 1;
        top: 0;
        left: 0;
        width: 100vw;
        height: 100vh;
    }

    #modal {
        display: var(--display);
       	position: fixed;
        z-index: 2;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        background: #fff;
        filter: drop-shadow(0 0 20px #333);
    }
</style>
```