Problem Statement
Build a tabs component. It takes a list of tabs, shows one tab's content at a time, and calls back to the parent with the index whenever the user picks a tab.
Functional Requirements
- Tab list: Render a button for each item in
data, using itslabel. - Active tab: The first tab is active on load. The active button gets
data-active="true"for styling. - Switch: Clicking a button makes that tab active and shows its
contentin the panel below. - Callback: Call
onTabChange(index)with the clicked tab's index. The demo parent logs it.
Technical Constraints
- The active index lives in the
Tabscomponent's own state. The parent is told about changes but does not set the active tab. - An empty
dataarray or a missingonTabChangemust not crash the component.
Component Structure
Props
{
data: Array<{
label: string, // Text shown in tab button
content: ReactNode // Content to display when tab is active
}>,
onTabChange: (index: number) => void // Callback fired when tab changes
}Usage Example
const tabsData = [
{
label: "Profile",
content: <div>Profile Info Content</div>,
},
{
label: "Dashboard",
content: <div>Dashboard Content</div>,
},
];
const onTabChangeHandler = (index) => {
console.log("Current Tab Index:", index);
};
<Tabs data={tabsData} onTabChange={onTabChangeHandler} />;State
currentTabIdx: number; // Tracks currently active tab indexImplementation Details
1. Tab Container
- Uses flexbox for horizontal tab alignment
- Implements gap spacing between tabs
- Maintains visual connection with content panel
2. Tab Buttons
- Interactive buttons with hover and active states
- Uses data-active attribute for styling active tab
- Implements onClick handler for tab switching and callback
- Smooth transition effects
3. Content Panel
- Displays content based on active tab index
Common Interview Questions
- How would you handle keyboard navigation? Put an
onKeyDownon the tab list. Left and Right arrows move to the previous or next tab and wrap at the ends, Home and End jump to the first and last. Only the active tab hastabIndex={0}, the rest get-1, so Tab moves focus into the panel instead of through every tab. - How would you make this component more accessible? Use
role="tablist"on the container,role="tab"witharia-selectedandaria-controlson each button, androle="tabpanel"witharia-labelledbyon the content. Give each tab and panel a stable id, for example fromuseId. - How would you implement lazy loading of tab content? Render only the active panel, as this component already does. For heavy panels, wrap them in
React.lazyandSuspense, and keep a set of visited tabs if a panel should keep its state after you leave it. - How would you handle tab overflow on small screens? Let the tab row scroll horizontally with
overflow-x: auto, and scroll the active tab into view when it changes. Another option is to move tabs that do not fit into a "More" menu. - How would you persist the active tab across page refreshes? Keep the index in the URL (
?tab=2or a hash) so it survives a refresh and can be shared.localStoragealso works but is not shareable. Clamp a saved index that is out of range. - How would you implement controlled vs uncontrolled versions of this component? Accept an optional
activeIndexprop. When it is passed, render from it and only callonTabChange. When it is not, keep the index in local state as now, with adefaultIndexprop for the starting tab.
Potential Enhancements
- Add keyboard navigation (arrow keys)
- Implement ARIA attributes for accessibility
- Add animation for content transitions
- Add support for disabled tabs
- Implement vertical tab orientation option
- Add support for dynamic tab addition/removal
- Add controlled mode with external state management
Common Edge Cases to Handle
- Empty tab data array
- Very long tab labels
- Dynamic content height
- Missing onTabChange callback
- Multiple rapid tab changes