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), andvue-toastflowis just a renderer on top with<ToastContainer />+ a globaltoasthelper. - 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 withTransitionGroupand overridable viaanimationconfig. - Typed API, works inside and outside components
- You install the plugin once, then import
toastfrom 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), andclear-allin a predictable way. - Extras
- Promise/async flows (
toast.loading), optional HTML content withsupportHtml, lifecycle hooks, events (toast.subscribeEvents), timestamps (showCreatedAt,createdAtFormatter), and a headless slot API if you want to render your own card.
Quick taste
// main.ts
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 -->
<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>
Links
- Playground / demo: https://toastflow.adrianjanocko.sk
- GitHub: https://github.com/adrianjanocko/toastflow
- npm (Vue renderer): https://www.npmjs.com/package/vue-toastflow
Top comments (0)