Article

Simple modal component with Vue.js

Remco Hendriksen on Nov 9, 2020
scss javascript component events
Last updated: Dec 1, 2020

When building web applications, you need a modal at some point. A popup asking for confirmation before a critical action, a dialog showing validation errors or maybe a login form.

In this post you will learn to create one utilizing vue-slots and some scss. Before continuing this post i would recommend you to read this article about structuring your vue js application. The scss-variables used in our modal-component assume that you have your project setup this way.

The template

<template>
  <div class="modal">
    <div class="modal__bg" @click="$emit('close')" />
    <div class="modal__content">
      <div class="modal__close">
        <button @click="$emit('close')">&times;</button>
      </div>
      <slot />
    </div>
  </div>
</template>

The template of this component is really minimalistic. A root div with class modal with two children modal__bg and modal__content. The modal__content contains a slot and a close-button. The <slot /> is where your component's content will be placed.

If you use the modal-component this way:

<modal>
  <h1>Modal heading</h1>
  <p>With some model content</p>
</modal>

it will be parsed as:

<div class="modal">
  ...
  <div class="modal__content">
  ...
    <h1>Modal heading</h1>
    <p>With some model content</p>
  </div>
</div>

The $emit('close') is here to emit an event to the parent component so that it knows when to hide the component. (Assuming that we show and hide the component through a v-if statement).

Styling

<style lang="scss">
.modal {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  width: auto;
  height: auto;
  z-index: 1000;

  &__bg {
    background-color: rgba($black, 0.25);
    bottom: 0;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    opacity: 1;
    position: fixed;
    z-index: 1;
  }

  &__content {
    background-color: $white;
    color: $black;
    border-radius: $border-radius;
    display: flex;
    flex-direction: column;
    width: 95vw;
    height: 95%;
    max-height: 95vh;
    max-width: 800px;
    padding: $spacing * 4;
    position: relative;
    opacity: 1;
    z-index: 2;
  }

  &__close {
    position: absolute;
    right: $spacing * 2;
    top: $spacing * 2;
    z-index: 3;
  }
}
</style>

The root-element is going to be a full-window element with position: fixed applied, so that it always floats over every content on the screen. A high enough z-index is required here. display:flex with align-items: center and justify-content:center makes sure our modal__content is always centered on the screen.

width: 95vw;
height: 95%;
max-height: 95vh;
max-width: 800px;

For the styling of the model__content, use width, height, max-height and max-width to make the model responsive. What this does is make the content 800px wide at maximum. When the screen is smaller then 800px, it will show the content at 95% width. If this does not makes sense, please let me now in the comments below.

Behind the modal__content we put a modal__bg which fades out our background a little bit. We also make this one fixed to make it a full-window element. Give this element a z-index which is 1 less then the modal__content so that it is always behind the content element.

At last you are going to need a close button. Position the button with position:absolute; to the top right of the window. Because this the place where will expect it.

Full component

<template>
  <div class="modal">
    <div class="modal__bg" @click="$emit('close')" />
    <div class="modal__content">
      <div class="modal__close">
        <button @click="$emit('close')">&times;</button>
      </div>
      <slot />
    </div>
  </div>
</template>
<style lang="scss">
.modal {
  align-items: center;
  justify-content: center;
  display: flex;
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  width: auto;
  height: auto;
  z-index: 1000;

  &__bg {
    background-color: rgba($black, 0.5);
    bottom: 0;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    opacity: 1;
    position: fixed;
    z-index: 2;
  }

  &__content {
    background-color: $white;
    color: $black;
    border-radius: $border-radius;
    display: flex;
    flex-direction: column;
    width: 95vw;
    height: 95%;
    max-height: 95vh;
    max-width: 800px;
    padding: $spacing * 4;
    position: relative;
    opacity: 1;
    z-index: 2;
  }

  &__close {
    position: absolute;
    right: $spacing * 2;
    top: $spacing * 2;
    z-index: 3;
  }
}
</style>

You now have built a simple modal component. You can always expand your component if you need more buttons for example.

Leave a comment below if you have got any questions about this article.

Leave a comment

More articles