Tired of Vue toast libraries, so I built my own (headless, Vue 3, TS-first)
Hey folks đź‘‹ author here, looking for feedback.
I recently needed a toast system for a Vue 3 app that was:
• modern
• lightweight
• and didn’t fight my custom styling
I tried several Vue toast libraries and kept hitting the same issues: a lot of them were Vue 2–only or basically unmaintained, the styling was hard-wired instead of properly themeable, some were missing pretty basic options, and almost none gave me predictable behavior for things like duplicates, timers, or multiple stacks.
So I ended up building my own: Toastflow (core engine) + vue-toastflow (Vue 3 renderer).
WHAT IT IS?
• Headless toast engine + Vue 3 renderer
Toastflow keeps state in a tiny, framework-agnostic store (toastflow-core), and vue-toastflow is just a renderer on top with <ToastContainer /> plus a global toast helper.
• CSS-first theming
The default look is driven by CSS variables (including per-type colors like --success-bg, --error-text, etc.). You can swap the design by editing one file or aligning it with your Tailwind/daisyUI setup.
• Smooth stack animations
Enter/leave + move animations when items above/below are removed, for all positions (top-left, top-center, top-right, bottom-left, bottom-center, bottom-right). Implemented with TransitionGroup and overridable via animation config.
• Typed API, works inside and outside components
You install the plugin once, then import toast from anywhere (components, composables, services, plain TS modules). Typed helpers: toast.show, toast.success, toast.error, toast.warning, toast.info, toast.loading, toast.update, toast.dismiss, toast.dismissAll, etc.
• Deterministic behavior
The core handles duplicates, timers, pause-on-hover, close-on-click, maxVisible, stack order (newest/oldest), and clear-all in a predictable way.
• Extras
Promise/async flows (toast.loading), optional HTML content with supportHtml, lifecycle hooks, events (toast.subscribeEvents), timestamps (showCreatedAt, createdAtFormatter), and a headless slot API if you want to render your own card.
QUICK TASTE
import { createApp } from 'vue'
import App from './App.vue'
import { createToastflow, ToastContainer } from 'vue-toastflow'
const app = createApp(App)
app.use(
createToastflow({
// optional global defaults
position: 'top-right',
duration: 5000,
}),
)
// register globally or import locally where you render it
app.component('ToastContainer', ToastContainer)
app.mount('#app')
Somewhere in your app (single-file component):
<script setup lang="ts">
import { toast } from 'vue-toastflow'
function handleSave() {
toast.success({
title: 'Saved',
description: 'Your changes have been stored.',
})
}
</script>
<template>
<button @click="handleSave">Save</button>
<ToastContainer />
</template>

Replies