DEV Community

Hélio Marcondes
Hélio Marcondes

Posted on

Global toast in Vue3

Hi there.

Today I will share the way I built a simple Toast component using Vue3 composables.

Recently, I had to create a project from scratch using Nuxt3 + TailwindCSS. In the middle of it, I needed a simple Toast component to handle some actions. So, after some research, I ended up with this component.

1- Creating the Toast component involves a bit of styling with TailwindCSS classes and custom CSS to control how the Toast appears.

Toast.vue

<script setup lang="ts">
defineProps({
    message: {
        required: false,
        type: String,
    },
    visibility: {
        required: true,
        type: Boolean,
    },
})
</script>

<template>
    <div v-if="visibility"
        class="toast z-50 fixed top-4 right-4 flex items-center gap-4 mb-4 text-gray-200 transition-all duration-[3000ms] w-80 overflow-hidden">
        <div class="p-3 w-full rounded-tl-none rounded-bl-none">
            <div class="flex items-center w-full">
                <div class="flex flex-col">
                    <div class="toast__title text-sm text-green-800">Success</div>
                    <div class="toast__text text-sm text-green-500">{{ message }}</div>
                </div>
            </div>
        </div>
    </div>
</template>

<style scoped>
.toast {
    box-shadow: 0px 4px 14px 0px rgba(0, 0, 0, 0.18);
    border-radius: 5px 0px 0px 5px;
    border-left: 7px solid #1fac66;
    width: 429px;
}
</style>

Enter fullscreen mode Exit fullscreen mode

2- Creating the composable
I've created two functions to handle the logic of the Toast component: one to add the message inside the component and another to reset it. I'm also using the useState composable from Nuxt to keep the variables visibility and messages available.

useToast.ts

export function useToast() {
    let visibility = useState('visibility', () => false)
    let messages = useState<string[]>('messages', () => [])

    function resetMessages() {
        messages.value = []
    }

    function addMessage(message: string) {
        if (message) {
            messages.value.push(message)
            visibility.value = true
            setTimeout(() => {
                visibility.value = false
                resetMessages()
            }, 3000)
        }
    }

    return {
        messages,
        addMessage,
        visibility,
    }
}

Enter fullscreen mode Exit fullscreen mode

3- In the root component of the project (app.vue in my case), I import the component and the composable to work together. In this example, there are some extras that I added for layout purposes:

app.vue

<script setup lang="ts">
const { messages, visibility } = useToast()
</script>
<template>
  <NuxtLayout>
    <NuxtPage></NuxtPage>
  </NuxtLayout>

  <TransitionGroup name="toast">
    <Toast v-for="(message, index) in messages" :key="index" :message="message" :index="index"
      :visibility="visibility" />
  </TransitionGroup>
</template>

<style>
.toast-enter-from,
.toast-leave-to {
  opacity: 0;
  transform: translateY(-60px);
}

.toast-enter-to,
.toast-leave-from {
  opacity: 1;
  transform: translateY(0);
}

.toast-enter-active,
.toast-leave-active {
  transition: all 0.3s ease;
}
</style>

Enter fullscreen mode Exit fullscreen mode

4- To see it working, you can simply call it from any page or component that you need. Here are two examples:

  • To trigger the modal from a page, you can use the following code:

index.vue

<script setup lang="ts">
const { addMessage } = useToast()

function triggerModal() {
    addMessage('My Amazing modal from Index.vue')
}
</script>

<template>
    <div>
        <div>This is my <b>index.vue</b> page</div>
        <button @click="triggerModal()">Trigger Modal</button>
    </div>
</template>
Enter fullscreen mode Exit fullscreen mode

Toast from page

  • To trigger the modal from a random component, you can use the following code:

myRandomComponent.vue

<script setup lang="ts">
const { addMessage } = useToast()

function triggerModal() {
    addMessage('My Amazing from component')
}
</script>
<template>
    <div>
        <div>My test component</div>
        <button @click="triggerModal()">Trigger Modal from my Component</button>
    </div>
</template>
Enter fullscreen mode Exit fullscreen mode

Toast from component

If you have any suggestions, please let me know. Thank you so much for reading!

Top comments (0)