Skip to content

shadcn.io is an independent project built on shadcn/ui

Free

Right Drawer Notifications for React

A bell with a 5 badge opens a Notifications drawer that says 5 unread, plus Mark all as read.

A bell with a badge of 5 opens a panel titled Notifications. It says you have 5 unread notifications, lists each item with a dot, and ends with Mark all as read.

Use it for an activity list on a phone. A short list can hang off the bell in a Popover. In React, Base UI does not mark the items read.

Preview

Preview width

Installation

Sign in to get your install command
Free with an account. It carries your token, so the CLI installs everything in one go.

Anatomy

Drawer anatomy: grabber, title, description, content, close

Usage

<Drawer swipeDirection="right">
  <DrawerTrigger
    render={
      <Button
        aria-label="Notifications, 5 unread"
        className="relative"
        size="icon-md"
        variant="outline"
      />
    }
  >
    <BellIcon aria-hidden="true" />
    <Badge
      aria-hidden="true"
      className="absolute -end-1 -top-1 h-5 min-w-5 px-1 tabular-nums"
      pill
    >
      5
    </Badge>
  </DrawerTrigger>
  <DrawerContent>
    <DrawerHeader>
      <DrawerTitle>Notifications</DrawerTitle>
      <DrawerDescription>You have 5 unread notifications</DrawerDescription>
    </DrawerHeader>
    <ul className="min-h-0 flex-1 divide-y overflow-y-auto">
      {NOTIFICATIONS.map((notification) => (
        <li key={notification.id}>
          <button
            className="flex w-full items-start gap-3 p-4 text-start outline-none hover:bg-muted/50 focus-visible:bg-muted/50"
            type="button"
{/* The rest of the file is under Installation. */}

FAQ

How is the bell button labeled?

It's icon-only, so aria-label gives the name and the count: Notifications, 5 unread. The badge is aria-hidden, so the count isn't read twice.

How are unread notifications marked?

A primary dot, a medium-weight title and a visually hidden Unread before the title. Read ones get a muted dot and regular weight, so the state isn't color alone.

Why is each notification a button?

So people can open it with a click or the keyboard. The rows have no handler yet: open the related page from each one.

Does Mark all as read clear the dots?

It's a DrawerClose, so it closes the drawer; it doesn't change the data. Keep the notifications in state and set them all read when it's pressed.

How does the drawer open from the right?

swipeDirection="right" on Drawer puts it on the right edge. The prop names the swipe that closes the drawer, so right docks it on the right; leave it out for a bottom drawer.

What does the drawer announce?

DrawerTitle names the dialog for assistive tech, so every drawer renders one; give it sr-only when the design has no visible heading. It reads Notifications, with You have 5 unread notifications as the description.

API Reference

Drawer

Sets swipeDirection="right". The rest is on the Drawer page.

Button

Sets aria-label="Notifications, 5 unread", size="icon-md" and variant="outline". The rest is on the Button page.

Badge

Sets aria-hidden and pill. The rest is on the Badge page.