# Notification Preferences Toggles

> Notification preferences panel with a row per activity type and separate email and push toggle switches for each.

A shadcn/ui block from the [@7ovr](https://7ovr.com) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/7ovr/notifications-4).

- Page, with a live preview: https://shadcn.exchange/r/7ovr/notifications-4
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/7ovr/notifications-4-light-0a80bab621d27475.png
- Registry item: https://7ovr.com/r/notifications-4.json
- Categories: notifications
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://7ovr.com/r/notifications-4.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `@base-ui/react`
- Registry items: `card`, `separator`, `switch`

## Files

### `registry/blocks/notifications/4/notifications-block.tsx`

```tsx
"use client"

import { useState } from "react"

import {
  Card,
  CardContent,
  CardDescription,
  CardHeader,
  CardTitle,
} from "@/components/ui/card"
import { Separator } from "@/components/ui/separator"
import { Switch } from "@/components/ui/switch"

type Channels = { email: boolean; push: boolean }

const categories = [
  {
    id: "comments",
    label: "Comments",
    description: "When someone comments on your work.",
  },
  {
    id: "mentions",
    label: "Mentions",
    description: "When someone @mentions you.",
  },
  {
    id: "deploys",
    label: "Deployments",
    description: "When a deploy succeeds or fails.",
  },
  {
    id: "digest",
    label: "Weekly digest",
    description: "A summary of activity every Monday.",
  },
]

const initial: Record<string, Channels> = {
  comments: { email: true, push: true },
  mentions: { email: true, push: true },
  deploys: { email: false, push: true },
  digest: { email: true, push: false },
}

export default function NotificationsBlock() {
  const [prefs, setPrefs] = useState(initial)

  const toggle = (id: string, channel: keyof Channels) =>
    setPrefs((prev) => ({
      ...prev,
      [id]: { ...prev[id], [channel]: !prev[id][channel] },
    }))

  return (
    <section className="flex w-full items-start justify-center bg-background px-6 py-16 text-foreground">
      <Card className="w-full max-w-lg">
        <CardHeader>
          <CardTitle>Notification preferences</CardTitle>
          <CardDescription>
            Choose how you want to hear from us for each type of activity.
          </CardDescription>
        </CardHeader>
        <CardContent>
          <div className="grid grid-cols-[1fr_auto_auto] items-center gap-x-6 gap-y-1">
            <span />
            <span className="text-center text-xs font-semibold tracking-wider text-muted-foreground uppercase">
              Email
            </span>
            <span className="text-center text-xs font-semibold tracking-wider text-muted-foreground uppercase">
              Push
            </span>
            {categories.map((category, index) => (
              <div key={category.id} className="contents">
                {index > 0 && <Separator className="col-span-3 my-2.5" />}
                <div className="flex flex-col gap-0.5">
                  <span className="text-sm font-medium">{category.label}</span>
                  <span className="text-xs text-muted-foreground">
                    {category.description}
                  </span>
                </div>
                <div className="flex justify-center">
                  <Switch
                    checked={prefs[category.id].email}
                    onCheckedChange={() => toggle(category.id, "email")}
                    aria-label={`${category.label} email`}
                  />
                </div>
                <div className="flex justify-center">
                  <Switch
                    checked={prefs[category.id].push}
                    onCheckedChange={() => toggle(category.id, "push")}
                    aria-label={`${category.label} push`}
                  />
                </div>
              </div>
            ))}
          </div>
        </CardContent>
      </Card>
    </section>
  )
}
```
