Kevin Dang
software engineerMaking a simple notification system
January 01, 2024I recently created a notification system in React and wanted to compose a light write-up here! I
won't delve fully into the implementation, but I'll touch upon some notable aspects below to provide
some soft guidance.
Firstly, what is a notification component?
A notification component usually appears subtly on a page to alert the user to some information; it
then typically self closes after a short time interval.
The appearance of a notification could
be a direct consequence of an action the user has performed (in which case the notification acts as
a feedback mechanism), or it could be an informative message like informing the user that a
background update is ready to be installed. Notifications alert the user of something but it is much
less obtrusive to the user experience than a modal for instance.
Side note: I came across
a toast
component while reading up on notification components. An interpretation I came across to
differentiate between the two is that the former does not require any sort of interaction from the
user and serves more as an informative component. On the other hand, a notification component often
requires the user to engage with it e.g. clicking on a button / CTA to do "something".
Thinking about the DX...
Designing the aesthetics of any component is always important, but what was interesting here was
thinking about how to build a notification system that would feel "good" to use for developers.
As mentioned earlier, a common use case for a notification component is to act as a
feedback mechanism. For example, we may want to show a success notification when a user adds
something to their basket on a e-commerce site. Or, if we go the other way, we might want to show an
error if something went wrong when trying to add the item to the basket.
With the above
in mind, we'll need to way to create notifications on the fly - developers should be able to create
a notification in their specific scenario and expect it to surface to the user.
After
some iterations, I ended up with something that looks like this:
const notification = notificationManager.create('<NOTIFICATION_TYPE>', {
heading: 'Hello world',
message: 'Your first notification',
});
I felt that the interface of the .create
parameter was quite neat. The first parameter takes in
the type of notification you want to create, and the second parameter is the configuration object
e.g. heading, message, timeout, action, etc..
The next scenario I wanted to cover was
transitioning to a different type of notification. Examples I saw online simply pushed a new (and
separate) notification to communicate the notification change. To illustrate this point, imagine a
user has just fired off an asynchronous action; an initial notification would then be shown to the
user; once the action completes, a new notification would be "stacked" on top of the initial one.
There's nothing wrong with this approach, but I wanted to create a way in which the initial
notification could be updated into a new one.
Having an update method on the notification
instance seemed logical! So I ended up with something like the below:
notification.update({
type: '<SOME_OTHER_TYPE>',
heading: 'Notification updated',
message: 'Action was a success!',
});
In its simplest form, I found that exposing these two methods would suffice for getting a
small notification system to function. I did also have a close
method on the notification manager
but this was just to handle the self-closing functionality - I needed a way to "close" a
notification when I didn't have the notification instance in the same context - and I did not expect
the user to have to worry about this method at all.
Notification Manager
As you saw above, the first snippet contains a notificationManager
. This was a custom utility hook
I created that would allow developers to "instantiate" a notification manager to manage creating and
updating notifications.
Essentially, the final code would look something like this:
const notificationManager = useNotificationManager();
const someClickHandler = async () => {
const notification = notificationManager.create('info', {
heading: 'Saving',
message: `We're saving your work - hold tight...`,
});
// Fire off some async action
await save();
// After async action resolves, update notification
notification.update({
type: 'success',
heading: 'Saved! ✅',
message: 'We successfully saved your work',
});
};
Defining a custom hook like this is a nice way to re-use the logic across your app!