2022 / 07 / 31
2022 / 09 / 18
A basic modal component with Vue 3 and Tailwind CSS

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

frontend
vue 3
typescript
tailwindcss
demo
common ui

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:

<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:

<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:

<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! :tada:

References