Notifications
The Notifications section of an AI product's settings. A channel by event grid with row, column and all toggles, quiet hours with a live next-quiet line, an email digest, real test sends for desktop, email, push and Slack, browser permission handling and a Slack connect flow. Drops into any Settings section.
Every AI product ends up with the same notifications page: a list of things that can happen, a list of places they can reach you, and a way to make it stop at night. NotificationSettings is that page as one section. It drops into a SettingsSection and uses the same draft, save bar and unsaved-changes guard as the rest of Settings.
The core is a grid of events by channels. The top row turns a channel on or off for every event, the first column turns an event on or off everywhere, and the corner checkbox does all of it. Each shows a dash when only some are on. With the keyboard the grid is one tab stop: arrow keys move between checkboxes, Home and End jump along a row, and Space toggles. Screen readers hear each one as "Email for Agent finished". On a phone the grid becomes one card per event with large channel chips.
Each channel has a Send test that really sends. Desktop and push show a notification banner over the page, and desktop also fires a real system notification once the browser allows it. Email opens a preview of the message in an inbox, the digest version when emails are bundled. Slack shows the message as your team sees it in the channel.
Desktop reads the browser's permission. When it hasn't been asked, Allow asks; when it's blocked, it says where to change it; when the browser can't show notifications, it says so. Slack starts disconnected: its column is off, and Connect opens a dialog to pick a workspace and channel. If connecting fails, the reason shows in the dialog and nothing is lost.
Quiet hours hold desktop and push alerts between two times on the days you pick, in the time zone you pick, with a line that says when the next quiet period starts or that it's quiet now. Urgent events like approvals can still come through.
Add the Pro registry to components.json
components.json Add your token
Create a token on your account page and put it in
.env.localasHEXTAUI_PRO_TOKEN.Add the block
pnpm dlx shadcn@latest add @hextaui-pro/notifications
Wire it to your API
Pass the saved preferences and an async onSave. Edits stay in a draft until someone saves from the save bar or with ⌘S. Return field errors to show them under the field, or throw to show the message in the save bar. onSendTest sends a real test before the preview appears.
Your own events and channels
Pass events to add or drop rows and channels to pick columns. Each event can carry a sample for test previews and urgent to let it through quiet hours. onSlackConnect gets the chosen workspace and channel; throw to show why it failed, and the dialog stays open.
Anatomy
The parts you compose, from the outside in.
| Part | Description |
|---|---|
NotificationSettings | The whole section: the event grid, channels with tests, quiet hours and the email digest. |
notificationEvents | The default events: Agent finished, Needs approval, Run failed, Mentions, Weekly summary and Billing. |
notificationChannels | The default channels: desktop, email, push and slack. |
NotificationSettings
Must be inside a SettingsSection.
| Prop | Type | Default |
|---|---|---|
preferencesWhat's saved now: { matrix, quietHours, quietFrom, quietTo, quietDays, timeZone, quietUrgent, digest }. matrix maps each event id to the channels that are on. When it changes and there are no edits, the draft follows it. | NotificationPreferences | – |
onSaveSave the draft. Return { quietDays: "…" } and the like to show field errors, or throw to show the message in the save bar. | (preferences) => void | errors | Promise<void | errors> | – |
eventsRows of the grid: { id, label, description?, urgent?, sample? }. sample is { title, body, subject, action } for test previews. urgent events can come through quiet hours. | NotificationEvent[] | notificationEvents |
channelsColumns of the grid and rows under Channels, in order. Any of "desktop", "email", "push" and "slack". | NotificationChannel[] | notificationChannels |
emailWhere email goes. Shown under Email and in the test email. | string | "[email protected]" |
deviceThe phone that gets push notifications. | string | "your phone" |
slackThe connected Slack channel. While it's null, the Slack column is off. | { workspace, channel } | null | null |
slackWorkspacesWorkspaces to pick from when connecting: { id, name, channels: { id, name, private? }[] }. | SlackWorkspace[] | [] |
onSlackConnectConnect the chosen channel. Throw to show the message in the dialog. | ({ workspace, channel }) => void | Promise<void> | – |
onSlackDisconnectDisconnect Slack after the person confirms. | () => void | Promise<void> | – |
onSendTestSend a real test. The preview shows once it resolves, and the button offers a retry if it throws. event is the first one that's on for that channel. | ({ channel, event }) => void | Promise<void> | – |
| Key | Action |
|---|---|
| Tab | Moves into the grid once, to the last checkbox you used, then on to the channels. |
| ↑↓←→ | In the grid, move between checkboxes. Left and right follow reading direction. |
| HomeEnd | In the grid, jump to the start or end of the row. With Ctrl or ⌘, to the first or last checkbox. |
| Space | Turns the focused checkbox on or off. On a row, column or corner checkbox, turns all of them on, or off when all are on. |
| Esc | Dismisses a test notification banner while it has focus. |
- The grid is an ARIA grid with column and row headers, and every checkbox is named by its channel and event, like "Mobile push for Run failed".
- Row, column and corner checkboxes are mixed when only some are on, so screen readers hear "partially checked".
- Test sends, permission results and Slack changes are announced politely. The next quiet period is a status line that updates as you edit.
- Test banners pause while hovered or focused, have a dismiss button, and fade instead of sliding with reduced motion.
- On phones every channel chip is a 44px target.
Code
5 files, added to components/blocks/notifications.