Settings screen

ProReact Native · Expo

A complete settings screen in inset grouped sections: account, notification switches that flip optimistically, a light, dark and system appearance switch, privacy, storage with a cache size that rolls down to zero when cleared, about, and a destructive sign out confirmed in an alert that runs it.

View as Markdown
Try it on your phone
Scan to open this demo in your phone's browser and use it with your fingers.

Installation

$ npx shadcn@latest add @goodui-native-pro/settings-screen

Then run npx expo install --fix so react-native-svg match your Expo SDK. Files land in components/goodui-native/settings-screen.

Pro screens install from the license-gated @goodui-native-pro registry. Setup: Pro access.

  • Wrap the app in GestureHandlerRootView and ThemeProvider (the alert renders in its overlay layer).

Usage

// app/(tabs)/settings.tsx
import { Stack, router } from "expo-router";
import { SettingsScreen } from "@/components/goodui-native/settings-screen/settings-screen";
import { api, useAccount } from "@/lib/account";
import { useColorSchemeSetting } from "@/lib/theme"; // your store behind ThemeProvider colorScheme

export default function SettingsRoute() {
  const account = useAccount();
  const [appearance, setAppearance] = useColorSchemeSetting();
  return (
    <>
      <Stack.Screen options={{ headerShown: false }} />
      <SettingsScreen
        account={{ name: account.name, email: account.email, avatar: account.photoUrl, plan: account.plan }}
        appearance={appearance}
        onAppearanceChange={setAppearance}
        notifications={account.notifications}
        onNotificationChange={(key, value) => api.updateNotifications({ [key]: value })}
        privateAccount={account.isPrivate}
        onPrivateAccountChange={(value) => api.updatePrivacy({ isPrivate: value })}
        cacheSize={account.cacheMegabytes}
        onClearCache={() => api.clearCache()}
        onNavigate={(route) => router.push(`/settings/${route}`)}
        onSignOut={async () => {
          await api.signOut();
          router.replace("/sign-in");
        }}
      />
    </>
  );
}

When to use

Use it for
  • The settings tab or screen of a consumer app with an account, notifications and appearance.
  • A starting point you trim: every navigation row only shows a chevron when you pass onNavigate.
Reach for something else
  • Editing profile fields (name, username, bio): use edit-profile-screen and link to it from the Edit profile row.
  • Settings with dozens of rows or search: compose ListSection and ListRow in a sticky-header-list or your own sections.

Examples

On its own, with a sample account

<SettingsScreen />

Drive the app theme from the appearance switch

const [scheme, setScheme] = useState<"light" | "dark" | "system">("system");

<ThemeProvider colorScheme={scheme}>
  <SettingsScreen appearance={scheme} onAppearanceChange={setScheme} />
</ThemeProvider>

API reference

PropTypeDefault
accountSettingsAccountsampleAccount
appearance
Appearance (controlled). Pair it with `onAppearanceChange`.
"light" | "dark" | "system"—
defaultAppearance
Starting appearance when uncontrolled. Default "system".
"light" | "dark" | "system""system"
onAppearanceChange
Called with light, dark or system. Wire it to ThemeProvider's colorScheme.
(appearance: SettingsAppearance) => void—
notifications
Starting notification switches.
SettingsNotifications{ push: true, email: false, sounds: true }
onNotificationChange
A notification switch changed. Return a promise: the switch flips at once and flips back if it rejects.
(key: keyof SettingsNotifications, value: boolean) => unknown—
privateAccount
Starting state of the private account switch.
booleanfalse
onPrivateAccountChange
The private account switch changed. Return a promise for an optimistic switch with rollback.
(value: boolean) => unknown—
quietHours
Quiet hours as 24 h "HH:mm" start and end, shown with the device's time format.
{ start: string; end: string } | null{ start: "22:00", end: "07:00" }
blockedCount
Number of blocked accounts shown in its row.
number3
twoFactor
Shows two-factor authentication as on or off.
booleantrue
cacheSize
Cache size in megabytes.
number248.6
onClearCache
Clear cache was tapped. Return a promise: the row spins, then the size rolls down to zero.
() => unknown—
version
Version shown in About, for example "2.4.1 (318)".
string"2.4.1 (318)"
onNavigate
A navigation row was tapped.
(route: SettingsRoute) => void—
onSignOut
Sign out was confirmed in the alert. Return a promise and the alert keeps a spinner on Sign out until it settles; it closes on success and shakes with an error on failure.
() => unknown—
title
Large title. Default "Settings".
string"Settings"
onBack
Shows a back button in the bar.
() => void—
locale
BCP 47 locale for numbers and times. Defaults to the device locale.
string—
testID
Identifier for end-to-end tests.
string—

