Tabs
Examples
(8)Properties
(45)Playground
Basics
Loading demo…
import
{
Block
,
Tabs
,
Text
}
from
'@platform-blocks/react-ui-library'
;
const
ITEMS
=
[
{
key
:
'overview'
,
label
:
'Overview'
,
content
:
<Text
>
High
-
level summary and entry point
.
</Text
>
}
,
{
key
:
'details'
,
label
:
'Details'
,
content
:
<Text
>
Deeper
dive into metrics and configuration
.
</Text
>
}
,
{
key
:
'activity'
,
label
:
'Activity'
,
content
:
<Text
>
Recent
events
,
tasks
,
and notifications
.
</Text
>
}
]
;
export
function
Demo
(
)
{
return
(
<Block
>
<Tabs
items
=
{
ITEMS
}
/>
<Text
variant
=
"small"
color
=
"muted"
>
Tabs
render inline content directly below the active trigger by
default
.
</Text
>
</Block
>
)
;
}
Animated transitions
animated flag and custom duration to smooth the switch between panels.Loading demo…
import
{
Block
,
Tabs
,
Text
}
from
'@platform-blocks/react-ui-library'
;
const
ITEMS
=
[
{
key
:
'home'
,
label
:
'Home'
,
content
:
(
<Block
>
<Text
weight
=
"medium"
>
Welcome
back
</Text
>
<Text
color
=
"muted"
>
Animated
transitions ease between dashboard sections and reinforce context shifts
.
</Text
>
</Block
>
)
}
,
{
key
:
'analytics'
,
label
:
'Analytics'
,
content
:
(
<Block
>
<Text
weight
=
"medium"
>
Analytics
overview
</Text
>
<Text
color
=
"muted"
>
Surface
key charts and
KPIs
while
the motion guides attention to
new
content
.
</Text
>
</Block
>
)
}
,
{
key
:
'settings'
,
label
:
'Settings'
,
content
:
(
<Block
>
<Text
weight
=
"medium"
>
Account
settings
</Text
>
<Text
color
=
"muted"
>
Manage
notifications
,
billing
,
and other preferences without abrupt content swaps
.
</Text
>
</Block
>
)
}
]
;
export
function
Demo
(
)
{
return
(
<Block
>
<Tabs
variant
=
"line"
animated
animationDuration
=
{
250
}
items
=
{
ITEMS
}
/>
<Text
variant
=
"small"
color
=
"muted"
>
Enable
`animated`
to add motion and use
`animationDuration`
to moderate the easing speed
.
</Text
>
</Block
>
)
;
}
Disabled tabs
onDisabledTabPress for auditing or messaging.Loading demo…
import
{
useState
}
from
'react'
;
import
{
Block
,
Tabs
,
Text
,
useTheme
}
from
'@platform-blocks/react-ui-library'
;
const
PANELS
=
[
{
key
:
'overview'
,
label
:
'Overview'
,
body
:
'High-level snapshot of product activity and health.'
}
,
{
key
:
'analytics'
,
label
:
'Analytics'
,
body
:
'Dive into usage metrics, adoption trends, and retention.'
}
,
{
key
:
'billing'
,
label
:
'Billing'
,
body
:
'Billing is temporarily disabled while invoices reconcile.'
,
disabled
:
true
}
,
{
key
:
'settings'
,
label
:
'Settings'
,
body
:
'Manage workspace preferences and security controls.'
}
]
;
export
function
Demo
(
)
{
const
theme
=
useTheme
(
)
;
const
[
activeTab
,
setActiveTab
]
=
useState
(
'overview'
)
;
const
[
lastAttempt
,
setLastAttempt
]
=
useState
<
string
|
null
>
(
null
)
;
return
(
<Block
>
<Tabs
activeTab
=
{
activeTab
}
onTabChange
=
{
(
tab
)
=>
{
setActiveTab
(
tab
)
;
setLastAttempt
(
null
)
;
}
}
onDisabledTabPress
=
{
(
key
)
=>
{
const
label
=
PANELS
.
find
(
(
panel
)
=>
panel
.
key
===
key
)
?
.
label
??
key
;
setLastAttempt
(
`${label} is currently disabled.`
)
;
}
}
variant
=
"line"
size
=
"md"
items
=
{
PANELS
.
map
(
(
{
body
,
.
.
.
panel
}
)
=>
(
{
.
.
.
panel
,
content
:
(
<Block
bg
=
{
theme
.
backgrounds
.
surface
}
borderColor
=
{
theme
.
backgrounds
.
border
}
radius
=
"lg"
p
=
"md"
>
<Text
>
{
body
}
</Text
>
</Block
>
)
}
)
)
}
/>
<Text
variant
=
"small"
color
=
"muted"
>
Disable
sensitive tabs
while
keeping the
`onDisabledTabPress`
callback to track attempted access
.
{
lastAttempt
?
` ${lastAttempt}`
:
''
}
</Text
>
</Block
>
)
;
}
Controlled state
onTabChange.Loading demo…
import
{
useState
}
from
'react'
;
import
{
Block
,
Tabs
,
Text
}
from
'@platform-blocks/react-ui-library'
;
const
ITEMS
=
[
{
key
:
'dashboard'
,
label
:
'🏠 Dashboard'
,
content
:
<Text
>
Monitor
analytics across teams and products
.
</Text
>
}
,
{
key
:
'settings'
,
label
:
'⚙️ Settings'
,
content
:
<Text
>
Configure
notifications
,
permissions
,
and integrations
.
</Text
>
}
,
{
key
:
'profile'
,
label
:
'👤 Profile'
,
content
:
<Text
>
Review
contact details
,
roles
,
and security options
.
</Text
>
}
]
;
export
function
Demo
(
)
{
const
[
activeTab
,
setActiveTab
]
=
useState
(
'dashboard'
)
;
const
activeLabel
=
ITEMS
.
find
(
(
item
)
=>
item
.
key
===
activeTab
)
?
.
label
??
activeTab
;
return
(
<Block
>
<Tabs
activeTab
=
{
activeTab
}
onTabChange
=
{
setActiveTab
}
items
=
{
ITEMS
}
/>
<Text
variant
=
"small"
color
=
"muted"
>
Active
tab
:
{
activeLabel
}
</Text
>
</Block
>
)
;
}
Location
start and end to position triggers above or below the associated content.Loading demo…
import
{
Block
,
Tabs
,
Text
}
from
'@platform-blocks/react-ui-library'
;
type
Location
=
'start'
|
'end'
;
const
SECTIONS
:
Array
<
{
label
:
string
;
location
:
Location
;
helper
:
string
}
>
=
[
{
label
:
'Tabs above content (start)'
,
location
:
'start'
,
helper
:
'Default placement keeps triggers directly above the active view.'
}
,
{
label
:
'Tabs below content (end)'
,
location
:
'end'
,
helper
:
'Use end placement when the content should lead and controls follow.'
}
]
;
const
buildItems
=
(
location
:
Location
)
=>
[
{
key
:
'home'
,
label
:
'Home'
,
content
:
(
<Text
>
Home
content rendered with tabs
{
location
===
'start'
?
'above'
:
'below'
}
the panel
.
</Text
>
)
}
,
{
key
:
'settings'
,
label
:
'Settings'
,
content
:
(
<Text
>
Update
configurations
while
keeping the tabs
{
location
===
'start'
?
'up top'
:
'after the details'
}
.
</Text
>
)
}
,
{
key
:
'profile'
,
label
:
'Profile'
,
content
:
(
<Text
>
Profile
information with navigation
{
location
===
'start'
?
'leading into'
:
'following'
}
the content
.
</Text
>
)
}
]
;
export
function
Demo
(
)
{
return
(
<Block
>
{
SECTIONS
.
map
(
(
{
label
,
location
,
helper
}
)
=>
(
<Block
key
=
{
location
}
>
<Text
weight
=
"medium"
>
{
label
}
</Text
>
<Tabs
variant
=
"line"
location
=
{
location
}
items
=
{
buildItems
(
location
)
}
/>
<Text
variant
=
"small"
color
=
"muted"
>
{
helper
}
</Text
>
</Block
>
)
)
}
</Block
>
)
;
}
Navigation only
navigationOnly tabs that render the triggers separately while a custom container handles the content region.Loading demo…
import
{
useMemo
,
useState
}
from
'react'
;
import
{
Block
,
Tabs
,
Text
,
useTheme
}
from
'@platform-blocks/react-ui-library'
;
const
NAV_ITEMS
=
[
{
key
:
'home'
,
label
:
'Home'
}
,
{
key
:
'products'
,
label
:
'Products'
}
,
{
key
:
'about'
,
label
:
'About'
}
,
{
key
:
'contact'
,
label
:
'Contact'
}
]
as
const
;
const
CONTENT_COPY
:
Record
<
typeof NAV_ITEMS
[
number
]
[
'key'
]
,
string
>
=
{
home
:
'Welcome back! Navigation only mode keeps the tab strip separated from the view.'
,
products
:
'Highlight product cards, filters, or category grids below the navigation.'
,
about
:
'Share the company story, values, and milestones alongside persistent tabs.'
,
contact
:
'Surface support channels and locations while the navigation stays fixed.'
}
;
export
function
Demo
(
)
{
const
theme
=
useTheme
(
)
;
const
[
activeTab
,
setActiveTab
]
=
useState
<
typeof NAV_ITEMS
[
number
]
[
'key'
]
>
(
'home'
)
;
const
items
=
useMemo
(
(
)
=>
NAV_ITEMS
.
map
(
(
{
key
,
label
}
)
=>
(
{
key
,
label
,
content
:
null
}
)
)
,
[
]
)
;
return
(
<Block
>
<Tabs
items
=
{
items
}
activeTab
=
{
activeTab
}
onTabChange
=
{
(
tabKey
)
=>
setActiveTab
(
tabKey as typeof NAV_ITEMS
[
number
]
[
'key'
]
)
}
variant
=
"chip"
navigationOnly
/>
<Block
bg
=
{
theme
.
backgrounds
.
surface
}
borderColor
=
{
theme
.
backgrounds
.
border
}
radius
=
"lg"
p
=
"lg"
>
<Text
>
{
CONTENT_COPY
[
activeTab
]
}
</Text
>
</Block
>
<Text
variant
=
"small"
color
=
"muted"
>
`navigationOnly`
renders just the triggers so you can manage layout and transitions
for
the content area yourself
.
</Text
>
</Block
>
)
;
}
Orientation
Loading demo…
import
{
Block
,
Tabs
,
Text
}
from
'@platform-blocks/react-ui-library'
;
type
Orientation
=
'horizontal'
|
'vertical'
;
const
buildItems
=
(
orientation
:
Orientation
)
=>
[
{
key
:
'general'
,
label
:
'General'
,
content
:
(
<Text
>
Broad
overview content rendered with a
{
orientation
}
tab list
.
</Text
>
)
}
,
{
key
:
'security'
,
label
:
'Security'
,
content
:
(
<Text
>
Security
controls and permissions laid out
for
a
{
orientation
}
arrangement
.
</Text
>
)
}
,
{
key
:
'notifications'
,
label
:
'Notifications'
,
content
:
(
<Text
>
Configure
alerts and digests with triggers stacked
{
orientation
===
'vertical'
?
'in a column'
:
'in a row'
}
.
</Text
>
)
}
]
;
const
ORIENTATIONS
:
Array
<
{
label
:
string
;
orientation
:
Orientation
;
helper
:
string
}
>
=
[
{
label
:
'Horizontal orientation'
,
orientation
:
'horizontal'
,
helper
:
'Default layout presents tabs in a row for top-level navigation.'
}
,
{
label
:
'Vertical orientation'
,
orientation
:
'vertical'
,
helper
:
'Vertical tab lists are ideal for settings sidebars and dense menus.'
}
]
;
export
function
Demo
(
)
{
return
(
<Block
>
{
ORIENTATIONS
.
map
(
(
{
label
,
orientation
,
helper
}
)
=>
(
<Block
key
=
{
orientation
}
>
<Text
weight
=
"medium"
>
{
label
}
</Text
>
<Tabs
orientation
=
{
orientation
}
items
=
{
buildItems
(
orientation
)
}
/>
<Text
variant
=
"small"
color
=
"muted"
>
{
helper
}
</Text
>
</Block
>
)
)
}
</Block
>
)
;
}
Variants
line, chip, and folder visual variants to match tabs with different design aesthetics.Loading demo…
import
type
{
ComponentProps
}
from
'react'
;
import
{
Block
,
Tabs
,
Text
}
from
'@platform-blocks/react-ui-library'
;
type
Variant
=
NonNullable
<ComponentProps
<
typeof
Tabs
>
[
'variant'
]
>
;
const
VARIANT_DEMOS
:
Array
<
{
heading
:
string
;
variant
:
Variant
;
helper
:
string
;
items
:
Array
<
{
key
:
string
;
label
:
string
;
description
:
string
}
>
;
}
>
=
[
{
heading
:
'Line variant'
,
variant
:
'line'
,
helper
:
'Minimal underline style aligns with primary navigation patterns.'
,
items
:
[
{
key
:
'documents'
,
label
:
'Documents'
,
description
:
'Underline emphasizes the active document view.'
}
,
{
key
:
'images'
,
label
:
'Images'
,
description
:
'Great for media browsers with subtle hierarchy.'
}
,
{
key
:
'videos'
,
label
:
'Videos'
,
description
:
'Pairs well with narrow tab bars and dense content.'
}
]
}
,
{
heading
:
'Chip variant'
,
variant
:
'chip'
,
helper
:
'Rounded pills feel tactile and work well for filters or secondary navigation.'
,
items
:
[
{
key
:
'overview'
,
label
:
'Overview'
,
description
:
'Pills highlight the selected state with a filled background.'
}
,
{
key
:
'details'
,
label
:
'Details'
,
description
:
'Suitable for segmented controls or inline filters.'
}
,
{
key
:
'settings'
,
label
:
'Settings'
,
description
:
'Works nicely for preference toggles.'
}
]
}
,
{
heading
:
'Folder variant'
,
variant
:
'folder'
,
helper
:
'Raised folder styling evokes classic tabbed interfaces.'
,
items
:
[
{
key
:
'overview-folder'
,
label
:
'Overview'
,
description
:
'Ideal for dashboards with nested content.'
}
,
{
key
:
'details-folder'
,
label
:
'Details'
,
description
:
'Tabs feel like document dividers for data-heavy views.'
}
,
{
key
:
'settings-folder'
,
label
:
'Settings'
,
description
:
'Keeps controls organized in admin interfaces.'
}
]
}
]
;
export
function
Demo
(
)
{
return
(
<Block
>
{
VARIANT_DEMOS
.
map
(
(
{
heading
,
variant
,
helper
,
items
}
)
=>
(
<Block
key
=
{
variant
}
>
<Text
weight
=
"medium"
>
{
heading
}
</Text
>
<Tabs
variant
=
{
variant
}
items
=
{
items
.
map
(
(
{
description
,
.
.
.
item
}
)
=>
(
{
.
.
.
item
,
content
:
<Text
>
{
description
}
</Text
>
}
)
)
}
/>
<Text
variant
=
"small"
color
=
"muted"
>
{
helper
}
</Text
>
</Block
>
)
)
}
</Block
>
)
;
}
</>
react-ui-library
100+ accessible, themeable components that work seamlessly across iOS, Android, and Web.
A Platform Blocks product.
Quick Links
Resources