Skip to Content

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 and activeClass for the selected state.
  • Content – regular Tailwind/inline styles.
  • Root & List – behave like normal div containers.