Motion and haptics

  • Clearing the cache: the row spins, then the size rolls down digit by digit to zero.
  • The large title collapses into the bar as you scroll; the appearance highlight stretches between segments and can be dragged.
  • Switches squash and stretch as they flip and flip back with a shake if their promise rejects.
  • Sign out: the alert springs in, keeps a spinner on Sign out while it runs and shakes on failure.
  • Reduced motion: the size changes in place, the title cross-fades without moving, the highlight moves without stretching and the alert fades.
  • Haptic: toggle-on or toggle-off on a switch flip, select on an appearance change, error when a switch's promise rejects
  • Haptic: error when Clear cache rejects
  • Haptic: success or error when the sign out promise settles (AlertDialog)
  • Switches, the segment highlight, the digit roll and the title collapse run on the UI thread.
  • Rows are plain views; the whole screen renders once and only rows whose state changes update.

Accessibility

  • Section headers are headers; navigation rows are buttons named by their title and value; switch rows hand the title to the switch.
  • Appearance is a tab list named 'Appearance' with the selected option marked.
  • The cache row reads 'Cache, 248.6 MB' on native; on the web its visible text names it. 'Cache cleared' is announced when the size reaches zero.
  • Clear cache reports busy while it runs and is disabled at zero.
  • Sign out opens an alert dialog that needs an answer: Cancel or Sign out, with the error announced if signing out fails.

Notes for AI

  • Pass appearance and onAppearanceChange and feed the value to ThemeProvider colorScheme; 'system' follows the device.
  • Switch values are starting states; the switches keep their own state and call onNotificationChange(key, value) or onPrivateAccountChange(value). Return a promise for a server setting and a rejection flips the switch back.
  • onClearCache and onSignOut may return promises; the screen shows pending and error states itself. Navigate after sign out inside your onSignOut.
  • Navigation rows only become pressable when onNavigate is passed; route keys are edit-profile, subscription, password, quiet-hours, blocked, two-factor, help, terms and privacy-policy.
  • quietHours takes 24 h 'HH:mm' strings and shows them in the device's time format; pass null for Off.
  • Render it full-bleed with the navigator header hidden (headerShown: false).
List row

Settings and navigation cells with a token-colored icon tile, title, subtitle and a trailing value, switch, badge, check or chevron, grouped in inset sections with headers and footers. On touch down the highlight floods in, the tile sinks and the chevron leans forward; on release the highlight lingers and fades.

Switch

An iOS-style switch with squash and stretch: the thumb stretches toward where it is going on touch down, then travels on a spring while the track color follows. Settings-row layout, tones, sizes and optimistic async changes.

Segmented control

Two to five views of the same thing with an elastic highlight that stretches toward the new segment, can be dragged, and ticks at every boundary. Includes a billing toggle.

Alert dialog

A centered alert that springs in from slightly larger; touch an action and slide across the others with a tick per action, tap outside and it nudges, and async actions keep it open with a spinner until they settle.

Rolling number

A number whose digits roll in the direction of change, with digits that open and close in place and currency signs that glide instead of jumping.

Edit profile screen

Edit your own profile: change the photo from an action sheet (take, choose, remove) and watch the new one spring in, fields that validate as you leave them, Save with its own pending, saved and error states riding on the keyboard, and an alert that guards unsaved changes on back.

Large title header

An iOS large title that collapses into a compact bar as the content scrolls: the titles cross-fade, the bar fades in, the title stretches when pulled past the top and a half-finished collapse settles on either state.