Global toast in Vue3
Tags: vue, vue3, component, composable
Published: 2024-04-18 19:53:51

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>

```

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,
    }
}

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

```
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>
```
![Toast from page](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9vndnzjbl8x6po6yknwo.png)

- 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>
```
![Toast from component](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rmpgnx2r4i1mu2rorkep.png)

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