Overview
The Tabs plugin allows you to create tabbed content sections, perfect for organizing related information in a compact, user-friendly format. Users can switch between tabs to view different content.
Installation
Basic Usage
import { Tabs } from '@yoopta/tabs' ;
const plugins = [
Tabs ,
// ... other plugins
];
< YooptaEditor
editor = { editor }
plugins = { plugins }
/>
Features
Multiple Tabs : Create unlimited tabs
Active Tab State : Track which tab is currently active
Easy Navigation : Switch between tabs effortlessly
Keyboard Controls : Add tabs with Enter key
Custom Content : Each tab can contain any content
Structure
The Tabs plugin consists of nested elements:
tabs-container (props: activeTabId)
├── tabs-list
│ └── tabs-item-heading (multiple tabs)
└── tabs-item-content (props: referenceId)
Configuration
import { Tabs } from '@yoopta/tabs' ;
const plugins = [
Tabs ,
];
Options
Description shown in menus
Element Props
tabs-container
ID of the currently active tab
tabs-item-content
ID linking content to its tab heading
Commands
addTabItem
Add a new tab to the tabs container.
import { TabsCommands } from '@yoopta/tabs' ;
TabsCommands . addTabItem ( editor , blockId , { at: selection });
removeTabItem
Remove a specific tab (to be implemented).
TabsCommands . removeTabItem ( editor , blockId , tabId );
Keyboard Behavior
Enter on Tab Heading : Creates a new tab
Enter in Tab Content : Inserts newline within content
Backspace : Prevents deletion at content start
Cmd/Ctrl+A in Content : Selects entire content
Initial Structure
When created, the plugin initializes with one tab:
{
'tabs-container' : {
activeTabId: 'generated-id' ,
},
tabs : [
{
heading: 'Tab 1' ,
content: 'Tab 1 content' ,
referenceId: 'generated-id' ,
},
],
}
Custom Rendering
import { Tabs } from '@yoopta/tabs' ;
import { useState } from 'react' ;
const CustomTabs = Tabs . extend ({
elements: {
'tabs-container' : {
render : ( props ) => {
const { activeTabId } = props . element . props ;
return (
< div className = "tabs-container" data-active = { activeTabId } { ... props . attributes } >
{ props . children }
</ div >
);
},
},
'tabs-list' : {
render : ( props ) => (
< div className = "tabs-header" role = "tablist" { ... props . attributes } >
{ props . children }
</ div >
),
},
'tabs-item-heading' : {
render : ( props ) => {
const isActive = checkIfActive ( props . element . id );
return (
< button
className = { `tab-button ${ isActive ? 'active' : '' } ` }
role = "tab"
aria-selected = { isActive }
{ ... props . attributes }
>
{ props . children }
</ button >
);
},
},
'tabs-item-content' : {
render : ( props ) => {
const { referenceId } = props . element . props ;
const isActive = checkIfActive ( referenceId );
return (
< div
className = "tab-panel"
role = "tabpanel"
hidden = { ! isActive }
{ ... props . attributes }
>
{ props . children }
</ div >
);
},
},
},
});
Styling Examples
Basic CSS
.tabs-container {
border : 1 px solid #e5e7eb ;
border-radius : 0.5 rem ;
overflow : hidden ;
}
.tabs-header {
display : flex ;
border-bottom : 2 px solid #e5e7eb ;
background : #f9fafb ;
}
.tab-button {
padding : 0.75 rem 1.5 rem ;
border : none ;
background : transparent ;
cursor : pointer ;
font-weight : 500 ;
color : #6b7280 ;
border-bottom : 2 px solid transparent ;
margin-bottom : -2 px ;
}
.tab-button.active {
color : #3b82f6 ;
border-bottom-color : #3b82f6 ;
}
.tab-panel {
padding : 1.5 rem ;
}
.tab-panel [ hidden ] {
display : none ;
}
With Icons
const IconTabs = Tabs . extend ({
elements: {
'tabs-item-heading' : {
render : ( props ) => {
const icon = getTabIcon ( props . element );
return (
< button className = "tab-button" { ... props . attributes } >
< span className = "tab-icon" > { icon } </ span >
< span className = "tab-text" > { props . children } </ span >
</ button >
);
},
},
},
});
Use Cases
Documentation Different programming language examples
Product Features Showcase different product features
FAQ Sections Categorized frequently asked questions
Pricing Plans Compare different pricing tiers
Best Practices
Use concise, descriptive labels for tabs
Keep number of tabs reasonable (3-7) for better UX
Maintain similar content structure across tabs
Ensure tabs are keyboard navigable
Consider mobile layouts with scrollable tabs
Advanced Patterns
With Tab Icons
const IconTabs = Tabs . extend ({
elements: {
'tabs-item-heading' : {
props: {
icon: null ,
},
render : ( props ) => {
const { icon } = props . element . props ;
return (
< button { ... props . attributes } >
{ icon && < Icon name = { icon } /> }
{ props . children }
</ button >
);
},
},
},
});
With Vertical Layout
const VerticalTabs = Tabs . extend ({
elements: {
'tabs-container' : {
render : ( props ) => (
< div className = "tabs-vertical" { ... props . attributes } >
{ props . children }
</ div >
),
},
},
});
.tabs-vertical {
display : grid ;
grid-template-columns : 200 px 1 fr ;
}
.tabs-vertical .tabs-list {
flex-direction : column ;
}
With Lazy Loading
const LazyTabs = Tabs . extend ({
elements: {
'tabs-item-content' : {
render : ( props ) => {
const { referenceId } = props . element . props ;
const isActive = checkIfActive ( referenceId );
const [ loaded , setLoaded ] = useState ( isActive );
useEffect (() => {
if ( isActive && ! loaded ) {
setLoaded ( true );
}
}, [ isActive ]);
return (
< div hidden = { ! isActive } { ... props . attributes } >
{ loaded ? props . children : 'Loading...' }
</ div >
);
},
},
},
});
Accessibility
The Tabs plugin should follow WAI-ARIA tabs pattern:
Tab List : role="tablist"
Tab : role="tab", aria-selected
Tab Panel : role="tabpanel", hidden
const AccessibleTabs = Tabs . extend ({
elements: {
'tabs-list' : {
render : ( props ) => (
< div role = "tablist" aria-label = "Content tabs" { ... props . attributes } >
{ props . children }
</ div >
),
},
'tabs-item-heading' : {
render : ( props ) => {
const isActive = checkIfActive ( props . element . id );
return (
< button
role = "tab"
aria-selected = { isActive }
aria-controls = { `panel- ${ props . element . id } ` }
id = { `tab- ${ props . element . id } ` }
{ ... props . attributes }
>
{ props . children }
</ button >
);
},
},
'tabs-item-content' : {
render : ( props ) => {
const { referenceId } = props . element . props ;
const isActive = checkIfActive ( referenceId );
return (
< div
role = "tabpanel"
id = { `panel- ${ referenceId } ` }
aria-labelledby = { `tab- ${ referenceId } ` }
hidden = { ! isActive }
{ ... props . attributes }
>
{ props . children }
</ div >
);
},
},
},
});