# V checkbox 13

> Notification preferences matrix with event rows and channel columns

A shadcn/ui block from the [@cnippet](https://ui.cnippet.dev/) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/cnippet/v-checkbox-13).

- Page, with a live preview: https://shadcn.exchange/r/cnippet/v-checkbox-13
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/cnippet/v-checkbox-13-light-aacf0d1167b426c9.png
- Registry item: https://ui.cnippet.dev/r/v-checkbox-13.json
- Categories: checkbox
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://ui.cnippet.dev/r/v-checkbox-13.json
```

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

## Dependencies

- Registry items: `@cnippet/checkbox`

## Files

### `registry/default/variants/v-checkbox-13.tsx`

```tsx
"use client";

import { useId, useState } from "react";
import { Button } from "@/registry/default/ui/button";
import { Checkbox } from "@/registry/default/ui/checkbox";

const COLUMNS = [
  { id: "email", label: "Email" },
  { id: "push", label: "Push" },
  { id: "sms", label: "SMS" },
];

const EVENTS = [
  { id: "new-message", label: "New message" },
  { id: "mention", label: "Mention" },
  { id: "task-assigned", label: "Task assigned" },
  { id: "comment", label: "Comment" },
  { id: "status-change", label: "Status change" },
];

type Prefs = Record<string, boolean>;

function cellKey(event: string, channel: string) {
  return `${event}__${channel}`;
}

export function Pattern() {
  const id = useId();
  const [prefs, setPrefs] = useState<Prefs>(() =>
    Object.fromEntries(
      EVENTS.flatMap((e) =>
        COLUMNS.map((c) => [cellKey(e.id, c.id), c.id === "email"]),
      ),
    ),
  );

  const toggle = (k: string) => setPrefs((p) => ({ ...p, [k]: !p[k] }));

  return (
    <div className="w-full max-w-sm space-y-3">
      <p className="font-semibold text-sm">Notification Preferences</p>
      <div className="overflow-x-auto rounded-lg border">
        <table className="w-full text-xs">
          <thead>
            <tr className="border-b bg-muted/40">
              <th className="py-2 pr-4 pl-3 text-left font-medium text-muted-foreground">
                Event
              </th>
              {COLUMNS.map((c) => (
                <th
                  className="px-3 py-2 text-center font-medium text-muted-foreground"
                  key={c.id}
                >
                  {c.label}
                </th>
              ))}
            </tr>
          </thead>
          <tbody>
            {EVENTS.map((evt, i) => (
              <tr
                className={i < EVENTS.length - 1 ? "border-b" : ""}
                key={evt.id}
              >
                <td className="py-2.5 pr-4 pl-3 text-foreground">
                  {evt.label}
                </td>
                {COLUMNS.map((ch) => {
                  const k = cellKey(evt.id, ch.id);
                  return (
                    <td className="px-3 py-2.5 text-center" key={ch.id}>
                      <Checkbox
                        checked={prefs[k]}
                        id={`${id}-${k}`}
                        onCheckedChange={() => toggle(k)}
                      />
                    </td>
                  );
                })}
              </tr>
            ))}
          </tbody>
        </table>
      </div>
      <Button className="w-full" size="sm">
        Save preferences
      </Button>
    </div>
  );
}
```
