Back to components
Form

Settings Form

A tabbed settings form with input, select, textarea, switch, checkbox, and radio controls.

Page preview
Source
This is the component source that shadcn installs into a consuming project.
registry/default/settings-form/settings-form.tsx
import { BellIcon, GlobeIcon, LockIcon } from "lucide-react"

import { Button } from "@/components/ui/button"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Checkbox } from "@/components/ui/checkbox"
import {
  Field,
  FieldContent,
  FieldDescription,
  FieldGroup,
  FieldLabel,
  FieldLegend,
  FieldSet,
} from "@/components/ui/field"
import { Input } from "@/components/ui/input"
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"
import {
  Select,
  SelectContent,
  SelectGroup,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from "@/components/ui/select"
import { Separator } from "@/components/ui/separator"
import { Switch } from "@/components/ui/switch"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { Textarea } from "@/components/ui/textarea"

type SettingsFormCopy = {
  title: string
  description: string
  tabs: [string, string, string]
  projectName: string
  projectNamePlaceholder: string
  region: string
  regions: [string, string, string]
  descriptionLabel: string
  descriptionPlaceholder: string
  emailAlerts: string
  emailAlertsDescription: string
  deployAlerts: string
  deployAlertsDescription: string
  visibility: string
  visibilityOptions: [string, string]
  requireReview: string
  requireReviewDescription: string
  save: string
}

type SettingsFormProps = {
  copy?: SettingsFormCopy
}

const defaultCopy: SettingsFormCopy = {
  title: "Project settings",
  description: "A form layout for product settings, account pages, and onboarding flows.",
  tabs: ["General", "Notifications", "Security"],
  projectName: "Project name",
  projectNamePlaceholder: "Registry UI",
  region: "Region",
  regions: ["US East", "Europe", "Asia Pacific"],
  descriptionLabel: "Description",
  descriptionPlaceholder: "Describe how this workspace should be used.",
  emailAlerts: "Email alerts",
  emailAlertsDescription: "Send critical updates to workspace owners.",
  deployAlerts: "Deploy notifications",
  deployAlertsDescription: "Notify the team when registry builds finish.",
  visibility: "Visibility",
  visibilityOptions: ["Team only", "Public preview"],
  requireReview: "Require review before publishing",
  requireReviewDescription: "New registry items must be approved before release.",
  save: "Save changes",
}

export function SettingsForm({ copy = defaultCopy }: SettingsFormProps) {
  return (
    <Card className="w-full max-w-2xl">
      <CardHeader>
        <CardTitle>{copy.title}</CardTitle>
        <CardDescription>{copy.description}</CardDescription>
      </CardHeader>
      <CardContent>
        <Tabs defaultValue="general">
          <TabsList>
            <TabsTrigger value="general">
              <GlobeIcon data-icon="inline-start" />
              {copy.tabs[0]}
            </TabsTrigger>
            <TabsTrigger value="notifications">
              <BellIcon data-icon="inline-start" />
              {copy.tabs[1]}
            </TabsTrigger>
            <TabsTrigger value="security">
              <LockIcon data-icon="inline-start" />
              {copy.tabs[2]}
            </TabsTrigger>
          </TabsList>

          <TabsContent value="general" className="pt-4">
            <FieldGroup>
              <Field>
                <FieldLabel htmlFor="project-name">{copy.projectName}</FieldLabel>
                <Input id="project-name" placeholder={copy.projectNamePlaceholder} />
              </Field>
              <Field>
                <FieldLabel>{copy.region}</FieldLabel>
                <Select defaultValue="us-east">
                  <SelectTrigger className="w-full">
                    <SelectValue />
                  </SelectTrigger>
                  <SelectContent>
                    <SelectGroup>
                      <SelectItem value="us-east">{copy.regions[0]}</SelectItem>
                      <SelectItem value="europe">{copy.regions[1]}</SelectItem>
                      <SelectItem value="asia-pacific">{copy.regions[2]}</SelectItem>
                    </SelectGroup>
                  </SelectContent>
                </Select>
              </Field>
              <Field>
                <FieldLabel htmlFor="project-description">
                  {copy.descriptionLabel}
                </FieldLabel>
                <Textarea
                  id="project-description"
                  placeholder={copy.descriptionPlaceholder}
                />
              </Field>
            </FieldGroup>
          </TabsContent>

          <TabsContent value="notifications" className="pt-4">
            <FieldGroup>
              <Field orientation="horizontal">
                <Switch id="email-alerts" defaultChecked />
                <FieldContent>
                  <FieldLabel htmlFor="email-alerts">
                    {copy.emailAlerts}
                  </FieldLabel>
                  <FieldDescription>
                    {copy.emailAlertsDescription}
                  </FieldDescription>
                </FieldContent>
              </Field>
              <Separator />
              <Field orientation="horizontal">
                <Checkbox id="deploy-alerts" defaultChecked />
                <FieldContent>
                  <FieldLabel htmlFor="deploy-alerts">
                    {copy.deployAlerts}
                  </FieldLabel>
                  <FieldDescription>
                    {copy.deployAlertsDescription}
                  </FieldDescription>
                </FieldContent>
              </Field>
            </FieldGroup>
          </TabsContent>

          <TabsContent value="security" className="pt-4">
            <FieldGroup>
              <FieldSet>
                <FieldLegend>{copy.visibility}</FieldLegend>
                <RadioGroup defaultValue="team">
                  <Field orientation="horizontal">
                    <RadioGroupItem id="visibility-team" value="team" />
                    <FieldLabel htmlFor="visibility-team">
                      {copy.visibilityOptions[0]}
                    </FieldLabel>
                  </Field>
                  <Field orientation="horizontal">
                    <RadioGroupItem id="visibility-public" value="public" />
                    <FieldLabel htmlFor="visibility-public">
                      {copy.visibilityOptions[1]}
                    </FieldLabel>
                  </Field>
                </RadioGroup>
              </FieldSet>
              <Separator />
              <Field orientation="horizontal">
                <Switch id="require-review" defaultChecked />
                <FieldContent>
                  <FieldLabel htmlFor="require-review">
                    {copy.requireReview}
                  </FieldLabel>
                  <FieldDescription>
                    {copy.requireReviewDescription}
                  </FieldDescription>
                </FieldContent>
              </Field>
            </FieldGroup>
          </TabsContent>
        </Tabs>

        <div className="mt-5 flex justify-end">
          <Button>{copy.save}</Button>
        </div>
      </CardContent>
    </Card>
  )
}