ResponsiveTabs
Tabs that become a menu on narrow screens, and a variant that keeps the current tab in the address.
Responsive shows tabs from the lg width (1024 px) and a
full-width menu button below it, so many tabs never overflow a phone. Search is the same
component with the current tab in a search parameter of the address, so a link or a reload opens
the same tab.
// apps/docs/examples/ui/responsive-tabs.tsx'use client';
import {useState} from 'react';import {FileTextIcon, HistoryIcon, UserIcon} from 'lucide-react';
import { ResponsiveTabs, TabPanel,} from '@workspace/active-ui/components/composed/responsive-tabs';
const TABS = [ {value: 'person', label: 'Person', icon: UserIcon}, {value: 'documents', label: 'Documents', icon: FileTextIcon}, {value: 'history', label: 'History', icon: HistoryIcon},];
/** * Tabs on a wide screen, a menu on a narrow one: make the window narrower * than 1024 px to see the menu. The panels are the same in both forms. */export default function ResponsiveTabsExample() { const [tab, setTab] = useState('person'); return ( <div className="w-full max-w-xl"> <ResponsiveTabs value={tab} onValueChange={setTab} tabs={TABS} mobilePlaceholder="Choose a section" desktopContentClassName="pt-4" > <TabPanel id="person">Name, date of birth and address.</TabPanel> <TabPanel id="documents">Statements and letters.</TabPanel> <TabPanel id="history">Every change, newest first.</TabPanel> </ResponsiveTabs> </div> );}Keeping the tab in the address#
// apps/docs/examples/ui/search-param-tabs.tsx'use client';
import {TabPanel} from '@workspace/active-ui/components/composed/responsive-tabs';import {SearchParamTabs} from '@workspace/active-ui/components/composed/search-param-tabs';
/** * The same tabs, with the current tab in the address: choosing a tab sets * `?example-tab=` on this page, so a link or a reload opens it again. The * default tab leaves the parameter out, and an unknown value falls back to it. */export default function SearchParamTabsExample() { return ( <div className="w-full max-w-xl"> <SearchParamTabs queryParamName="example-tab" defaultValue="summary" tabs={[ {value: 'summary', label: 'Summary'}, {value: 'payments', label: 'Payments'}, ]} mobilePlaceholder="Choose a section" desktopContentClassName="pt-4" > <TabPanel id="summary">The totals of this year.</TabPanel> <TabPanel id="payments">Every payment of this year.</TabPanel> </SearchParamTabs> </div> );}Search reads and writes one search parameter (query, default tab) with
the Next.js App Router. Choosing the default tab removes the parameter; a value that matches no tab
shows the default tab and leaves the address as it is. Each choice adds a history entry, without
scrolling.
What the composition adds#
Two forms, one set of panels. On a wide screen a tab list; on a narrow one a button with the current tab's label that opens a menu with one radio item per tab.
Tabrenders the panels in both forms.Panel Icons per tab, before the label, in both forms.
Usage#
import { ResponsiveTabs, TabPanel,} from '@workspace/active-ui/components/composed/responsive-tabs';
<ResponsiveTabs value={tab} onValueChange={setTab} tabs={[ {value: 'person', label: 'Person'}, {value: 'history', label: 'History'}, ]} mobilePlaceholder="Choose a section"> <TabPanel id="person">…</TabPanel> <TabPanel id="history">…</TabPanel></ResponsiveTabs>;What your application provides#
The tabs and one
Tabper tab, with the tab'sPanel valueas itsid.For
Search: a Next.js App Router page, and a parameter name that does not clash with others on the page.Param Tabs
Accessibility and keyboard#
Wide: a tab list as on Tabs; the arrow keys move between tabs, and
EnterorSpaceactivates one.Narrow: the button opens a menu; the current tab is the checked radio item, the arrow keys move,
Enterchooses and closes the menu.
API#
ResponsiveTabs#
ResponsiveTabs
Props
- Type
ReactNode
One Tab per tab.
- Type
string
Text on the menu button when no tab matches value. Supplied translated.
- Type
readonly TabConfig[]
The tabs, in order.
- Type
string
The value of the current tab.
- Type
string
Classes for the wrapper of the tab list on wide screens.
- Type
string
Classes for the wrapper of the panels.
Events
Called by the component when something happens.
- Type
(value: string) => void
Receives the value of the chosen tab.
Takes only the props listed.
TabPanel
Types
The props of Responsive, each described in its prop list on this page.
interface ResponsiveTabsProps {
value: string;
onValueChange: (value: string) => void;
tabs: readonly TabConfig[];
children: React.ReactNode;
desktopClassName?: string;
desktopContentClassName?: string;
mobilePlaceholder: string;
}One tab: its identity, its label and an optional icon.
interface TabConfig {
value: string;
label: React.ReactNode;
icon?: React.ComponentType<{className?: string}>;
}The props of Tab, each described in its prop list on this page.
interface TabPanelProps {
id: string;
children: React.ReactNode;
className?: string;
}SearchParamTabs#
Import from @workspace/
Props
- Type
ReactNode
One Tab per tab.
- Type
string
The default tab value to use when the query parameter is not present in the URL.
- Type
string
Text on the menu button when no tab matches value. Supplied translated.
- Type
readonly TabConfig[]
The tabs, in order.
- Type
string
Classes for the wrapper of the tab list on wide screens.
- Type
string
Classes for the wrapper of the panels.
- Type
string- Default
'tab'
The name of the URL search parameter to sync with the active tab state.
Takes only the props listed.