Skip to content

A basic modal component with Vue 3 and Tailwind CSS

Let's write a reusable modal with Vue and Tailwind CSS.

2 min read

Let's go for a simple base modal implementation that provides a backdrop and a centered <div> where we can put any content we need.

Example usage in BaseModalExample.vue:

BaseModalExample.vue
<script setup lang="ts">
import { ref } from 'vue'
import BaseModal from '@/components/modals/BaseModal.vue'
const showModal = ref(false)
</script>
<template>
<button
type="button"
class="bg-indigo-200 px-3 py-1 font-medium"
@click="showModal = true"
>
Show modal
</button>
<BaseModal :show="showModal">
<div class="p-4">
<div class="text-lg">Hello Modal World!</div>
<div class="py-2 text-sm">Click to close:</div>
<button
type="button"
class="bg-indigo-200 px-3 py-1 font-medium"
@click="showModal = false"
>
Hide modal
</button>
</div>
</BaseModal>
</template>

Let's see how to implement the BaseModal.vue component, shall we? :monocle:

Prerequisites

For this, you'll need a Vue 3 + TypeScript (+ Tailwind CSS) project.

You can set up one following the instructions here:
Build a Vue 3 + TypeScript dev environment with Vite 

Where modals go

It's recommended that we display our modals in a div at the bottom of our main HTML file to avoid other elements accidentally rendering on top of them.

Add a new #modals element to your index.html:

index.html
<body>
<div id="app" class="relative z-10"></div>
<!-- >> This one is new! << -->
<div id="modals" class="relative z-20"></div>
<script type="module" src="/src/main.ts"></script>
</body>

We also add a z-index value and position: relative to the #app and #modals layers.
Now our modals will show on top of everything else thanks to stacking contexts. 

Base modal

Let's add a src/components/modals/BaseModal.vue:

src/components/modals/BaseModal.vue
<script setup lang="ts">
defineProps<{
show: boolean
}>()
</script>
<template>
<!-- Render inside our `<div id="modals"></div>` in index.html -->
<Teleport to="#modals">
<!-- Show / hide the modal -->
<div v-if="show" class="">
<!-- The backdrop -->
<div class="fixed inset-0 bg-gray-900 opacity-40"></div>
<!-- Where the actual content goes -->
<div class="fixed inset-0 flex items-center justify-center">
<div class="bg-white text-black">
<slot></slot>
</div>
</div>
</div>
</Teleport>
</template>

This example provides some basic styling.
Feel free to add props or slots to configure the different aspects of a modal --- backdrop color, opacity, content background color, content text color, etc.

That's it! 🎉

References

More notes connected by topic, not algorithmic noise.