Tabs
Split one object's content into peer views that users switch between without losing their place, state or URL.
6 states5 edge casesDesktop + Mobile
01The problem
Record pages grow: overview, activity, files, settings, billing. Showing everything on one page buries the important parts; splitting into separate pages loses context. Tabs solve this only when they behave predictably — the URL reflects the tab, state survives switching, and the tabs don't overflow off-screen.
02When to use
Use when
- Detail pages with several distinct views of the same object
- Settings areas with clearly separate sections
- Switching between peer views of the same data (List / Board / Calendar)
Avoid when
- Content users need to compare side by side — tabs hide one view while showing another.
- Sequential steps — use a stepper, which communicates order and progress.
- More than ~6 sections — use a sidebar or a settings index page.
03Recommended UX
Statecraft recommendation
Use tabs only for peer views of one object, reflect the active tab in the URL, and keep each tab's state — scroll, filters, unsaved input — when users switch away and back. Never put actions that affect the whole object inside one tab.
Users share links to “the Activity tab of this invoice”. If the URL doesn't encode the tab, the link lands on the wrong view and Back doesn't work.
Switching tabs is exploratory. Losing filters or a half-typed comment on every switch teaches users not to explore.
Object-wide actions (Edit, Delete, Share) belong in the object header above the tabs, visible from every tab.
04Interaction flow
- 01
Object header
Title, status and object-wide actions.
- 02
Tab bar
Default tab selected; URL reflects it.
- 03
Switch tab
Content swaps; header and scroll position of tab bar stay.
- 04
Return to a tab
Its filters, scroll and drafts are restored.
Result
- Viewing tab
- Shared deep link
05Desktop behaviour
On desktop, tabs sit directly under the object header in a single row. Labels are short nouns with optional counts.
Desktop rules
- One row only. If tabs don't fit, move the lowest-priority ones into a “More” menu instead of wrapping.
- Use links with real URLs (/invoices/1024/activity) or a query param — not client-only state.
- Show counts only where they help decide to open a tab: “Files 3”, not “Activity 241”.
- Keep the tab bar sticky on long pages so users can switch without scrolling back up.
06Mobile behaviour
On mobile, a row of 5 tabs won't fit. Either scroll the tab bar horizontally with a clear affordance, or replace tabs with a section picker.
Mobile rules
- For up to 4 short tabs, use a full-width segmented row.
- For more, use a horizontally scrollable tab bar with the active tab scrolled into view and a fade at the edge.
- Alternatively, use a dropdown section picker labelled with the current tab.
- Keep object-wide actions in the header overflow menu.
07States
Default tab
First or most used tab active on load; URL may omit it.
Active
Underline or fill plus stronger text; aria-selected or aria-current.
Loading tab
Tab bar stays interactive; content shows a skeleton of that tab's layout.
Unsaved changes
Tab label shows a dot; switching keeps the draft.
Overflow
Extra tabs in “More ▾”, which becomes active when one of them is.
Disabled / hidden
Tabs the user can't access are hidden, not disabled.
08Edge cases
EC-01
Unsaved form in a tab
The user edits Settings, then switches to Activity.
Recommendation
Keep the draft in memory and mark the tab with a dot. Warn only when leaving the object entirely, not when switching tabs.
EC-02
Deep link to a tab the user can't see
A member opens a link to the Billing tab, which only admins see.
Recommendation
Open the default tab with a notice: “Billing is only visible to admins.” Don't show a broken or empty tab.
EC-03
Active tab inside More
The active tab is one of the overflowed tabs.
Recommendation
Show the active tab's name on the More button (“Integrations ▾”) so the current location is never hidden.
EC-04
Tabs vs. filters
Tabs like “All / Open / Closed” are really filters on one list.
Recommendation
Use a segmented control in the list toolbar instead, and combine it with the filter state in the URL.
EC-05
Slow tab content
The Activity tab takes 2 seconds to load.
Recommendation
Switch the active tab immediately and show a skeleton; never block the tab bar until data arrives.
09Microcopy
Tab labels
Avoid
“Information, Data, Other”
Prefer
“Overview, Activity, Files, Settings”
Count
Prefer
“Files 3”
Overflow
Avoid
“…”
Prefer
“More ▾”
Restricted deep link
Prefer
“Billing is only visible to admins. Showing Overview.”
10Accessibility
- For in-page tabs, use the tabs pattern: role="tablist", role="tab" with aria-selected, and role="tabpanel" linked by aria-controls.
- Arrow keys move between tabs; Tab moves into the panel.
- If tabs navigate between URLs, use links with aria-current="page" instead of the ARIA tab roles.
- The unsaved-changes dot needs a text alternative such as “Settings, unsaved changes”.
11Developer notes
- 01
Pick one model and stick to it: route-based tabs (each tab a URL segment) or in-page tabs with a query param. Both must survive refresh and Back.
- 02
Preserve per-tab state in a store keyed by object and tab, or keep inactive panels mounted if they're cheap.
- 03
Prefetch the next likely tab's data on hover or after the first tab loads.
- 04
Compute visible tabs from permissions on the server.
12Implementation prompt
For Claude Code, Codex or Cursor
Implement tabs on the existing detail page. Requirements: - Object header (title, status, object-wide actions) above the tabs, visible on every tab. - Tabs reflect the URL (route segment or ?tab=), survive refresh and Back, and support deep links. - Single row; overflowed tabs go into "More ▾", which shows the active tab's name when relevant. - Preserve per-tab state (filters, scroll, unsaved drafts) across switches; mark tabs with unsaved changes. - Switch immediately and show a skeleton while a tab's data loads; prefetch likely tabs. - Hide tabs the user can't access; deep links to them fall back to the default tab with a notice. - Accessibility: tablist/tab/tabpanel with arrow-key navigation for in-page tabs, or links with aria-current for route tabs. - Sticky tab bar on long pages. - Mobile: segmented row for ≤4 tabs, otherwise a scrollable tab bar with the active tab in view, or a section picker. Do not redesign the existing interface.