Installation

TIP

Like the name suggests, vue3-popper is written for Vue 3. There are no plans to support both Vue 2.x and Vue 3.x at the moment.

You can install Vue 3 Popper by opening your terminal in your project and running the following command:

yarn add vue3-popper
npm i vue3-popper

Global

You can import and register the component globally:

import { createApp } from "vue";
import Popper from "vue3-popper";

const app = Vue.createApp({});
app.component("Popper", Popper);

Component

Or use it on a case by case basis:

<template>
  <Popper content="This is the Popper content 🍿">
    <button>Trigger element</button>
  </Popper>
</template>

<script>
  import { defineComponent } from "vue";
  import Popper from "vue3-popper";

  export default defineComponent({
    components: {
      Popper,
    },
  });
</script>

Usage

You can add Popper to any of your elements or components. Just wrap them with Popper and use the content prop or slot for your popover.

Using the content prop

If your content is only a simple string, you can use the content prop:

<template>
  <Popper content="This is the Popper content">
    <button>Trigger element</button>
  </Popper>
</template>

Using the content slot

If your content is more complex, you can use the #content slot:

<template>
  <Popper>
    <button>Trigger element</button>
    <template #content>
      <div>This is the Popper content</div>
    </template>
  </Popper>
</template>

What about styles?

Popper only comes with some barebones styling by default, but it also uses a list of predefined CSS variables. You can overwrite these variables to suit your needs.

CSS variables

CSS variableExample value
--popper-theme-background-color#ffffff
--popper-theme-background-color-hover#ffffff
--popper-theme-text-colorinherit
--popper-theme-border-width1px
--popper-theme-border-stylesolid
--popper-theme-border-color#eeeeee
--popper-theme-border-radius6px
--popper-theme-padding16px
--popper-theme-box-shadow0 6px 30px -6px rgba(0, 0, 0, 0.25)

You can overwrite them any way you like, for example in a Vue component:

<template>
  <Popper content="This is the Popper content 🍿">
    <Button>Demo</Button>
  </Popper>
</template>

<style>
  :root {
    --popper-theme-background-color: #333333;
    --popper-theme-background-color-hover: #333333;
    --popper-theme-text-color: #ffffff;
    --popper-theme-border-width: 0px;
    --popper-theme-border-style: solid;
    --popper-theme-border-radius: 6px;
    --popper-theme-padding: 32px;
    --popper-theme-box-shadow: 0 6px 30px -6px rgba(0, 0, 0, 0.25);
  }
</style>

You could also create a theme.css file:

:root {
  --popper-theme-background-color: #333333;
  --popper-theme-background-color-hover: #333333;
  --popper-theme-text-color: #ffffff;
  --popper-theme-border-width: 0px;
  --popper-theme-border-style: solid;
  --popper-theme-border-radius: 6px;
  --popper-theme-padding: 32px;
  --popper-theme-box-shadow: 0 6px 30px -6px rgba(0, 0, 0, 0.25);
}

Import it:

import { createApp } from "vue";
import App from "./App.vue";
import "./theme.css"; // Magic happens here

createApp(App).mount("#app");

And your Popper is styled!

Dynamic theming

Using the CSS variables you could even add multiple themes to your popover.

.dark {
  --popper-theme-background-color: #333333;
  --popper-theme-background-color-hover: #333333;
  --popper-theme-text-color: white;
  --popper-theme-border-width: 0px;
  --popper-theme-border-radius: 6px;
  --popper-theme-padding: 32px;
  --popper-theme-box-shadow: 0 6px 30px -6px rgba(0, 0, 0, 0.25);
}

