This took me a lot longer to figure out than I feel it should have, so as always, I thought I'd write about it.
I've been working on a project where a Svelte application integrates with an Auth server. If the app discovers that the user is logged out, it displays a modal prompting the user to login. When the clicks the login button, a new tab is open, prompting the user through the login process, redirecting to the app, then closing the tab. The modal checks for the user's JWT using an interval timer, and closes the tab once it is set, allowing the user to continue to use the application exactly where they left off.
The system works great, and I've built something similar in multiple UI frameworks. But, writing unit tests for this in Svelte with Vitest turned out to be a much harder task than I expected. I'm gonna write a few blog posts on this. this is the first one that will tell you how to create and use the modal.
The Setup
First, create a Svelte app following the instructions.
Then setup Flowbite-Svelte, a UI Component Library built for Tailwind and Svelte.
Or you can just check out this repo for these samples and follow along.
Specific steps are beyond the scope of this of this article.
Create the Modal
Create a new Svelte component, MyModal.svelte. First, import the Modal and Button from Flowbite Svelte:
2 import { Modal, Button } from "flowbite-svelte";
3</script>
Then, create the Modal template:
2</Modal>
We gave the Modal a title and a size, which is a Tailwind convention defining this as a mid-size modal. We also gave it the permanent property, which prevents the modal from being closed--although in my experience that does not quite work yet. Then we bind to a property named opened. This is how we determine whether or not the modal is displayed as part of the app, or completely hidden.
Back to TypeScript, create an input property to open or close the Modal:
Then create a open property, using the $state rune:
And finally, add an effect so that when the isModalOpen input prop changes, so does the opened state:
Back to the HTML section of this component, let's populate the rest of the Modal. Give it a body:
2 This is a modal example using Flowbite-Svelte.
3 </p>
This is just some HTML and Tailwind styles.
Then give it a footer:
2 <div class="flex justify-end">
3 <Button color="gray" onclick={doSomething}>Do Something</Button>
4 </div>
5 {/snippet}
The footer includes the Flowbite Svelte button, when clicked runs the doSomething () method. Create that in our TypeScript block:
2 const timer = setInterval(() => {
3 opened = false;
4 clearInterval(timer);
5 }, 1000)
6}
The click handler method sets an interval() and use that to close the modal. In my real-world use case, the method would start the user authentication flow to reset the User's JWT and then do a cookie check inside the interval. But, for the purposes of this sample, we're just gonna simulate that with an automatic delayed close.
Use the Modal
Now, move to your default Svelte page, +page.svelte, and set up the Modal. First, some imports in your TypeScript block:2 import { Button } from "flowbite-svelte";
3 import MyModal from "$lib/MyModal.svelte";
4</script>
Then in the HTML portion of the template set up a button that will show the Modal, and the actual Modal:
2 <Button onclick={openModal}>
3 Show Modal
4 </Button>
5
6 <MyModal bind:isModalOpen={opened}></MyModal>
7</div>
We need to add two items to the TypeScript block, from this: The openModal() Click Handler, and the opened state value:
Run the app, and you should see the Modal pop up, then close shortly thereafter:

Final Thoughts
Building the Modal was very easy, but when it came time to write unit tests that was a whole other story. In the next two parts of this article, I'm going to show you how I wrote tests for this using both fake timers, and real timers.
