rapidUI

Sub Tabs

Secondary, lower-emphasis tabs for nested navigation.

Installation

pnpm dlx shadcn@latest add https://rdf.dev.360online.com/r/sub-tabs.json

Installs to @/components/ui/sub-tabs.

Manual installation

Install the dependencies:

pnpm add @base-ui/react@^1.8.0

Copy the source into your project:

components/ui/sub-tabs.tsx
'use client'

import { Tabs as TabsPrimitive } from '@base-ui/react/tabs'
import { cn } from '@/lib/utils'

function SubTabs({ className, ...props }: TabsPrimitive.Root.Props) {
	return <TabsPrimitive.Root data-slot="sub-tabs" className={cn('flex flex-col gap-2', className)} {...props} />
}

function SubTabsList({ className, ...props }: TabsPrimitive.List.Props) {
	return (
		<TabsPrimitive.List
			data-slot="sub-tabs-list"
			className={cn(
				'inline-flex h-10 w-fit items-center gap-2 rounded-lg border border-border bg-muted p-[3px] text-foreground',
				className,
			)}
			{...props}
		/>
	)
}

function SubTabsTrigger({ className, ...props }: TabsPrimitive.Tab.Props) {
	return (
		<TabsPrimitive.Tab
			data-slot="sub-tabs-trigger"
			className={cn(
				"inline-flex h-full items-center justify-center gap-1.5 rounded-md border border-transparent px-2 text-sm font-normal whitespace-nowrap text-muted-foreground transition-all outline-none cursor-pointer hover:bg-background/50 hover:text-primary focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-active:bg-background data-active:font-semibold data-active:text-primary data-active:shadow-sm data-active:hover:bg-secondary [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
				className,
			)}
			{...props}
		/>
	)
}

function SubTabsContent({ className, ...props }: TabsPrimitive.Panel.Props) {
	return (
		<TabsPrimitive.Panel
			data-slot="sub-tabs-content"
			className={cn('flex-1 text-sm outline-none focus-visible:ring-3 focus-visible:ring-ring', className)}
			{...props}
		/>
	)
}

export { SubTabs, SubTabsList, SubTabsTrigger, SubTabsContent }

A secondary, lower-emphasis tab bar for switching views inside a page section, typically nested below the main Tabs. Built on the Base UI Tabs primitive (from the @base-ui/react package). The list is an outlined h-8 bg-muted bar and the active trigger gets a bg-background fill with text-primary.

Features

  • Built on the Base UI Tabs primitive
  • Same sizing and focus ring as the other controls (h-8, rounded-lg, ring-3 ring-ring/50)
  • Keyboard navigation and screen reader support
  • Controlled and uncontrolled modes
  • Icons in triggers (sized to size-4 automatically)
  • Disabled triggers

Usage

import { SubTabs, SubTabsContent, SubTabsList, SubTabsTrigger } from '@/components/ui/sub-tabs'

export function SubTabsDemo() {
  return (
    <SubTabs defaultValue="incoming" className="w-full max-w-md">
      <SubTabsList>
        <SubTabsTrigger value="incoming">Incoming</SubTabsTrigger>
        <SubTabsTrigger value="outgoing">Outgoing</SubTabsTrigger>
      </SubTabsList>
      <SubTabsContent value="incoming">Incoming documents…</SubTabsContent>
      <SubTabsContent value="outgoing">Outgoing documents…</SubTabsContent>
    </SubTabs>
  )
}

Examples

Default

The document section of a building permit case, split into incoming, outgoing and internal notes.

With Icons

Icons placed before the label in each trigger, inside a resident's "My pages" card.

Nested in Tabs

The typical placement: the main Tabs switch between sections of a case, and SubTabs switch views inside one of them.

Controlled

Controlled via value / onValueChange, so other parts of the screen can read and change the active view.

Disabled State

A trigger with disabled is dimmed and can't be selected – here the decision stays locked until the assessment is done.

Full Width

Give the list w-full and each trigger flex-1 to stretch the bar, for example for opening hours.

Anatomy

<SubTabs>
  <SubTabsList>
    <SubTabsTrigger />
  </SubTabsList>
  <SubTabsContent />
</SubTabs>
  • SubTabs - root (a flex column with gap-2), accepts all Base UI Tabs Root props
  • SubTabsList - the outlined trigger bar
  • SubTabsTrigger - a single tab; active state uses data-active
  • SubTabsContent - the panel for a tab

Accessibility

Follows the WAI-ARIA tabs pattern:

  • Proper roles (tablist, tab, tabpanel)
  • Arrow keys move between tabs, Home / End jump to the first / last tab
  • Tab moves focus into the active panel
  • Visible focus ring on triggers and panels

Customization

<SubTabs defaultValue="tab1">
  <SubTabsList className="w-full">
    <SubTabsTrigger value="tab1" className="flex-1">
      <Settings2Icon />
      Tab 1
    </SubTabsTrigger>
  </SubTabsList>
  <SubTabsContent value="tab1" className="rounded-lg border p-4">
    Content 1
  </SubTabsContent>
</SubTabs>
  • Tabs - For the primary tab bar
  • Toggle Group - For switching a setting rather than a view