Tabbed View
A robust, config-driven container component that organizes content into distinct panels. Supports lazy evaluation, state preservation, icons, and disabled states.
Interactive Demo
Features multiple tabs including icons, a disabled 'Admin Panel' tab, and a lazy-rendered 'Settings' tab.
Installation
Install the TabbedView component.
pnpm dlx shadcn@latest add https://micto-ui-kit.misangono.net/r/micto/tabbed-view.jsonBasic Usage
Provide an array of tabs with labels, icons, and content.
import { useState } from "react";
import { TabbedView } from "@/components/micto/tabbed-view";
import { UserIcon, SettingsIcon } from "lucide-react";
export default function BasicTabs() {
const [activeTab, setActiveTab] = useState("profile");
return (
<TabbedView
tabs={[
{
tabValue: "profile",
label: "Profile",
icon: UserIcon,
content: <div>Profile Content Here</div>,
},
{
tabValue: "settings",
label: "Settings",
icon: SettingsIcon,
content: <div>Settings Content Here</div>,
},
]}
value={activeTab}
onValueChange={setActiveTab}
/>
);
}Lazy Evaluation (Thunk Pattern)
By default, passing a React Element (e.g. <HeavyComponent />) evaluates it immediately. To strictly defer instantiation and execution until the tab is opened, pass a function that returns the component.
import { useState } from "react";
import { TabbedView } from "@/components/micto/tabbed-view";
import { HeavyChartComponent } from "./heavy-chart";
export default function LazyTabs() {
return (
<TabbedView
tabs={[
{
tabValue: "fast",
label: "Fast Tab",
content: <div>Immediate rendering</div>,
},
{
tabValue: "heavy",
label: "Heavy Tab",
// Pass a function to lazy-evaluate the component!
// HeavyChartComponent won't be instantiated or mounted until this tab is clicked.
content: () => <HeavyChartComponent />,
},
]}
/>
);
}State Preservation (keepMounted)
Radix UI normally unmounts inactive tabs to save memory, which destroys component state (like form inputs). Use keepMounted={true} to hide them via CSS instead, preserving all state.
import { useState } from "react";
import { TabbedView } from "@/components/micto/tabbed-view";
export default function StatefulTabs() {
return (
<TabbedView
// Set to true to keep hidden tabs mounted in the DOM.
// This preserves state (e.g., input field values) when switching tabs.
keepMounted={true}
tabs={[
{
tabValue: "form-step-1",
label: "Step 1",
content: <input type="text" placeholder="Type here and switch tabs..." />,
},
{
tabValue: "form-step-2",
label: "Step 2",
content: <div>Step 2 details</div>,
},
]}
/>
);
}TabbedView Props
Root component configuration.
| Prop | Type | Default | Description |
|---|---|---|---|
| tabs | TabItem[] | Array of tab configurations. | |
| defaultValue | string | tabs[0].tabValue | The value of the tab that should be active when initially rendered. |
| value | string | undefined | The controlled value of the active tab. |
| onValueChange | (value: string) => void | undefined | Event handler called when the value changes. |
| keepMounted | boolean | false | If true, keeps hidden tabs in the DOM so they don't lose state (like form inputs). |
| className | string | undefined | Optional CSS classes to apply to the root Tabs component. |
TabItem Interface
Configuration object for individual tabs.
| Prop | Type | Default | Description |
|---|---|---|---|
| tabValue | string | Unique identifier for the tab. | |
| label | ReactNode | The label displayed on the tab trigger. | |
| content | ReactNode | (() => ReactNode) | The content to display when the tab is active. Pass a function for strict lazy evaluation. | |
| icon | ElementType | undefined | Optional icon component (like Lucide icons) to display next to the label. |
| disabled | boolean | false | If true, prevents the user from selecting the tab. |
| className | string | undefined | Optional CSS classes to apply specifically to this tab's trigger. |