How to Build a Settings Page in React with Tabs
A complete guide to building a tabbed settings page in React: profile, notifications, billing, and API keys — all accessible, all with Ninna UI components.
By Ninna UI Team
Settings pages are a staple of SaaS apps — and they're a great showcase for tab navigation, form components, and accessible interaction patterns. Let's build one with Ninna UI.
1. Tab structure
Use the Tabs component from @ninna-ui/navigation. It handles arrow-key navigation, ARIA tablist semantics, and focus management:
import { Tabs } from "@ninna-ui/navigation";import { Container } from "@ninna-ui/layout";
export function SettingsPage() { return ( <Container className="py-8 max-w-3xl"> <Tabs defaultValue="profile"> <Tabs.List> <Tabs.Trigger value="profile">Profile</Tabs.Trigger> <Tabs.Trigger value="notifications">Notifications</Tabs.Trigger> <Tabs.Trigger value="billing">Billing</Tabs.Trigger> <Tabs.Trigger value="api-keys">API Keys</Tabs.Trigger> </Tabs.List> <Tabs.Content value="profile">{/* Profile form */}</Tabs.Content> <Tabs.Content value="notifications">{/* Notification toggles */}</Tabs.Content> <Tabs.Content value="billing">{/* Billing info */}</Tabs.Content> <Tabs.Content value="api-keys">{/* API key management */}</Tabs.Content> </Tabs> </Container> );}2. Profile tab
import { Field, Input, Textarea } from "@ninna-ui/forms";import { Button, Avatar } from "@ninna-ui/primitives";import { VStack } from "@ninna-ui/layout";
<VStack gap="4"> <Avatar name="Jane Doe" size="xl" /> <Field label="Display name" htmlFor="name"> <Input id="name" defaultValue="Jane Doe" /> </Field> <Field label="Bio" htmlFor="bio"> <Textarea id="bio" rows={3} defaultValue="Product designer" /> </Field> <Button color="primary">Save changes</Button></VStack>3. Notifications tab
Use Switch components for toggle settings. Remember: Switch uses onCheckedChange, not onChange:
import { Switch } from "@ninna-ui/forms";import { VStack } from "@ninna-ui/layout";import { Text } from "@ninna-ui/primitives";
<VStack gap="4"> <div className="flex items-center justify-between"> <Text>Email notifications</Text> <Switch defaultChecked /> </div> <div className="flex items-center justify-between"> <Text>Push notifications</Text> <Switch /> </div> <div className="flex items-center justify-between"> <Text>Weekly digest</Text> <Switch defaultChecked /> </div></VStack>4. API keys tab
import { Button, Badge, Code } from "@ninna-ui/primitives";import { VStack, HStack } from "@ninna-ui/layout";
<VStack gap="3"> <HStack justify="between"> <Code>sk_live_****...****</Code> <Button size="sm" variant="outline">Rotate</Button> </HStack> <HStack justify="between"> <Code>sk_test_****...****</Code> <Button size="sm" variant="outline">Rotate</Button> </HStack> <Button color="primary" size="sm">Create new key</Button></VStack>There's a ready-made Settings block at /blocks/api-keys-settings that includes this pattern with copy-to-clipboard and delete confirmation.