:bell: Powerful toast notification system for Vue and Nuxt.
The next minor release brings a new exciting feature, plently of improvements and a non-breaking but important change to the API.
At this point, I believe Notivue is feature-complete and most likely there won't be new features in future releases besides improvements and fixes.
useNotivueInstance
Consumers can now fully control the Notivue instance by stopping and restarting it.
In .vue
components:
<script setup lang="ts">
import { useNotivueInstance } from 'notivue'
const { isRunning, startInstance, stopInstance } = useNotivueInstance()
</script>
In any other module:
import { startInstance, stopInstance } from 'notivue'
This allows the users of an app, for example, to properly enable and disable notifications in total autonomy.
Before this release, this was not "officially" supported and it could be achieved for example, by mounting/unmounting <Notivue />
(e.g. using v-if
) but this was not optimal as it would have not stopped the store from "working" in the background, processing unwanted notifications and wasting resources.
This is now done the right way: when the instance is stopped, the whole store is reset, any watcher is detached, any update to the config either via direct setter or via update()
is gracefully proxied without throwing errors. Same for push
, which is made no-op when the instance is stopped.
Finally, notivue
can now be initialized with a startOnCreation
option (which defaults to true
):
import { createNotivue } from 'notivue'
const notivue = createNotivue({
startOnCreation: false,
// Other options
})
If set to false
, Notivue won't be started when the app is mounted.
This can be useful to make sure that for some users Notivue is never enabled or to only start it when the environment is ready:
import { updateConfig, startInstance } from 'notivue'
fetch('/api/user-preferences')
.then((res) => res.json())
.then((data) => {
if (data.notifications.isEnabled) {
startInstance()
updateConfig(data.notifications.config)
}
})
transition
config property and disable automatic transition property detectionBefore this release Notivue automatically computed the transition property added to the containers when repositioning them by "reading" the animationDuration
and animationTimingFunction
properties of the enter animation as soon as the first notification was rendered.
This logic required about 100 lines of code and added some complexity that could definitely be avoided as the majority of users are not customizing animations at all. This also made the package smaller and more efficient.
Starting from this release, Notivue won't compute anymore the transition property automatically, instead it should be defined manually in the configuration if using custom animations.
const notivue = createNotivue({
transition: 'transform 200ms ease-out',
})
Check the docs website for more info.
Notivue handles repositioning of the notifications using a custom approach via CSS transitions and inline styles. Internally, the store includes a method named updatePositions
that is called to compute and apply those styles everytime the notifications must be repositioned (creation, clearing, destroy, size-change, etc).
Before this release, such method was called a couple of times more than necessary on both creation and clearing, creating some unnecessary overhead.
By adding a tiny logic that keeps track of the lifecycle events (creation, clearing and destruction), I was able to move the updatePositions
logic to a single place of the store in order to make sure that repositoning now happens only once per each of those events.
Notivue uses the ResizeObserver
API to keep track of the size of the containers and to reposition them if needed, for example, when the content of a notification is updated dynamically, all the notifications must be repositioned.
Before this release, the ResizeObserver
callback would have repositioned the notifications when they were about to be destroyed, if their size never changed. This was totally unnecessary as it was already handled (as explained in the previous section) and it has been taken care of.
pauseOnTabChange: false
Before this release, if pauseOnTabChange
was disabled in the config, the notifications would have been immediately destroyed when the window was blurred.
I realized that this not optimal as an user would expect the notifications timeouts to be kept alive and running.
This has been taken care of and the behavior when pauseOnTabChange
is disabled is now basically the same as if the user din't left the app.
Notivue automatically syncs the transition duration and easing used for repositioning with your enter animation duration and easing. Before this release the approach used was too optimistica and could fail in some edge cases. This has been fixed by using requestAnimationFrame
.
The next minor release of Notivue introduces a couple of cool features plus some improvements:
avoidDuplicates
config optionisStreamPaused
internal signalNotificationProgress
drop-in componentfocus/blur
events instead of visibilityChange
--nv-accent
CSS variableThe above new features can be summarized in this short video:
https://github.com/smastrom/notivue/assets/60471784/0835175d-131c-4e47-82c5-b100859d018e
avoidDuplicates
config optionIt is now possible to prevent the creation of notifications already displayed in the stream or already awaiting in the queue (duplicates).
I've found this feature extremely useful in scenarios where the same notification might be repeatedly created (external services errors, frequently updated settings), creating unnecessary noise in the UI.
Since the whole implementation required just a few lines of code, I believed it was a good trade-off implementing it.
While this option is disabled by default, it can be enabled by setting avoidDuplicates
to true
in Notivue's config:
const notivue = createNotivue({
avoidDuplicates: true
})
Once enabled, if a notification like the following is pushed to the stream:
push.error({
title: 'Error',
message: 'There was an error sending your message, please try again.'
})
...and a notification with the same title
, message
and type
is already displayed, Notivue will immediately make screen readers announce it again without rendering a new one.
Then, it will replace the remaining duration of the visible notification with the duration of the duplicate one (like "restarting" it).
:bulb: By design, this feature only supports static notification types (success, error, warning, info) and not dynamic types (promise, promise-resolve, promise-reject).
If using Custom Components, it is possible to "detect" when a duplicate is pushed by watching the duplicateCount
property of the notification item (in your custom component setup function):
watch(
() => props.item.duplicateCount,
(newVal) => {
console.log(`Total duplicates: ${newVal}`)
}
)
For example, the above property could be used as component key to play/restart an animation everytime a duplicate of that notification is pushed (and its duration is updated).
isStreamPaused
internal signalNotivue now exposes a readonly reactive proxy of its internal isStreamPaused
signal, enabling consumers using Custom Components to create progress bars and advanced side-effects.
Check the docs website on how to implement a progress bar in your custom notification.
<NotificationProgress />
drop-in componentNotivue's built-in Notifications now accept a default slot which can be used to add a new drop-in component named NotificationProgress
(and anything else you'd like to) which fully leverages the new APIs introduced above.
All you have to do is to import it and pass the notification item to the item
prop:
<script setup>
import { Notivue, Notification, NotificationProgress } from 'notivue'
import 'notivue/notification-progress.css'
</script>
<template>
<Notivue v-slot="item">
<Notification :item="item">
<NotificationProgress :item="item" />
</Notification>
</Notivue>
<!-- RouterView, etc. -->
</template>
:bulb: Please note that the CSS must also be imported either in
app.vue
or your app entry point.
This release brings few minor improvements to <Notifications />
and <NotivueSwipe />
.
Notification
export as alias of Notifications
in order to use it as the new default import mentioned in the documentation.
I have come to the conclusion that users may think they're importing a list, whereas they're actually importing a single notification.<animate>
with pure CSS keyframes animation.--nv-y-align-has-title
theme variable. It is now possible to set a different vertical align that only applies to notifications with title. The variable is set to center
by default.<Notification
:item="item"
:theme="{ ...lightTheme, '--nv-y-align-has-title': 'flex-start' }"
/>
In addition to that, it is now possible to conditionally style the notification elements by using the attribute data-notivue-has-title
,
.Notivue__notification[data-notivue-has-title='true'] {
/* */
}
It is now possible to disable the built-in Teleport feature by passing false
to the teleportTo
config option:
const notivue = createNotivue({
teleportTo: false
})
<Notivue />
renders three elements with the following structure:
List container — ol
└── List items — li[]
└── Item container - div
└── Notification - slot
In order to orchestrate the stream according to your config, Notivue computes and adds some inline styles to the ol
, li
and div
elements. No classes nor other magic is involved, just those styles.
Since this release, it is now possible to define your own styles that take precedence over the default ones by passing a styles
object to the omonimous <Notivue />
prop:
<Notivue
:styles="{
list: {
position: 'relative'
height: '100%'
},
listItem: {},
itemContainer: {}
}"
v-slot="item"
>
<Notifications :item="item" />
</Notivue>
Both new features give you total control over the positioning, allowing, for example to render the stream relatevely to a specific container with a position other than fixed
.
The way Notivue handles pause and resume timeouts has been rewritten from scratch. The 60% of the involved code has been removed resulting in a much cleaner and clearer codebase.
The timeouts clearing logic has also been refactored. Compared to the previous releases, when a notification is about to be cleared, all the timeouts related to it are now cleared before triggering the leave animation and destroying the notification.
The first minor release of Notivue v2, ships a new feature, fixes a NotivueSwipe bug and brings various improvements to the built-in notifications component.
update
methodThe object returned by useNotivue
now includes an update
method that can be used to update the configuration. Before this release it was only possible to update it by directly assigning a value to each returned ref:
Pre v2.1.0
const config = useNotivue()
config.postion.value = 'bottom-right'
config.enqueue.value = true
v2.1.0
const config = useNotivue()
config.update({
position: 'bottom-right',
enqueue: true,
})
New options will be merged to your initial configuration and so on. Alternatively, the same method also accepts a function that returns new options and exposes the current configuration as argument:
config.update((currConfig) => ({
enqueue: !currConfig.enqueue
}))
To update the configuration from outside the setup context, you can use the updateConfig
utility which has the same signature of the above-mentioned update
method:
import { updateConfig } from 'notivue'
NotivueSwipe - Fixed a bug where the computation of isPointerInside
was not performed correctly.
promise-resolve
and promise-reject
icons appear using Vue Transition component.prefers-reduced-motion
is set to reduce
slateTheme
foreground color.Before wishing happy new year to everyone and going for a bit off until the beginning of January, I wanted to release Notivue v2.0.0
.
This release unifies usage between Vite, Nuxt and Astro setups by bringing an isomorphic API that deprecates usePush
and exports push
from notivue
by finally allowing it to be called from anywhere no matter what framework is being used.
While usePush
has been deprecated, it is still exported from notivue
to facilitate the migration. For Nuxt setups, upgrading to v2.0.0 shouldn't break your code at all.
Unfortunately, for Vite-based setups an edit in both your main.js
and any file where you imported push
is required.
main.js
import { createNotivue } from 'notivue'
import { createApp } from 'vue'
import App from './App.vue'
+ const notivue = createNotivue(/* options */)
const app = createApp(App)
- export const push = createNotivue(app, /* options */)
+ app.use(notivue)
app.mount('#app')
In any of your files:
- import { push } from '@/main'
+ import { push } from 'notivue'
push.success('Hello from Notivue 2.0.0')
All you have to do is to remove this statement from anywhere in your code:
- const push = usePush()
push
is now auto-imported directly from notivue
and can be called from anywhere without worrying about the setup context.
notivue/astro
This package now exports Astro-dedicated versions of Notivue
and push
from notivue/astro
meant to connect the Notivue store to Astro client code using Custom Events.
This makes possible to push notifications from anywhere: script tags, React components, Vue components, you name it. Persistence of the notification stream across page navigation is also supported when using View Transitions.
Check the installation guide in the docs.
Promise-based notifications can now be pushed using the load
method alternatively to the promise
one and updated using success
and error
methods alternatively to resolve
and reject
. After using this package for a bit in my professional work, I realized that calling promise
, resolve
and reject
was a bit odd.
let notification = push.promise('Loading...')
notification.resolve('Done!')
// Same
let notification = push.load('Loading...')
notification.success('Done!')
let notification = push.promise('Loading...')
notification.reject('Error!')
// Same
let notification = push.load('Loading...')
notification.error('Error!')
triggerRef
when not necessaryenterClass
when completed. This prevents the enter animation to be played again when navigating between pages using Astro View Transitions.notivue
plugin. The compatibility fallback released in v1 is not anymore exported.useNotivueConfig
composable alias.popIn
animation applied to built-in notifications icon.NotivueSwipe - Improved stream pausing/resuming after touch interactions. Interactions that may trigger a clear or a position reset will now schedule the resumal using debounce instead of immediately performing it.
This gives an overall better UX by avoiding that:
While this behavior worked great with mouse interactions because the stream is always paused on hover, it wasn't really suitable for touch interactions.
Affected interactions and debounce timings:
NotivueSwipe - Fix isPointerInside
computation, which paused the stream after releasing a notification if the mouse pointer was still hovering a notification.
NotivueCore - Added will-change: transform
when repositioning the notifications. This improves performance on Android Firefox which was the only environment "suffering" from it since the last minor release.
Fixed a regression introduced in v1.4.3 where the transition data computation failed in nuxt middlewares or initial page load.
Time elapsed between v1.4.3 and v1.4.4: 1 hour and 30 minutes.