htsx

Tab

Tab component for Hono JSX

Examples

// Simple
<Tab
  defaultValue="overview"
  items={[
    {
      value: "overview",
      title: "Overview",
      content: "First panel. Visible by default since defaultValue is set.",
    },
    // more items
  ]}
/>
 
// Rich (Custome style, JSX for title and content)
<Tab
  defaultValue="two"
  class="rounded-md border border-border bg-card text-card-foreground"
  items={[
    {
      value: "one",
      title: (
        <span class="flex items-center gap-2">
          <Sun />
          One
        </span>
      ),
      content: (
        <ul>
          <li>
            <code>content</code> accepts JSX.
          </li>
          <li>Icons, lists, images, buttons, etc.</li>
        </ul>
      ),
    },
    // more items
  ]}
/>

Code

ui/tab.tsx
import type { Child, JSX } from "hono/jsx";
import { c } from "./c";
 
export function Tab({
  id,
  defaultValue,
  items,
  class: custom,
  ...props
}: JSX.IntrinsicElements["div"] & {
  defaultValue?: string;
  items: { value: string; title: Child; content: Child }[];
}) {
  const groupName = id ?? crypto.randomUUID();
  const activeValue = items.find((item) => item.value === defaultValue)?.value ?? items[0]?.value;
  return (
    <div id={id} class={c("flex flex-wrap", custom)} {...props}>
      {items.map((item) => (
        <div key={item.value} class="group/tab contents">
          <input
            type="radio"
            id={`${groupName}-${item.value}`}
            name={groupName}
            checked={item.value === activeValue || undefined}
            class="sr-only"
          />
          <label
            for={`${groupName}-${item.value}`}
            class="-mb-px cursor-pointer border-b-2 border-transparent px-4 py-2 font-medium text-muted-foreground select-none group-has-checked/tab:border-primary group-has-checked/tab:text-foreground hover:text-foreground"
          >
            {item.title}
          </label>
          <div class="order-last hidden w-full border-t border-border p-4 group-has-checked/tab:block">
            {item.content}
          </div>
        </div>
      ))}
    </div>
  );
}