One of the most important parts of an application is the notifications and the visual feedbacks. Refine has this built-in notification integration that works automatically when it's needed in cases such as when a request fails or when a form is submitted.
While this integration is not coupled with the UI integrations, it will be a wise choice to use the one that is provided by the UI libraries for a consistent design language. This is why Refine's UI integrations also provides a notificationProvider
to be used with the notification integration of refine.
Notification Providers
Refine let's you set a notification API by providing the notificationProvider
property to the <Refine />
component. notificationProvider
is an object with close and open methods. Refine uses these methods to show and hide notifications. These methods can be called from anywhere in the application with useNotification
hook.
An notificationProvider
must include open
and close
methods with the following types:
interface NotificationProvider {
open: (params: OpenNotificationParams) => void;
close: (key: string) => void;
}
interface OpenNotificationParams {
key?: string;
message: string;
type: "success" | "error" | "progress";
description?: string;
cancelMutation?: () => void;
undoableTimeout?: number;
}
Once you provide the notification provider, Refine seamlessly integrate with data hooks to displays user-friendly notifications for various data-related actions, ensuring a clear and informative user experience. This includes:
- Form Submission: Whether a form is successfully submitted or encounters errors, Refine will display the appropriate notification to keep the user informed.
- Resource Management: Creation, deletion, update, import, and export of resources are all accompanied by success or error notifications, providing immediate feedback to the user.
- Data Fetching: Refine also displays notifications for failed data fetching operations, including those using useList, useInfiniteList, useMany, useOne.
- Auth Actions: Login, logout, register, update password, and forgot password actions are all integrated with Refine's notification provider to display error notifications.
Built-in Notification Providers
Using of the prebuilt notification providers are optional and can be customized, extended or even swapped with a custom implementation if needed.
As an example, we'll demonstrate how to open and close notifications using the useNotification
hook. However, in most cases, you won't need to do this, as Refine typically manages notifications for you automatically.
- Ant Design
- Material UI
- Mantine
- Chakra UI
import React from "react";
import { useNotification } from "@refinedev/core";
import { Button, Col, Row } from "antd";
export const HomePage: React.FC = () => {
const { open, close } = useNotification();
return (
<Row
gutter={[16, 16]}
style={{
justifyContent: "center",
alignItems: "center",
height: "100vh",
}}
>
<Col>
<Button
type="primary"
onClick={() => {
open?.({
type: "success",
message: "Notification Title",
description:
"This is the content of the notification.",
key: "notification-key",
});
}}
>
Open Notification
</Button>
</Col>
<Col>
<Button
type="default"
onClick={() => {
close?.("notification-key");
}}
>
Close Notification
</Button>
</Col>
</Row>
);
};
Dependencies: @refinedev/antd@latest,@refinedev/core@latest,@refinedev/simple-rest@latest,antd@^5.0.5
Code Files
File: /App.tsx
Content: import React from "react";
import { Refine } from "@refinedev/core";
import { useNotificationProvider, RefineThemes } from "@refinedev/antd";
import { ConfigProvider, App as AntdApp } from "antd";
import dataProvider from "@refinedev/simple-rest";
import "@refinedev/antd/dist/reset.css";
import { HomePage } from "./home-page";
const API_URL = "https://api.fake-rest.refine.dev";
const App: React.FC = () => {
return (
<ConfigProvider theme={RefineThemes.Blue}>
<AntdApp>
<Refine
dataProvider={dataProvider(API_URL)}
notificationProvider={useNotificationProvider}
>
<HomePage />
</Refine>
</AntdApp>
</ConfigProvider>
);
};
export default App;
File: /home-page.tsx
Content: import React from "react";
import { useNotification } from "@refinedev/core";
import { Button, Col, Row } from "antd";
export const HomePage: React.FC = () => {
const { open, close } = useNotification();
return (
<Row
gutter={[16, 16]}
style={{
justifyContent: "center",
alignItems: "center",
height: "100vh",
}}
>
<Col>
<Button
type="primary"
onClick={() => {
open?.({
type: "success",
message: "Notification Title",
description:
"This is the content of the notification.",
key: "notification-key",
});
}}
>
Open Notification
</Button>
</Col>
<Col>
<Button
type="default"
onClick={() => {
close?.("notification-key");
}}
>
Close Notification
</Button>
</Col>
</Row>
);
};
import React from "react";
import Grid from "@mui/material/Grid2";
import Button from "@mui/material/Button";
import { useNotification } from "@refinedev/core";
export const HomePage = () => {
const { open, close } = useNotification();
return (
<Grid
container
spacing={2}
sx={{
justifyContent: "center",
alignItems: "center",
height: "100vh",
}}
>
<Grid>
<Button
variant="contained"
onClick={() => {
open?.({
type: "success",
message: "Notification Title",
description:
"This is the content of the notification.",
key: "notification-key",
});
}}
>
Open Notification
</Button>
</Grid>
<Grid>
<Button
variant="outlined"
onClick={() => {
close?.("notification-key");
}}
>
Close Notification
</Button>
</Grid>
</Grid>
);
};
Dependencies: @refinedev/core@latest,@refinedev/simple-rest@latest,@emotion/react@^11.8.2,@emotion/styled@^11.8.1,@mui/lab@^6.0.0-beta.14,@mui/material@^6.1.6,@mui/system@latest,@refinedev/mui@latest
Code Files
File: /App.tsx
Content: import React from "react";
import { Refine } from "@refinedev/core";
import {
RefineThemes,
useNotificationProvider,
RefineSnackbarProvider,
} from "@refinedev/mui";
import CssBaseline from "@mui/material/CssBaseline";
import GlobalStyles from "@mui/material/GlobalStyles";
import { ThemeProvider } from "@mui/material/styles";
import dataProvider from "@refinedev/simple-rest";
import { HomePage } from "./home-page";
const App: React.FC = () => {
return (
<ThemeProvider theme={RefineThemes.Blue}>
<CssBaseline />
<GlobalStyles styles={{ html: { WebkitFontSmoothing: "auto" } }} />
<RefineSnackbarProvider preventDuplicate={true}>
<Refine
dataProvider={dataProvider(
"https://api.fake-rest.refine.dev",
)}
notificationProvider={useNotificationProvider}
>
<HomePage />
</Refine>
</RefineSnackbarProvider>
</ThemeProvider>
);
};
export default App;
File: /home-page.tsx
Content: import React from "react";
import Grid from "@mui/material/Grid2";
import Button from "@mui/material/Button";
import { useNotification } from "@refinedev/core";
export const HomePage = () => {
const { open, close } = useNotification();
return (
<Grid
container
spacing={2}
sx={{
justifyContent: "center",
alignItems: "center",
height: "100vh",
}}
>
<Grid>
<Button
variant="contained"
onClick={() => {
open?.({
type: "success",
message: "Notification Title",
description:
"This is the content of the notification.",
key: "notification-key",
});
}}
>
Open Notification
</Button>
</Grid>
<Grid>
<Button
variant="outlined"
onClick={() => {
close?.("notification-key");
}}
>
Close Notification
</Button>
</Grid>
</Grid>
);
};
import React from "react";
import { Flex, Button } from "@mantine/core";
import { useNotification } from "@refinedev/core";
export const HomePage = () => {
const { open, close } = useNotification();
return (
<Flex mih={"100vh"} gap="md" justify="center" align="center">
<Button
onClick={() => {
open?.({
type: "success",
message: "Notification Title",
description: "This is the content of the notification.",
key: "notification-key",
});
}}
>
Open Notification
</Button>
<Button
variant="outline"
onClick={() => {
close?.("notification-key");
}}
>
Close Notification
</Button>
</Flex>
);
};
Dependencies: @refinedev/core@latest,@refinedev/simple-rest@latest,@mantine/notifications@^5.10.4,@emotion/react@^11.8.2,@mantine/core@^5.10.4,@mantine/hooks@^5.10.4,@refinedev/mantine@^2.28.21
Code Files
File: /App.tsx
Content: import React from "react";
import { Refine } from "@refinedev/core";
import { useNotificationProvider, RefineThemes } from "@refinedev/mantine";
import { NotificationsProvider } from "@mantine/notifications";
import { MantineProvider, Global } from "@mantine/core";
import dataProvider from "@refinedev/simple-rest";
import { HomePage } from "./home-page";
const App: React.FC = () => {
return (
<MantineProvider
theme={RefineThemes.Blue}
withNormalizeCSS
withGlobalStyles
>
<Global styles={{ body: { WebkitFontSmoothing: "auto" } }} />
<NotificationsProvider position="top-right">
<Refine
dataProvider={dataProvider(
"https://api.fake-rest.refine.dev",
)}
notificationProvider={useNotificationProvider}
>
<HomePage />
</Refine>
</NotificationsProvider>
</MantineProvider>
);
};
export default App;
File: /home-page.tsx
Content: import React from "react";
import { Flex, Button } from "@mantine/core";
import { useNotification } from "@refinedev/core";
export const HomePage = () => {
const { open, close } = useNotification();
return (
<Flex mih={"100vh"} gap="md" justify="center" align="center">
<Button
onClick={() => {
open?.({
type: "success",
message: "Notification Title",
description: "This is the content of the notification.",
key: "notification-key",
});
}}
>
Open Notification
</Button>
<Button
variant="outline"
onClick={() => {
close?.("notification-key");
}}
>
Close Notification
</Button>
</Flex>
);
};
import React from "react";
import { Flex, Button } from "@chakra-ui/react";
import { useNotification } from "@refinedev/core";
export const HomePage = () => {
const { open, close } = useNotification();
return (
<Flex align="center" justify="center" height="100vh" gap={4}>
<Button
onClick={() => {
open?.({
type: "success",
message: "Notification Title",
description: "This is the content of the notification.",
key: "notification-key",
});
}}
>
Open Notification
</Button>
<Button
variant="outline"
onClick={() => {
close?.("notification-key");
}}
>
Close Notification
</Button>
</Flex>
);
};
Dependencies: @refinedev/core@latest,@refinedev/simple-rest@latest,@chakra-ui/react@^2.5.1,@refinedev/chakra-ui@^2.26.17
Code Files
File: /App.tsx
Content: import React from "react";
import { Refine } from "@refinedev/core";
import { RefineThemes, useNotificationProvider } from "@refinedev/chakra-ui";
import { ChakraProvider } from "@chakra-ui/react";
import dataProvider from "@refinedev/simple-rest";
import { HomePage } from "./home-page";
const App: React.FC = () => {
return (
<ChakraProvider theme={RefineThemes.Blue}>
<Refine
notificationProvider={useNotificationProvider()}
dataProvider={dataProvider("https://api.fake-rest.refine.dev")}
>
<HomePage />
</Refine>
</ChakraProvider>
);
};
export default App;
File: /home-page.tsx
Content: import React from "react";
import { Flex, Button } from "@chakra-ui/react";
import { useNotification } from "@refinedev/core";
export const HomePage = () => {
const { open, close } = useNotification();
return (
<Flex align="center" justify="center" height="100vh" gap={4}>
<Button
onClick={() => {
open?.({
type: "success",
message: "Notification Title",
description: "This is the content of the notification.",
key: "notification-key",
});
}}
>
Open Notification
</Button>
<Button
variant="outline"
onClick={() => {
close?.("notification-key");
}}
>
Close Notification
</Button>
</Flex>
);
};
Undoable
Refine also supports undoable notification.
You can trigger an undoable notification by setting the type
to progress
. After timeout, the notification will be closed automatically. If the user clicks the undo button, the cancelMutation
callback will be called.
const { open } = useNotification();
open?.({
type: "progress",
message: "Progress",
undoableTimeout: 5,
cancelMutation: () => {
},
});
Mutation hooks such as useUpdate
, useDelete
and useForm
supports undoable notifications. It can be used for canceling the mutation.
import { useForm } from "@refinedev/core";
useForm({ mutationMode: "undoable" });
Customizing Notifications
With props
All data hooks have a successNotification
and errorNotification
prop that can be used to customize the notification that will be shown when the hook is called.
We will look useUpdate
and useForm
hooks as an example but all data hooks have the same props and they work the same way.
import { useUpdate } from "@refinedev/core";
const { mutate } = useUpdate();
mutate({
successNotification: (data, values, resource) => {
return {
message: `${data.title} Successfully fetched.`,
description: "Success with no errors",
type: "success",
};
},
errorNotification: (data, values, resource) => {
return {
message: `Something went wrong when getting ${data.id}`,
description: "Error",
type: "error",
};
},
});
import { useForm } from "@refinedev/core";
useForm({
successNotification: (data, values, resource) => {
return {
message: `Successfully created ${data.title}`,
description: "good job!",
type: "success",
};
},
errorNotification: (error, values, resource) => {
return {
message: `Failed to create ${values.title}`,
description: error.message,
type: "error",
};
},
});
With i18n
Refine's notification integration is also integrated with the i18n Provider
. This means that you can use the i18n
integration to customize the notifications.
Refine uses following keys for the notifications and popultes {{resource}}
and {{statusCode}}
. You can override these keys in your i18n
provider to customize the notifications.
{
"notifications": {
"success": "Successful",
"error": "Error (status code: {{statusCode}})",
"undoable": "You have {{seconds}} seconds to undo",
"createSuccess": "Successfully created {{resource}}",
"createError": "There was an error creating {{resource}} (status code: {{statusCode}})",
"deleteSuccess": "Successfully deleted {{resource}}",
"deleteError": "Error when deleting {{resource}} (status code: {{statusCode}})",
"editSuccess": "Successfully edited {{resource}}",
"editError": "Error when editing {{resource}} (status code: {{statusCode}})",
"importProgress": "Importing: {{processed}}/{{total}}"
}
}