# Settings Device

> A phone-style Settings screen with a search field, an account row, and grouped rows with switches.

A shadcn/ui block from the [@esuiss](https://suiss.app/ui?utm_source=shadcn.exchange) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/esuiss/settings-device).

- Page, with a live preview: https://shadcn.exchange/r/esuiss/settings-device
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/esuiss/settings-device-light-f73da656ea1be210.png
- Registry item: https://suiss.app/ui/r/new-york-v4/settings-device.json
- Categories: blocks, settings
- Listed: 2026-10-20, source last changed 2026-10-20

## Install

```bash
npx shadcn@latest add https://shadcn.exchange/r/esuiss/settings-device.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: `@esuiss/avatar`, `@esuiss/input-group`, `@esuiss/item`, `@esuiss/switch`

## Files

### `bases/radix/components/blocks/settings-device.tsx`

```tsx
"use client"

import * as React from "react"

import { IconPlaceholder } from "@/app/(create)/components/icon-placeholder"
import { Avatar, AvatarFallback } from "@/components/ui/avatar"
import {
  InputGroup,
  InputGroupAddon,
  InputGroupInput,
} from "@/components/ui/input-group"
import {
  Item,
  ItemActions,
  ItemContent,
  ItemDescription,
  ItemGroup,
  ItemMedia,
  ItemSeparator,
  ItemTitle,
} from "@/components/ui/item"
import { Switch } from "@/components/ui/switch"

type Toggles = {
  airplane: boolean
  wifi: boolean
  bluetooth: boolean
  focus: boolean
}

type Row = {
  label: string
  icon: React.ReactNode
  tint: string
  toggle?: keyof Toggles
  detail?: (toggles: Toggles) => string
}

const groups: Row[][] = [
  [
    {
      label: "Airplane Mode",
      icon: (
        <IconPlaceholder
          lucide="PlaneIcon"
          tabler="IconPlane"
          hugeicons="AirplaneIcon"
          phosphor="AirplaneIcon"
          remixicon="RiPlaneLine"
          className="size-4.25"
        />
      ),
      tint: "bg-orange-500",
      toggle: "airplane",
    },
    {
      label: "Wi-Fi",
      icon: (
        <IconPlaceholder
          lucide="WifiIcon"
          tabler="IconWifi"
          hugeicons="Wifi01Icon"
          phosphor="WifiHighIcon"
          remixicon="RiWifiLine"
          className="size-4.25"
        />
      ),
      tint: "bg-blue-500",
      detail: (toggles) => (toggles.wifi ? "Home" : "Off"),
    },
    {
      label: "Bluetooth",
      icon: (
        <IconPlaceholder
          lucide="BluetoothIcon"
          tabler="IconBluetooth"
          hugeicons="BluetoothIcon"
          phosphor="BluetoothIcon"
          remixicon="RiBluetoothLine"
          className="size-4.25"
        />
      ),
      tint: "bg-blue-500",
      detail: (toggles) => (toggles.bluetooth ? "On" : "Off"),
    },
    {
      label: "Cellular",
      icon: (
        <IconPlaceholder
          lucide="SignalIcon"
          tabler="IconAntennaBars5"
          hugeicons="SignalFull02Icon"
          phosphor="CellSignalFullIcon"
          remixicon="RiSignalCellular3Line"
          className="size-4.25"
        />
      ),
      tint: "bg-green-500",
      detail: (toggles) => (toggles.airplane ? "Off" : ""),
    },
  ],
  [
    {
      label: "Notifications",
      icon: (
        <IconPlaceholder
          lucide="BellIcon"
          tabler="IconBell"
          hugeicons="Notification03Icon"
          phosphor="BellIcon"
          remixicon="RiNotificationLine"
          className="size-4.25"
        />
      ),
      tint: "bg-red-500",
    },
    {
      label: "Sounds & Haptics",
      icon: (
        <IconPlaceholder
          lucide="Volume2Icon"
          tabler="IconVolume"
          hugeicons="VolumeHighIcon"
          phosphor="SpeakerHighIcon"
          remixicon="RiVolumeUpLine"
          className="size-4.25"
        />
      ),
      tint: "bg-pink-500",
    },
    {
      label: "Focus",
      icon: (
        <IconPlaceholder
          lucide="MoonIcon"
          tabler="IconMoon"
          hugeicons="MoonIcon"
          phosphor="MoonIcon"
          remixicon="RiMoonLine"
          className="size-4.25"
        />
      ),
      tint: "bg-indigo-500",
      toggle: "focus",
    },
    {
      label: "Screen Time",
      icon: (
        <IconPlaceholder
          lucide="HourglassIcon"
          tabler="IconHourglass"
          hugeicons="HourglassIcon"
          phosphor="HourglassIcon"
          remixicon="RiHourglassLine"
          className="size-4.25"
        />
      ),
      tint: "bg-indigo-500",
    },
  ],
  [
    {
      label: "General",
      icon: (
        <IconPlaceholder
          lucide="SettingsIcon"
          tabler="IconSettings"
          hugeicons="Settings01Icon"
          phosphor="GearIcon"
          remixicon="RiSettingsLine"
          className="size-4.25"
        />
      ),
      tint: "bg-zinc-500",
    },
    {
      label: "Display & Brightness",
      icon: (
        <IconPlaceholder
          lucide="SunIcon"
          tabler="IconSun"
          hugeicons="Sun03Icon"
          phosphor="SunIcon"
          remixicon="RiSunLine"
          className="size-4.25"
        />
      ),
      tint: "bg-blue-500",
    },
  ],
]

function RowLink(props: React.ComponentProps<"a">) {
  return <a {...props} />
}

function RowShell({ href, ...props }: React.ComponentProps<"a">) {
  if (href) return <a href={href} {...props} />
  return <div {...(props as React.ComponentProps<"div">)} />
}

const groupClass = "shrink-0 bg-background dark:bg-muted"

export function SettingsDevice() {
  const idPrefix = React.useId()
  const [query, setQuery] = React.useState("")
  const [toggles, setToggles] = React.useState<Toggles>({
    airplane: false,
    wifi: true,
    bluetooth: true,
    focus: false,
  })

  const setToggle = (key: keyof Toggles, value: boolean) =>
    setToggles((current) => ({
      ...current,
      [key]: value,
      ...(key === "airplane" && value ? { wifi: false, bluetooth: false } : {}),
    }))

  const matches = (label: string) =>
    !query || label.toLowerCase().includes(query.toLowerCase())
  const visible = groups
    .map((rows) => rows.filter((row) => matches(row.label)))
    .filter((rows): rows is [Row, ...Row[]] => rows.length > 0)

  return (
    <section className="flex min-h-170 items-center justify-center bg-muted sm:p-5">
      <div className="relative flex w-full flex-col overflow-hidden bg-muted sm:h-165 sm:w-90 sm:rounded-[3.125rem] sm:border-[10px] sm:border-black sm:shadow-[0_0_0_1.5px_var(--color-muted-foreground),0_30px_60px_-20px_rgb(0_0_0/0.45)] dark:bg-background">
        <div
          aria-hidden
          className="hidden h-12.5 shrink-0 items-center justify-between ps-8.5 pe-7 pt-1 text-base font-semibold sm:flex"
        >
          <span>9:41</span>
          <span className="flex items-center gap-1.25">
            <IconPlaceholder
              lucide="SignalIcon"
              tabler="IconAntennaBars5"
              hugeicons="SignalFull02Icon"
              phosphor="CellSignalFullIcon"
              remixicon="RiSignalCellular3Line"
              className="size-3.75"
            />
            {toggles.wifi && (
              <IconPlaceholder
                lucide="WifiIcon"
                tabler="IconWifi"
                hugeicons="Wifi01Icon"
                phosphor="WifiHighIcon"
                remixicon="RiWifiLine"
                className="size-3.75"
              />
            )}
            <span className="flex h-2.75 w-6 rounded-[3px] border border-current p-[1.5px]">
              <span className="flex-1 rounded-[1.5px] bg-current" />
            </span>
          </span>
        </div>
        <span
          aria-hidden
          className="absolute top-2.75 left-1/2 hidden h-7.25 w-25 -translate-x-1/2 rounded-full bg-black sm:block"
        />
        <div className="no-scrollbar flex flex-1 flex-col gap-4.5 overflow-y-auto px-4 pt-6 pb-6 sm:pt-1.5">
          <h1 className="px-1 text-4xl font-bold tracking-tight">Settings</h1>
          <InputGroup className="-mt-2 h-9 shrink-0 rounded-[0.625rem] dark:bg-muted">
            <InputGroupAddon>
              <IconPlaceholder
                lucide="SearchIcon"
                tabler="IconSearch"
                hugeicons="Search01Icon"
                phosphor="MagnifyingGlassIcon"
                remixicon="RiSearchLine"
              />
            </InputGroupAddon>
            <InputGroupInput
              type="search"
              value={query}
              onChange={(event) => setQuery(event.target.value)}
              placeholder="Search"
              aria-label="Search settings"
              className="text-lg"
            />
          </InputGroup>
          {!query && (
            <ItemGroup variant="inset" className={groupClass}>
              <div role="listitem" className="contents">
                <Item size="sm" className="gap-3.5 py-2.5" asChild>
                  <RowLink href="#account">
                    <Avatar className="size-14.5">
                      <AvatarFallback className="bg-linear-to-br from-[oklch(0.78_0.1_250)] to-[oklch(0.6_0.15_280)] text-xl text-white dark:from-[oklch(0.78_0.1_250)] dark:to-[oklch(0.6_0.15_280)]">
                        JR
                      </AvatarFallback>
                    </Avatar>
                    <ItemContent className="gap-0.5">
                      <ItemTitle className="text-xl font-semibold">
                        Jamie Rivera
                      </ItemTitle>
                      <ItemDescription className="text-sm">
                        suiss Account, Cloud and more
                      </ItemDescription>
                    </ItemContent>
                    <IconPlaceholder
                      lucide="ChevronRightIcon"
                      tabler="IconChevronRight"
                      hugeicons="ArrowRight01Icon"
                      phosphor="CaretRightIcon"
                      remixicon="RiArrowRightSLine"
                      className="size-3.5 text-muted-foreground rtl:rotate-180"
                    />
                  </RowLink>
                </Item>
              </div>
            </ItemGroup>
          )}
          {visible.map((rows) => (
            <ItemGroup
              key={rows[0].label}
              variant="inset"
              className={`${groupClass} **:data-[slot=item-separator]:ms-14.25`}
            >
              {rows.map((row, index) => {
                const detail = row.detail?.(toggles)
                const switchId = `${idPrefix}-${row.toggle}`
                return (
                  <div key={row.label} role="listitem" className="contents">
                    {index > 0 && <ItemSeparator />}
                    <Item size="sm" asChild className="min-h-11 gap-3.5 py-1.5">
                      <RowShell href={row.toggle ? undefined : `#${row.label}`}>
                        <ItemMedia
                          className={`size-7.25 rounded-[7px] text-white ${row.tint}`}
                        >
                          {row.icon}
                        </ItemMedia>
                        <ItemContent>
                          <ItemTitle className="text-base font-normal">
                            {row.toggle ? (
                              <label htmlFor={switchId}>{row.label}</label>
                            ) : (
                              row.label
                            )}
                          </ItemTitle>
                        </ItemContent>
                        <ItemActions className="gap-1.5 text-base text-muted-foreground">
                          {row.toggle ? (
                            <Switch
                              id={switchId}
                              checked={toggles[row.toggle]}
                              onCheckedChange={(value) =>
                                setToggle(row.toggle as keyof Toggles, value)
                              }
                            />
                          ) : (
                            <>
                              {detail}
                              <IconPlaceholder
                                lucide="ChevronRightIcon"
                                tabler="IconChevronRight"
                                hugeicons="ArrowRight01Icon"
                                phosphor="CaretRightIcon"
                                remixicon="RiArrowRightSLine"
                                className="size-3.5 text-muted-foreground rtl:rotate-180"
                              />
                            </>
                          )}
                        </ItemActions>
                      </RowShell>
                    </Item>
                  </div>
                )
              })}
            </ItemGroup>
          ))}
          {visible.length === 0 && (
            <p className="p-7.5 text-center text-base text-muted-foreground">
              No Results
            </p>
          )}
        </div>
      </div>
    </section>
  )
}
```