.light {
  --popper-theme-background-color: #ffffff;
  --popper-theme-background-color-hover: #ffffff;
  --popper-theme-text-color: #333333;
  --popper-theme-border-width: 1px;
  --popper-theme-border-style: solid;
  --popper-theme-border-color: #eeeeee;
  --popper-theme-border-radius: 6px;
  --popper-theme-padding: 32px;
  --popper-theme-box-shadow: 0 6px 30px -6px rgba(0, 0, 0, 0.25);
}
<template>
  <Popper
    :class="theme"
    arrow
    disableClickAway
    content="This is the Popper content 🍿"
  >
    <Button>Demo</Button>
  </Popper>
</template>

I don't want to use CSS variables

That's fine, you can always just apply your own styles, just make sure it's scoped and you use the :deep selector:

<template>
  <Popper arrow content="This is the Popper content 🍿">
    <Button>Demo</Button>
  </Popper>
</template>

<style scoped>
  :deep(.popper) {
    background: #e92791;
    padding: 20px;
    border-radius: 20px;
    color: #fff;
    font-weight: bold;
    text-transform: uppercase;
  }

  :deep(.popper #arrow::before) {
    background: #e92791;
  }

  :deep(.popper:hover),
  :deep(.popper:hover > #arrow::before) {
    background: #e92791;
  }
</style>

How can I wrap Popper with my own component?

It is generally a good idea to wrap 3rd party components like vue3-popper with your own local component. It gives you things like:

  • Modularity
  • Scalability
  • Ability to add custom styles or extensions
  • If you need to change something (even swap out vue3-popper for something else) you only need to do that once in your wrapper component.

Here's an example of how you can wrap vue3-popper with your own component:

TIP

Notice that in this example, hover, openDelay and closeDelay are all hardcoded. This is just to show how you can create an opinionated wrapper.

<template>
  <!-- Hardcoded "hover", "openDelay" and "closeDelay" -->
  <Popper v-bind="$attrs" hover openDelay="200" closeDelay="100">
    <template v-for="(_, slot) of $slots" v-slot:[slot]="scope">
      <slot :name="slot" v-bind="scope" />
    </template>
  </Popper>
</template>

<script>
  import { defineComponent } from "vue";
  import Popper from "vue3-popper";

  export default defineComponent({
    name: "MyPopperWrapper",
    components: {
      Popper,
    },
  });
</script>

You could then go on to define your styles etc. and use your component just like you would Popper:

<template>
  <MyPopperWrapper arrow placement="right">
    <Button>Demo</Button>
    <template #content>
      <div>This is the Popper content 🍿</div>
    </template>
  </MyPopperWrapper>
</template>

Reacting to Popper events

Sometimes you need to add some side-effects when closing/opening Poppers. You can use the built-in events for that:

<template>
  <Popper
    arrow
    @open:popper="openAlert"
    @close:popper="closeAlert"
    content="This is the Popper content 🍿"
  >
    <Button>Demo</Button>
  </Popper>
</template>

<script>
  import { defineComponent } from "vue";
  export default defineComponent({
    name: "PopperEvents",
    methods: {
      openAlert() {
        alert("Opening Popper!");
      },
      closeAlert() {
        alert("Closing Popper!");
      },
    },
  });
</script>

Using scoped slot properties

You can gain access to the close function for those edge cases. In this example we use the close function to dismiss the Popper when a button is clicked inside of it.

<template>
  <Popper arrow>
    <Button>Demo</Button>
    <template #content="{ close }">
      <Button @click="close">Close</Button>
    </template>
  </Popper>
</template>

Manually controlling the Popper

You can use the show prop to manually control the Popper. Other events (click, hover) are ignored when in manual mode.

<template>
  <div>
    <Popper arrow content="This is the Popper content 🍿" :show="showPopper">
      <Button>Demo</Button>
    </Popper>
    <div>
      <input id="toggle" type="checkbox" v-model="showPopper" />
      <label for="toggle">Toggle Popper</label>
    </div>
  </div>
</template>

<script>
  export default {
    name: "PopperManual",
    data() {
      return {
        showPopper: true,
      };
    },
  };
</script>

TIP

Vue 3 Popper has multiple useful props as well, check out the API docs for more info.