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.