# Settings Panel

> A settings sheet: profile fields, notification switches, a paper picker, and a Save button that toasts with a buddy.

A shadcn/ui block from the [@plopui](https://plopui.com/?utm_source=shadcn.exchange) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/plopui/settings-panel).

- Page, with a live preview: https://shadcn.exchange/r/plopui/settings-panel
- Screenshot: https://shadcn.exchange/shot/plopui/settings-panel/light.png
- Registry item: https://plopui.com/r/settings-panel.json
- Categories: blocks
- Listed: 2026-11-05, source last changed 2026-11-05

## Install

```bash
npx shadcn@latest add https://shadcn.exchange/r/plopui/settings-panel.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: `@plop-ui/fonts`, `@plop-ui/react`
- Registry items: `https://plopui.com/r/plop-setup.json`

## Files

### `registry/plopui/settings-panel.tsx`

```tsx
"use client";

import { useState, type CSSProperties, type FormEvent } from "react";
import { Button, Heading, Select, Sheet, Switch, TextField, toast } from "@plop-ui/react";

/** A settings sheet: profile fields, notification switches, a paper picker, and a Save button that toasts with a buddy. */
export function SettingsPanel() {
  const [name, setName] = useState("Rosa Santos");
  const [email, setEmail] = useState("rosa@example.com");
  const [replies, setReplies] = useState(true);
  const [digest, setDigest] = useState(false);
  const [paper, setPaper] = useState("poster");
  const [saving, setSaving] = useState(false);
  const emailError = email.includes("@") ? undefined : "That doesn't look like an email.";

  function save(e: FormEvent) {
    e.preventDefault();
    if (emailError) return;
    setSaving(true);
    // Replace with your own save call.
    setTimeout(() => {
      setSaving(false);
      toast.success("Settings saved", { buddy: true });
    }, 800);
  }

  return (
    <Sheet tape="mustard">
      <form onSubmit={save} noValidate style={stack}>
        <Heading level={2}>Settings</Heading>

        <fieldset style={group}>
          <legend style={legend}>Profile</legend>
          <TextField label="Name" value={name} onChange={(e) => setName(e.target.value)} autoComplete="name" />
          <TextField
            label="Email"
            type="email"
            value={email}
            onChange={(e) => setEmail(e.target.value)}
            autoComplete="email"
            error={emailError}
          />
        </fieldset>

        <fieldset style={group}>
          <legend style={legend}>Notifications</legend>
          <Switch checked={replies} onCheckedChange={setReplies}>
            Email me when someone replies
          </Switch>
          <Switch checked={digest} onCheckedChange={setDigest} color="ink">
            Weekly digest
          </Switch>
        </fieldset>

        <Select
          label="Paper"
          hint="Pass it to your <PlopProvider surface>."
          value={paper}
          onValueChange={setPaper}
          options={[
            { value: "poster", label: "Poster" },
            { value: "notebook", label: "Notebook" },
            { value: "kraft", label: "Kraft" },
            { value: "riso", label: "Riso" },
            { value: "mat", label: "Cutting mat" },
          ]}
        />

        <div style={row}>
          <Button type="submit" variant="ink" loading={saving} loadingLabel="Saving">
            Save changes
          </Button>
          <Button type="reset" variant="plain">
            Cancel
          </Button>
        </div>
      </form>
    </Sheet>
  );
}

const stack: CSSProperties = { display: "grid", gap: 24, maxWidth: 440 };
const group: CSSProperties = { display: "grid", gap: 16, margin: 0, padding: 0, border: 0 };
const legend: CSSProperties = { marginBottom: 8, fontFamily: "var(--plop-font-letter)", fontStyle: "italic", fontWeight: 700, textTransform: "uppercase", color: "var(--plop-ink)" };
const row: CSSProperties = { display: "flex", flexWrap: "wrap", gap: 16, paddingTop: 8 };
```
