Skip to content
shadcn.io

shadcn.io is not affiliated with official shadcn/ui

Preference Matrix Notification Block for React

A notification preference matrix: five kinds of notification down the page and four channels across, a checkbox in every cell, an all-types row that turns a channel on or off, marks on changed cells and an unsaved count.

Framework
UI library

Preview

Preview width

Installation

Part of Pro
Pro unlocks the code and one-line install of every Pro button and text effect, plus the MCP for your AI editor.
Have Pro already? Sign in to open it.Opens here as soon as you're Pro.

Overview

Let people say what reaches them and where, with this notification preference matrix for React with Base UI. A compact header sits over one hairline frame with a plus mark on each corner. The first band counts what is on ("11 of 17 on"). Under it a native table crosses five kinds of notification (mentions, replies, alerts, the weekly digest and product news) with four channels (email, push, in-app and SMS): every cell is a Checkbox named for both, such as "Mentions by email", and a dash marks a channel that can't carry a kind. The first row, All types, holds one box per channel that turns the whole column on or off and shows a bar while only part of it is on; each column head counts what it has on. A cell that differs from what is saved wears a small dot. The last band counts the unsaved changes in a status line beside a Shine Button to save them and an outline Button to reset them. On a phone each kind becomes a card with its four channels as labelled boxes. It's built from Checkbox, Shine Button, Button and Guides, with a CSS entrance only. For an app's notification page, a service's preference centre or an account settings tab.

FAQ

How do I fill it with my own kinds and channels?

Edit the constants at the top of the file. CHANNELS lists each channel with its label and the word a box's name uses ("email", "SMS"). KINDS lists each kind of notification with its name, a one-line hint and the channels it can go to (offered). SAVED is the set of cells the account has stored when the page opens, written as kind:channel keys: put the person's real settings there. Every count, the column boxes and the unsaved total are worked out from those, so nothing is typed twice.

What happens when I press Save, and what stays local?

Nothing leaves the page. The block keeps two copies of the settings, the saved one and the one being edited, and counts the cells that differ, so the status line can say "3 unsaved changes". Save makes the edited copy the saved one and says "Saved 3 changes in this demo"; Reset puts the saved copy back. To make it real, send the edited copy (or only the cells that changed) to your API in the save handler, update the saved copy from the response and show a failure in the same status line. Both buttons stay in place and focusable while there is nothing to do, and pressing one then says so.

How does the All types row work?

Each box looks at the cells of its channel that exist: ticked when all are on, empty when none are, and a bar in between. Pressing it turns the channel on for every kind; pressing it again when it is all on turns it off. A channel a kind doesn't use (the digest by SMS) shows a dash and is left out of the counts, so the weekly digest never keeps the SMS column from reading "all on". The counts under the channel names on a desktop follow the same rule.

How does it show what changed?

Two ways, and neither relies on colour. The status line counts the cells that differ from the saved copy and says it in words, and each of those cells wears a small dot at the corner of its box. Putting a cell back to how it was removes its dot and lowers the count, so the count never drifts from the table.

How does it look on a phone?

Below the medium breakpoint the table becomes one card per kind: its name and hint over four boxes labelled Email, Push, In-app and SMS, each box's whole cell pressable. All types is the first card, with the same four boxes. The save bar stays pinned to the bottom of the screen while you scroll the panel, so Save is always in reach; from the medium breakpoint it is simply the last band.

Is the matrix accessible?

On a desktop it is a native table with a caption, column headers for the channels and a row header for each kind, so a screen reader announces both as you move between cells, and every box is also named for both ("Replies by push"). The all-types boxes are named by their channel ("Email for all types"). The status line is a polite live region that is present from the start. Every box works from the keyboard with Space. On Base UI the mixed state is the checkbox's indeterminate prop.

  • Pro

    A notification settings panel: a channel strip with all-or-nothing checkboxes, a matrix of switches by activity and channel with a locked security email, quiet hours, and a save bar that counts unsaved changes.

  • Pro

    A destinations window for alerts: a rail of Slack, email, PagerDuty, webhook and SMS destinations with a status word, beside the chosen one's routing by severity, a pause switch and a simulated test.

  • Pro

    A focus app's do-not-disturb panel: a status card that says in a sentence what the settings amount to, a 24-hour strip of the muted hours, one hour, until tomorrow or a nightly schedule, and the calls that still get through.

  • Pro

    A meeting app's sound settings: a master switch, a volume slider with a level meter, a switch and a tone for each kind of notification, and a Play button that makes a real short tone in your browser when pressed.

  • Pro

    A team chat inbox with a settings dialog shown open: a real table of notification kinds by channel with a switch in every cell, mixed-state column masters, reset and save, and a chip on the inbox that reports the saved count.

  • Pro

    What a notification email looks like before it is sent: pick Mention, Weekly digest or Security alert, switch between desktop and phone width, and Send me a test, which only says it was queued.