How to Configure the Notification Provider with Sonner in refine-shadcn
To configure Sonner notifications in refine-shadcn, import the notificationProvider from @ferdiunal/refine-shadcn, pass it to the <Refine> component's notificationProvider prop, and ensure the <Toaster /> component is rendered in your component tree.
The ferdiunal/refine-shadcn repository ships with a complete, ready-made notification system that integrates the Sonner toast library with Refine's core notification API. This implementation eliminates boilerplate by mapping Refine's NotificationProvider contract directly to Sonner's toast methods, providing immediate support for success, error, and loading states.
Setting Up the Notification Provider
The notification provider is implemented in packages/theme/src/providers/notificationProvider.ts. It exports a concrete implementation of Refine's NotificationProvider interface that translates Refine notification calls into Sonner toast invocations.
// packages/theme/src/providers/notificationProvider.ts
import { NotificationProvider } from "@refinedev/core";
import { toast } from "sonner";
export const notificationProvider: NotificationProvider = {
open: ({ key, message, description, type }) => {
if (type === "success") {
toast.success(message, { description, id: key });
}
if (type === "error") {
toast.error(message, { description, id: key });
}
if (type === "progress") {
toast.loading(message, { description, id: key });
}
},
close: (key) => toast.dismiss(key),
};
export const useNotificationProvider = (): NotificationProvider => notificationProvider;
The provider handles three notification types:
- success: Maps to
toast.success() - error: Maps to
toast.error() - progress: Maps to
toast.loading()for asynchronous operations
Each call passes a unique key as the toast ID, enabling programmatic closing via the close method using toast.dismiss().
Rendering the Sonner Toaster Component
To display notifications, you must render the Sonner <Toaster> component somewhere in your application hierarchy. The refine-shadcn theme provides a wrapped version in packages/theme/src/ui/sonner.tsx that integrates with next-themes for automatic dark mode support.
// packages/theme/src/ui/sonner.tsx
"use client";
import { useTheme } from "next-themes";
import { Toaster as Sonner, ToasterProps } from "sonner";
const Toaster = ({ ...props }: ToasterProps) => {
const { theme = "system" } = useTheme();
return (
<Sonner
theme={theme as ToasterProps["theme"]}
className="toaster group"
style={{
"--normal-bg": "var(--popover)",
"--normal-text": "var(--popover-foreground)",
"--normal-border": "var(--border)",
} as React.CSSProperties}
{...props}
/>
);
};
export { Toaster };
If you use the BaseLayout component from refine-shadcn, the <Toaster /> is already included at the layout root in packages/theme/src/layouts/base.tsx:
// packages/theme/src/layouts/base.tsx
import { Toaster, TooltipProvider } from "@/ui";
<TooltipProvider>
{children}
<Toaster />
</TooltipProvider>
Note: If you implement a custom layout instead of BaseLayout, you must manually import and render <Toaster /> from @ferdiunal/refine-shadcn (or from @/ui/sonner in internal packages) to ensure notifications appear.
Wiring the Provider into Your Refine Application
Connect the notification provider to your Refine application by passing it to the <Refine> component's notificationProvider prop. The Vite-React template in the repository demonstrates this pattern in templates/vite-react/src/App.tsx:
// templates/vite-react/src/App.tsx
import { DefaultLayout, notificationProvider } from "@ferdiunal/refine-shadcn";
import { Refine } from "@refinedev/core";
function App() {
return (
<Refine
routerProvider={routerProvider}
dataProvider={dataProvider(API_URL)}
resources={resources}
i18nProvider={i18nProvider}
notificationProvider={notificationProvider}
>
<DefaultLayout>
{/* Your routes */}
</DefaultLayout>
</Refine>
);
}
Once wired, any notification triggered through Refine's API will automatically render as a Sonner toast with the styling and positioning configured in the <Toaster /> component.
Triggering Notifications in Your Components
With the provider configured, use the useNotification hook from @refinedev/core to display notifications in your components. The hook provides an open method that accepts the same parameters mapped in the provider implementation.
import { useNotification } from "@refinedev/core";
export const MyComponent = () => {
const { open, close } = useNotification();
const handleSave = async () => {
const key = open?.({
type: "progress",
message: "Saving changes…"
});
try {
await saveData();
open?.({
key,
type: "success",
message: "Changes saved successfully!"
});
} catch (error) {
open?.({
key,
type: "error",
message: "Failed to save",
description: String(error)
});
}
};
return <button onClick={handleSave}>Save</button>;
};
The key parameter ensures that progress notifications are replaced by success or error states rather than stacking multiple toasts. If you need to manually dismiss a notification, call close(key) or use the toast.dismiss() method directly from Sonner.
Summary
- Import the
notificationProviderfrom@ferdiunal/refine-shadcn(located atpackages/theme/src/providers/notificationProvider.ts) - Pass the provider to the
<Refine>component via thenotificationProviderprop to enable Sonner integration - Render the
<Toaster />component in your layout (automatically included inBaseLayout, manual placement required for custom layouts) - Use the
useNotificationhook from@refinedev/coreto trigger Sonner toasts with success, error, and progress types
Frequently Asked Questions
Where is the notification provider implemented in refine-shadcn?
The provider is implemented in packages/theme/src/providers/notificationProvider.ts according to the ferdiunal/refine-shadcn source code. It exports a notificationProvider object that implements Refine's NotificationProvider interface by mapping notification types to Sonner's toast.success(), toast.error(), and toast.loading() methods.
Do I need to manually render the Sonner Toaster component?
If you use the BaseLayout or DefaultLayout from refine-shadcn, the <Toaster /> is already rendered internally at packages/theme/src/layouts/base.tsx. If you use a custom layout, you must manually import and render the Toaster component from @ferdiunal/refine-shadcn (or @/ui/sonner in monorepo setups) to ensure notifications are visible.
How does the Sonner integration handle dark mode themes?
The Toaster wrapper in packages/theme/src/ui/sonner.tsx uses the useTheme hook from next-themes to detect the current theme mode. It passes this theme value to Sonner's theme prop and injects CSS variables (--normal-bg, --normal-text, --normal-border) that reference the shadcn/ui design tokens, ensuring toasts match your application's color scheme automatically.
Can I customize the notification duration or styling?
Yes. While the default provider uses Sonner's default durations, you can customize the <Toaster /> component by passing props like duration, position, or toastOptions when rendering it in your layout. For per-notification customization, you would need to extend the notificationProvider implementation to pass additional options through the open method's payload.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →