Skip to content

Instantly share code, notes, and snippets.

Last active March 23, 2024 08:48
Show Gist options
  • Save eolant/ba0f8a5c9135d1a146e1db575276177d to your computer and use it in GitHub Desktop.
Save eolant/ba0f8a5c9135d1a146e1db575276177d to your computer and use it in GitHub Desktop.
Vuetify Confirm Dialog component that can be used locally or globally
<v-dialog v-model="dialog" :max-width="options.width" :style="{ zIndex: options.zIndex }" @keydown.esc="cancel">
<v-toolbar dark :color="options.color" dense flat>
<v-toolbar-title class="white--text">{{ title }}</v-toolbar-title>
<v-card-text v-show="!!message" class="pa-4">{{ message }}</v-card-text>
<v-card-actions class="pt-0">
<v-btn color="primary darken-1" text @click.native="agree">Yes</v-btn>
<v-btn color="grey" text @click.native="cancel">Cancel</v-btn>
* Vuetify Confirm Dialog component
* Insert component where you want to use it:
* <confirm ref="confirm"></confirm>
* Call it:
* this.$'Delete', 'Are you sure?', { color: 'red' }).then((confirm) => {})
* Or use await:
* if (await this.$'Delete', 'Are you sure?', { color: 'red' })) {
* // yes
* }
* else {
* // cancel
* }
* Alternatively you can place it in main App component and access it globally via this.$root.$confirm
* <template>
* <v-app>
* ...
* <confirm ref="confirm"></confirm>
* </v-app>
* </template>
* mounted() {
* this.$root.$confirm = this.$
* }
export default {
data: () => ({
dialog: false,
resolve: null,
reject: null,
message: null,
title: null,
options: {
color: 'primary',
width: 290,
zIndex: 200
methods: {
open(title, message, options) {
this.dialog = true
this.title = title
this.message = message
this.options = Object.assign(this.options, options)
return new Promise((resolve, reject) => {
this.resolve = resolve
this.reject = reject
agree() {
this.dialog = false
cancel() {
this.dialog = false
Copy link

eolant commented Jun 8, 2020

@eliyahuKriel hmm, not sure about your code, but you should use an instance app:

export const app = new Vue({
  render: h => h(require('$comp/App'))

Copy link

God bless you my friend, you have helped me very much

Copy link

great ...!!!

Copy link

Cerceis commented Nov 18, 2020

Thanks, this was helpful.

In the description to use this globally, I think this part:

mounted() {
   *   this.$root.$confirm = this.$;`

Should actually be this:

mounted() {
   *   this.$root.$confirm = this.$refs.confirm;

Along with this post.
When using it as global component(registered in App.vue)
I need to use *await this.$root.$'Delete', 'Are you sure?', { color: 'red' })
instead of *await this.$refs.$'Delete', 'Are you sure?', { color: 'red' })

Copy link

activeliang commented Nov 28, 2020

How to use in general JS files or use it in global
Called in custom modules, such as router.js, api.js, custom.js

My solution:
Create a new file: confirm.js

import Confirm from './confirm.vue' 
import Vue from 'vue'

const instance = new (Vue.extend(Confirm))()

  instance.$vuetify = { breakpoint: {}}

export default instance

Then in main.js:

import Confirm from './cofirm.js'
Vue.prototype.$confirm = Confirm

Then in the js file you want to call:

import Confirm from './cofirm.js''Delete', 'Are you sure?', { color: 'red' }).then((confirm) => {})


It is worth noting:

  1. The v-dialog component must provide the dark parameter to use the dark theme
  2. The purpose of instance.$vuetify = { breakpoint: {}} in the above example is to remove the error message Error in callback for watcher "isActive": "TypeError: Cannot read property 'smAndDown' of undefined

It could be called both in Vue instance context:

this.$'Delete', 'Are you sure?', { color: 'red' }).then((confirm) => { console.log('close') })

Copy link

atreya2011 commented Jan 7, 2021

Here is the TypeScript version

import { defineComponent, reactive, toRefs } from "@vue/composition-api";

interface Options {
  color: string;
  width: number;
  zIndex: number;

export default defineComponent({
  setup() {
    const state = reactive({
      dialog: false,
      resolve: (val: boolean) => {},
      reject: (val: boolean) => {},
      message: "",
      title: "",
      options: {
        color: "primary",
        width: 290,
        zIndex: 200,

    const open = (title: string, message: string, options: Options) => {
      state.dialog = true;
      state.title = title;
      state.message = message;
      state.options = Object.assign(state.options, options);
      return new Promise<boolean>((resolve, reject) => {
        state.resolve = resolve;
        state.reject = reject;

    const agree = () => {
      state.dialog = false;

    const cancel = () => {
      state.dialog = false;

    return {

Call it this way using TypeScript:

    <v-btn icon color="red" @click="openConfirm">
    <confirm ref="veeConfirmRef"></confirm>
<script lang="ts">
import { onMounted, ref } from "@vue/composition-api";
import Confirm from "@/components/Confirm.vue"

export default {
  components: {

  setup() {
    const veeConfirmRef = ref<InstanceType<typeof Confirm>>();

    onMounted(() => {
      console.log('page onMounted')

    const openConfirm = ()=> {
        .open('Delete', 'Are you sure?', { color: "error" })
        .then(confirm => {
          console.log("onBeforeDeleteItem confirm : " + confirm);


Copy link

StarWar commented Apr 3, 2021

I'm getting the following error after upgrading some dependencies i.e nuxtjs.

TypeError: Cannot read property 'open' of undefined
    at VueComponent.mounted (default.vue?ec86:160)
    at invokeWithErrorHandling (vue.runtime.esm.js?2b0e:1854)
    at callHook (vue.runtime.esm.js?2b0e:4219)
    at Object.insert (vue.runtime.esm.js?2b0e:3139)
    at invokeInsertHook (vue.runtime.esm.js?2b0e:6346)
    at Vue.patch [as __patch__] (vue.runtime.esm.js?2b0e:6565)
    at Vue._update (vue.runtime.esm.js?2b0e:3945)
    at Vue.updateComponent (vue.runtime.esm.js?2b0e:4060)
    at Watcher.get (vue.runtime.esm.js?2b0e:4479)
    at new Watcher (vue.runtime.esm.js?2b0e:4468)
    The error is at following lines
    mounted() {
    this.$root.$confirm = this.$
    this.$root.$toast = this.$

Copy link

this.$root.$confirm = this.$

Having same issue. How can we use this in nuxt globally. something like this.$root.$

Copy link

Just to add to the comments .. I registered this component globally based off of the Vue 2 documentation and use it all over vs adding it to the App.vue. This way I was able to use it as initially described.

// in your main.js before you instantiate your new Vue({}) application:
import ConfirmDialog from "@/components/shared/ConfirmDialog"
Vue.component('ConfirmDialog', ConfirmDialog)

// in <template>:
// <ConfirmDialog ref="confirm"></ConfirmDialog>

// Call it:
this.$'Delete', 'Are you sure?', { color: 'red' }).then((confirm) => {})
// Or use await:
if (await this.$'Delete', 'Are you sure?', { color: 'red' })) {
  // yes
} else {
  // cancel

Copy link

wobsoriano commented Jul 18, 2021

If you are using the Composition API plugin, you can use provide/inject plus a useDialog composable that injects the show dialog function. Cleanest and TypeScript friendly!

Here's how you would call it:

<script lang="ts">
import { defineComponent } from '@nuxtjs/composition-api';
import { useDialog } from '~/composables';

export default defineComponent({
  setup() {
    const createConfirmDialog = useDialog();

    const handleDelete = async () => {
      try {
        const shouldProceed = await createConfirmDialog(
          'Delete this post?',
          { width: 300 }
      } catch (_e) {}

    return {

Full implementation here:

Copy link

jtgasper3 commented Sep 5, 2021

Standing on the shoulders of the giants before me, I was able to get a global confirmation function working with Nuxt.js:

  1. In the layout component where the dialog is place:

    <app-confirmation-dialog ref="confirm" />
      //because I'm using Class based components I used an `@Ref()` shorthand, but using $refs in the RH side of the assignment below should work too:
      readonly confirm!: AppConfirmationDialog
      // the real magic??
       mounted(): void {
        this.$nuxt.$root.$confirm =
  2. Its usage in other components:

    await this.$root.$confirm(
            'Skip Day',
            'Are you sure you want to toggle the Skip Day status?',
              color: 'red',
  3. Most likely because I am using Typescript I had to add a Nuxt plugin with:

    import { Options } from '@/types'
    declare module 'vue/types/vue' {
      interface Vue {
        $confirm(title: string, message: string, options: Options): Promise<boolean>
  4. and I had a type defined in a file shared between the plugin and the other components:

    export interface Options {
      color?: string
      width?: number
      zIndex?: number

Copy link

Why use @click.native when @click seems to do the trick? Is there any scenario when a simple @click is not enough?

Copy link

If you need to implement it on vue3+vuetify3, you can refer to this article

Copy link

mepanko91 commented Feb 22, 2024


Add this function inside confirm dialog component so it will resolve with false if dialog is closed by clicking outside

function atDialogUpdate(val) {
  if (!val) {

Copy link

pkhuji commented Mar 23, 2024

If using in multiple places then confirmDialog component can be placed in root #app component like this

<div id='#app'>
  <confirmDialog :ref='store.setConfirmDialogRef' />

then in pinia store or any shared state

state: () => ({
  confirmDialogRef: null,
actions: {
  setConfirmDialogRef(val) {
    this.confirmDialogRef = val;

The store.setConfirmDialogRef method will update ref in store.
then in any function

async function submit() {
  if (!await
    "You sure?",
  )) {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment