shadcn.io is not affiliated with official shadcn/ui
Notification Delivery Log Table Block for React Aria
A delivery matrix with one row per notification and one column per channel, a mark and a word in each cell, per-channel totals, a Resend button on failed cells and a type filter.
Installation
Overview
Show what happened to every message, with this notification delivery log for React with React Aria. A strip of four hairline cells counts deliveries on Email, Push, SMS and Chat across the notifications shown ("5 of 6 delivered, 1 failed"), and under it a native table has one row per notification and one column per channel. Each cell is a mark and a word: Delivered, Failed or Skipped, with a ring, a cross or a dash so it reads without colour. A row says how many of the channels it tried got through ("3 of 4 delivered"). A failed cell has a Resend Button: it waits about a second, the cell then reads "Delivered on retry", focus moves to that result and the counts update. A Select picks the notification type inside a fixed-height frame, and one type has nothing sent so the Empty state can be reached. On a phone the channel columns shrink to their marks and Resend becomes an icon. For notification services, message queues, webhooks and any table of attempts.
FAQ
How do I fill it with my own notifications and channels?
Edit NOTICES and CHANNELS. A notification is its subject, its type, the time it went out and one result per channel: delivered, failed or skipped. The totals, the "3 of 4 delivered" line and the strip are all worked out from those results by small helpers, so they cannot drift from the cells. Add a channel by adding it to CHANNELS and to the record type, and a column and a strip cell appear. The types in the Select come from KINDS.
What does Resend do, and what is local?
In the demo it changes local state only: the button shows its busy state for 0.9 seconds (not at all when reduced motion is on), then the cell becomes "Delivered on retry" and the totals include it. No message is sent. In your app, call your send endpoint in resend, keep the pending list while the request runs, and move the cell to delivered or back to failed with the response; the rest of the table follows from the results.
Why does a row say 3 of 4 delivered instead of 4 of 4?
A skipped channel was never tried: the person had it switched off, or the notification type does not use it. So the row counts only the channels that were attempted, and a skipped cell says "Skipped" in muted text rather than looking like a failure. Only a failed cell offers Resend. The strip above the table counts the same way, so "1 of 2" on SMS means two attempts, one delivered.
Does filtering by type change the page height?
No. The table sits in a fixed-height frame with a sticky head, rows keep one height whatever their cells say, and the strip and the status line keep their space. Choosing a type changes the rows inside the frame and the counts above it. Digests has nothing sent in the sample, which shows the empty state with a button that returns to all types and puts focus on the frame.
Is it accessible?
Yes. It is a native table with a caption, column heads, and a row header for each notification. Every mark has its word, hidden visually only on a phone and still read aloud, and the Resend buttons are named by channel and subject ("Resend Push for New sign-in from Lisbon"). One polite status line, present from the start, reports filter changes and each retry result, and focus lands on the cell's new result after a retry so nobody is left on a button that has gone.
How does it look on a phone?
Each channel column narrows to its mark, with the full word still available to a screen reader, and a failed cell shows a small round-arrow Resend button instead of the cross. The subject keeps its second line, which on a phone is the "3 of 4 delivered" count alone; the type and time appear from the medium breakpoint. The strip becomes two cells by two.