Siteplane UI

Settings List

Arrange labeled settings and their controls in a shared card.

Markdown

Project settings

Manage visibility and notifications for your project.

Visibility
Choose who can access this project.
Email notifications
Receive updates about activity in this project.
Weekly digest
A summary of project activity every Monday.
"use client";

import {
  Select,
  SelectPopup,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/siteplane/components/select";
import {
  SettingsList,
  SettingsListItem,
} from "@/siteplane/components/settings-list";
import { Switch } from "@/siteplane/components/switch";

const visibilityOptions = [
  { label: "Workspace", value: "workspace" },
  { label: "Private", value: "private" },
];

export default function Example() {
  return (
    <SettingsList
      className="max-w-md"
      description="Manage visibility and notifications for your project."
      title="Project settings"
    >
      <SettingsListItem
        control={(controlProps) => (
          <Select defaultValue="workspace" items={visibilityOptions}>
            <SelectTrigger className="w-40" {...controlProps}>
              <SelectValue />
            </SelectTrigger>
            <SelectPopup>
              {visibilityOptions.map(({ label, value }) => (
                <SelectItem key={value} value={value}>
                  {label}
                </SelectItem>
              ))}
            </SelectPopup>
          </Select>
        )}
        description="Choose who can access this project."
        title="Visibility"
      />
      <SettingsListItem
        control={(controlProps) => <Switch {...controlProps} defaultChecked />}
        description="Receive updates about activity in this project."
        layout="inline"
        title="Email notifications"
      />
      <SettingsListItem
        control={(controlProps) => <Switch {...controlProps} />}
        description="A summary of project activity every Monday."
        layout="inline"
        title="Weekly digest"
      />
    </SettingsList>
  );
}

Installation

pnpm dlx shadcn@4.18.0 add @siteplane/settings-list

Complete the one-time setup first. Registry JSON lists source files and dependencies.

For this page’s examples, also install:

pnpm dlx shadcn@4.18.0 add @siteplane/badge @siteplane/input @siteplane/select @siteplane/switch

Usage

import {
  SettingsList,
  SettingsListItem,
} from "@/siteplane/components/settings-list";
import { Switch } from "@/siteplane/components/switch";

<SettingsList title="Preferences">
  <SettingsListItem
    title="Email updates"
    control={(props) => <Switch {...props} />}
  />
</SettingsList>;

Composition

SettingsList
└── SettingsListItem
    └── control(props) → Switch, Input or another public control

Controlled values

Keep the values in your application state and spread every prop passed to control, so the title and description stay associated with the control. An action button, such as Change or Manage, keeps its own text as its name, so pass it only aria-describedby. tag adds a badge beside the title, and layout="inline" keeps a short control next to its label in a narrow list.

Profile

How other members see you in this workspace.

Display name
Product updates
Occasional news about new features.
Single sign-onManaged
Your organization manages sign-in for this workspace.
"use client";

import { useState } from "react";
import { Badge } from "@/siteplane/components/badge";
import { Input } from "@/siteplane/components/input";
import {
  SettingsList,
  SettingsListItem,
} from "@/siteplane/components/settings-list";
import { Switch } from "@/siteplane/components/switch";

export default function Example() {
  const [name, setName] = useState("Mara");
  const [productUpdates, setProductUpdates] = useState(true);
  return (
    <SettingsList
      description="How other members see you in this workspace."
      title="Profile"
    >
      <SettingsListItem
        control={(props) => (
          <Input {...props} onValueChange={setName} value={name} />
        )}
        title="Display name"
      />
      <SettingsListItem
        control={(props) => (
          <Switch
            {...props}
            checked={productUpdates}
            onCheckedChange={setProductUpdates}
          />
        )}
        description="Occasional news about new features."
        layout="inline"
        title="Product updates"
      />
      <SettingsListItem
        control={(props) => <Switch {...props} defaultChecked disabled />}
        description="Your organization manages sign-in for this workspace."
        layout="inline"
        tag={<Badge variant="outline">Managed</Badge>}
        title="Single sign-on"
      />
    </SettingsList>
  );
}

API reference

Component properties
PropTypeDefaultDescription
SettingsList.titleReactNoderequiredSection heading, an h2 unless titleLevel sets another level.
SettingsList.titleLevel1 | 2 | 3 | 4 | 5 | 62Heading level of the title, with the same look at every level. A list inside a section of a settings page takes the next level.
SettingsList.descriptionReactNodeunsetSection help text.
SettingsListItem.titleReactNoderequiredAccessible control label.
SettingsListItem.descriptionReactNodeunsetAssociated description.
SettingsListItem.control(props: SettingsListControlProps) => ReactNoderequiredRenders the actual control with generated aria-labelledby and optional aria-describedby. An action button takes only aria-describedby, so it keeps its own text as its name.
SettingsListItem.layoutinline | responsiveresponsiveResponsive stacks until the list is 36rem (576 px) wide; inline keeps label and control together.
SettingsListItem.tagReactNodeunsetSupplementary tag beside the title.

SettingsListProps extends native section props without native title. SettingsListItemProps extends native div props without title/children. SettingsListControlProps contains aria-labelledby and optional aria-describedby. Both wrappers accept className but their types do not expose ref. The card, separators and associations are provided by the component; values, request state, validation and saving are application-owned.

Read the installed source.

Usage notes

Use responsive layout for wider text controls. The rows follow the width of the list, not of the screen: a responsive row puts its control beside its copy once the list is 36rem (576 px) wide, and the card padding, the header inset and the row gaps widen with it. A list beside a sidebar or in a column therefore stays stacked while it is narrow. The list is a size container named settings-list, so a control can follow the same switch: className="w-full @min-[36rem]/settings-list:w-80" fills a stacked row and takes 320 px beside the copy. Do not replace control props with hard-coded labels that break the generated association. The list itself is not a form and does not submit values.