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>