Skip to main content
All articles
Tutorial July 1, 2026 8 min read

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.


Build it with Ninna UI

Accessible React components, CSS-only theming, Tailwind v4 native.

Get Started

Keep reading