React notification made easy π !
MIT License
Bot releases are visible (Hide)
The code for this release has been sitting on my computer for almost a year but with so many things going on it was hard for me to release it but it's finally there! A good chunk of the code has been rewritten, a bunch of bugs have been fixed. I've also addressed the oldest open feature request (Jan 10, 2020) π.
By default, when the notification is hovered or the window loses focus, the timer for dismissing the notification is paused. There are many other situations where you might want to pause the timer as well. For instance, consider wanting to toggle the notification timer based on the document's visibility. This wasn't possible to do previously, but with the new API, it's a breeze.
document.addEventListener("visibilitychange", () => {
if (document.visibilityState === "visible") {
toast.play({ id: "123" });
} else {
toast.pause({ id: "123" });
}
});
More usages:
toast.play()
toast.pause()
toast.play({ containerId: "123" })
toast.pause({ containerId: "123" })
toast.play({ id: "123" })
toast.pause({ id: "123" })
toast.play({ id: "123", containerId: "12" })
toast.pause({ id: "123", containerId: "12" })
This feature was the oldest one in the backlog (Jan 10, 2020). I don't know if the user who requested this feature is still using the library but I bet thanks to her/him, a bunch of user will be happy.
toast.dismiss({ container: "123" })
toast.dismiss({ id: "123", containerId: "12" })
The method is backward compatible. toast.dismis()
and toast.dismiss("123")
work as usual.
You can limit the call to toast.isActive
to a specific container.
toast.isActive(toastId, containerId)
When providing data to the notification, the content of data is correctly infered by typescript.
toast((props) => {
return <div>{props.data.foo}</div>
},{
data: {
foo: "bar"
}
})
When providing your own logic to display the icon, you now have access to the isLoading
field.
const CustomIcon = props => {
if (props.isLoading) return <Spinner />;
switch (props.type) {
case 'info':
return <Info color={iconColor} />;
case 'success':
return <Success color={iconColor} />;
case 'error':
return <Error color={iconColor} />;
case 'warning':
return <Warning color={iconColor} />;
default:
return undefined;
}
};
<ToastContainer icon={CustomIcon} />
The initial release for this feature was planned right after the release of the v9. The code was their but things happen in life and I wasn't able to focus on the project that much.
That being said, I'm glad to finally release it.
To enable it, add the stacked
prop to the ToastContainer
. I also suggest to disable the progress bar :).
<ToastContainer stacked />
The progress bar leaves a background trail by default.
The opacity of the trail can be customized by overriding the css variable --toastify-color-progress-bgo
.
// disable the trail
--toastify-color-progress-bgo: 0;
// increase the opacity
--toastify-color-progress-bgo: .8;
The minimun version of react required is now 18
. This version has been released for more than a year. Nextjs, react-query already did this move as well, so I believe it's a good time for the library as well.
That being said, I know that not all code base have the chance to be running on the latest version of react, so I'm considering to have a package for v17
if the demand is high enough.
The enableMultiContainer
props is not needed anymore and has been removed. As long as your container has an id
assigned then you are good to go.
// before
<ToastContainer id="myContainer" enableMultiContainer />
// now π
<ToastContainer id="myContainer" />
The toast.POSITION
and toast.TYPE
constants have been removed. Typescript came a long way since the initial release of the library. The ecosystem has matured to a point where such constants are not needed anymore.
The closeOnClick
prop is now false
by default instead of true
. When using the library I keep on turning this feature off which make me realize that it was not a good default in the first place. To turn it on do as follow
<ToastContainer closeOnClick />
The draggable
prop is set to touch
by default instead of true
. Which means that, by default, notifications are only draggable on touch devices (most likely mobiles and tablets). While swipping a notification on mobile feels natural, dragging on desktop is not. If you want your notification to be draggable regardless of the device type just set draggable to true
.
<ToastContainer draggable />
Published by fkhadra over 1 year ago
Toast is undefined || Uncaught TypeError: Cannot read properties of undefined (reading 'content')
#858 #952Published by fkhadra over 1 year ago
Mainly bug fixes as I'm working on the next major release.
Published by fkhadra almost 2 years ago
toast.promise
let you type data
for each state. This is useful when rendering something based on the response.For example:
interface Success {
username: string
}
interface Error {
err: string
}
toast.promise<Success,Error>(myPromise, {
success: {
render({ data }) {
return data.username;
}
},
error: {
render({ data }) {
return data.err;
}
}
})
toast.update
accepts a generic as well to specify the data typeinterface TData {
username: string
}
toast.update<TData>(id, {
data: payload,
render({ data }) {
return `hello ${data.username}`
}
})
toast.done
not dismissing toast #853Added deprecation notice for the API below. They will be removed in the next major release
API | Why | Alternative |
---|---|---|
onClick |
Not used that much, it's increasing the API surface for nothing | Can easily be implemented in userland. Just render a react component and attach the handler to it. toast(<div onClick={doSomething}>hello</div>)
|
onOpen |
Does not play well with useEffect behavior in react18 (runs twice in dev mode) see #741 |
A better approach is to use toast.onChange see https://fkhadra.github.io/react-toastify/listen-for-changes/
|
onClose |
Does not play well with useEffect behavior in react18 (runs twice in dev mode) see #741 |
A better approach is to use toast.onChange see https://fkhadra.github.io/react-toastify/listen-for-changes/
|
toast.POSITION |
Reduce bundle size :) | Thanks to typescript, we now have autocomplete |
toast.TYPE |
Reduce bundle size :) | Thanks to typescript, we now have autocomplete |
Published by fkhadra about 2 years ago
Published by fkhadra over 2 years ago
Published by fkhadra over 2 years ago
useNotificationCenter
typing #796Published by fkhadra over 2 years ago
Published by fkhadra over 2 years ago
This release does not work with CRA v4(2 years old) due to the lack of support for es modules. I strongly advise to upgrade to lastest CRA if possible.
Alternative solution:
Published by fkhadra over 2 years ago
toast.update
#770Published by fkhadra over 2 years ago
Thanks to @Rambatino, @grit96, @Vl3oss, @eolme for this release
Published by fkhadra over 2 years ago
Say hello to addons! What are addons? So, addons are like DLCs in video games but for react-toastify π. More seriously, you can think of it as utilities built around react-toastify. For example, custom theme, hooks, components etc...
The first addon that I would like to introduce is the useNotificationCenter
headless hook! As the name suggests, it let you build your notification center on top of react-toastify. See for yourself π
https://user-images.githubusercontent.com/5574267/166549174-48422a61-ff7b-4eeb-b53f-d903db3d1595.mov
Another example using MUI.
Check the documentation for more details.
This second addon will be released later. There are a bunch of details that I need to cover and I don't want to release something too buggy. Nevertheless, I'm really excited about it and I think it's worth showcasing anyway.
I call it StackedContainer
for now, it's an alternative to the ToastContainer
component.
There are 2 breaking changes. The API change for toast.onChange
and the removal of toast.configure
.
toast.onChange
The previous API was returning the numberOfToastDisplayed
and the containerId
. Honestly, this API seems to be incomplete.
For example, with the old API, if I wanted to do some logging this would be very difficult because I don't have enough data to log.
toast.onChange((numberOfToastDisplayed, containerId) => {
logger.info("nothing useful to log, ...")
})
The new API offers more possibilities. The callback will receive a ToastItem
. The item provides a bunch of useful properties status
, content
, id
, data
, etc...
interface ToastItem<Data = {}> {
id: Id;
content: React.ReactNode;
theme?: Theme;
type?: TypeOptions;
isLoading?: boolean;
containerId?: Id;
data: Data;
icon?: React.ReactNode | false;
status: "added" | "removed" | "updated"
}
const unsubscribe = toast.onChange((payload: ToastItem) => {
switch (payload.status) {
case "added":
// new toast added
break;
case "updated":
// toast updated
break;
case "removed":
// toast has been removed
break;
}
});
For example, if I want to log something every time there is a new error notification, with the new API it's trivial
toast.onChange(payload => {
if(payload.status === "added" && payload.type === toast.TYPE.ERROR) {
logger.error({
createdAt: Date.now(),
content: payload.content,
data: payload.data
})
}
})
toast.configure
removaltoast.configure
works fine for most cases but the current implementation has one main issue. It does not work with react context because it creates a new react tree.
That being said, having 2 APIs to do the same thing is a bad thing.
Published by fkhadra over 2 years ago
toast.configure
.toast.onChange
. API for this method will change in the next major releasePublished by fkhadra over 2 years ago
onClose
now work as expected!ToastPromiseParams
is now exportedThanks to @03c, @wilson-webdev, @essential-randomness for their contributions
Published by fkhadra almost 3 years ago
π Features
Published by fkhadra about 3 years ago
Published by fkhadra about 3 years ago
Published by fkhadra about 3 years ago
toast.promise
accept function that returns a promise.Published by fkhadra about 3 years ago
React-toastify has been around for 5 years(will turn five the 8 November π). Since the beginning, one of the goals was to provide a library that is highly customizable and also able to work out of the box. Every major release introduces breaking changes but this is for the best π.
Notifications of different types (toast.info
, toast.error
, toast.success
, toast.warning
) display an icon associated with the selected type. You can also notice that the progress bar color matches the type color.
Don't be afraid π±, if you don't like those icons you can use your own or remove them. This is what it looks like in practice.
toast("Default toast behavior is untouched, no icon to display");
toast.info("Lorem ipsum dolor"); // same as toast(message, {type: "info"});
toast.error("Lorem ipsum dolor")
toast.success("Lorem ipsum dolor")
toast.warn("Lorem ipsum dolor")
toast.error("Without icon", {
icon: false
});
toast.success("You can provide any string", {
icon: "π"
});
// custom icons have access to the theme and the toast type
toast.success("And of course a component of your choice", {
icon: MyIcon
});
toast.success("Even a function, given you return something that can be rendered", {
icon: ({theme, type}) => <img src="url"/>
});
//Disable icons
<ToastContainer icon={false} />
Prior to v8, toast.info
, toast.error
, etc... Would display respectively a blue notification, a red notification, etc... This is not the case anymore. There are 3 distinct themes: light
, dark
, colored
. The theme can be applied globally or per notification.
//Set the theme globally
<ToastContainer theme="dark" />
// define per toast
toast.info("Display a dark notification of type info");
toast.info("Display a light notification of type info", { theme: "light" });
toast.info("Display a blue notification of type info", { theme: "colored" });
This separation will benefit theming in the future.
The library exposes a toast.promise
function. Supply a promise and the notification will be updated if it resolves or fails. When the promise is pending a spinner is displayed. Again you hide it, I bet you already know how toπ.
Let's start with a simple example
const resolveAfter3Sec = new Promise(resolve => setTimeout(resolve, 3000));
toast.promise(
resolveAfter3Sec,
{
pending: 'Promise is pending',
success: 'Promise resolved π',
error: 'Promise rejected π€―'
}
)
Displaying a simple message is what you would want to do in 90% of cases. But what if the message you want to display depends on the promise response, what if you want to change some options for the error notification? Rest assured, under the hood, the library uses toast.update
. Thanks to this, you have full control over each notification.
const resolveWithSomeData = new Promise(resolve => setTimeout(() => resolve("world"), 3000));
toast.promise(
resolveAfter3Sec,
{
pending: 'Promise is pending',
success: {
render({data}){
return `Hello ${data}`
},
// other options
icon: "π’",
},
error: {
render({data}){
// When the promise reject, data will contains the error
return <MyErrorComponent message={data.message} />
}
}
}
)
If you want to take care of each step yourself you can use toast.loading
and update the notification yourself.
const id = toast.loading("Please wait...")
//do something else
toast.update(id, { render: "All is good", type: "success" });
One way to pass data to the notification was to use the context api or provide your own component. Starting v8 a data
option is now available to make it easier.
toast(({data}) => `Hello ${data}`, {
data: "world"
})
Most of the time, users are ok with the default style, they just want to change some colors to match their brand. I think one way to improve the DX for all of us is to embrace CSS variables. That's why the library has switched to css variables!
All you want is to change the color of the progress bar? No problem
:root{
// this is the default value below
--toastify-color-progress-light: linear-gradient(
to right,
#4cd964,
#5ac8fa,
#007aff,
#34aadc,
#5856d6,
#ff2d55
);
}
You can find the list of all exposed variables here
There are few breaking changes.
dark
has been removed// before v8
toast("hello", {
type: "dark"
})
// toast.TYPE.DARK is no longer available
// in v8
toast("hello", {
theme: "dark"
})
// or
toast.dark("hello")
colored
theme to get the same behavior as the previous versiontoast.info("hello", {
theme: "colored"
})
// or apply the theme globally
<ToastContainer theme="colored" />
info
, error
, warning
, success
// to opt-out gloablly from this behavior
<ToastContainer icon={false} />
// or per toast
toast.info("hello", {
icon: false
});
.Toastify__toast--dark {
}
.Toastify__toast--default {
}
.Toastify__toast--info {
}
.Toastify__toast--success {
}
.Toastify__toast--warning {
}
.Toastify__toast--error {
}
That's it for this release. Thank you for using react-toastify and happy coding!
Published by fkhadra over 3 years ago