Base Usage
Tabs
provide a composable, lightweight tabbed interface inspired by Shadcn, adjusted for UI Toolkit.
import { h } from "preact"
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/comps/echo/tabs"
export function TabsDemo() {
return <Tabs defaultValue="mage" class="w-[340px]">
<TabsList>
<TabsTrigger value="mage">Mage</TabsTrigger>
<TabsTrigger value="warrior">Warrior</TabsTrigger>
</TabsList>
<TabsContent value="mage">Glass cannon with a god complex, top of the damage chart and bottom of the armor list.</TabsContent>
<TabsContent value="warrior">First in the fight, last to fall, and definitely the reason the healer has trust issues.</TabsContent>
</Tabs>
}
The component supports both controlled (value
+ onValueChange
) and uncontrolled (defaultValue
) patterns.
import { h } from "preact"
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/comps/echo/tabs"
import { useState } from "onejs-preact/hooks"
export function TabsControlledDemo() {
const [tab, setTab] = useState("mage")
function handleTabChange(value: string) {
console.log("Tab changed to:", value)
setTab(value)
}
return <Tabs value={tab} onValueChange={handleTabChange} class="w-[340px]">
<TabsList>
<TabsTrigger value="mage">Mage</TabsTrigger>
<TabsTrigger value="warrior">Warrior</TabsTrigger>
</TabsList>
<TabsContent value="mage">Glass cannon with a god complex, top of the damage chart and bottom of the armor list.</TabsContent>
<TabsContent value="warrior">First in the fight, last to fall, and definitely the reason the healer has trust issues.</TabsContent>
</Tabs>
}
KeepAlive vs Unmounted
By default, inactive panels are unmounted to save memory.
Set keepAlive
if you need background state to persist (e.g. videos, timers).
<Tabs.Content value="panel-a" keepAlive>
{/* stays in the DOM even when not active */}
</Tabs.Content>
Vertical Orientation
Tabs.List
accepts orientation="vertical"
for sidebar layouts.
import { h } from "preact"
import { Tabs } from "@/comps/echo/tabs"
export const TabsVerticalDemo = () => (
<Tabs defaultValue="one" class="flex-row w-[380px]">
<Tabs.List orientation="vertical" class="mr-4">
<Tabs.Trigger value="one">One</Tabs.Trigger>
<Tabs.Trigger value="two">Two</Tabs.Trigger>
<Tabs.Trigger value="three">Three</Tabs.Trigger>
</Tabs.List>
<div class="flex-1">
<Tabs.Content value="one">🍀 Lucky</Tabs.Content>
<Tabs.Content value="two">🔥 Spicy</Tabs.Content>
<Tabs.Content value="three">🧊 Chilly</Tabs.Content>
</div>
</Tabs>
)
Styling Hooks
- Trigger – use
class
for default look andactiveClass
for the selected state. - Content – regular Tailwind/inline styles.
- Root & List – behave like normal
div
containers.