Tabs
Alterna entre painéis de conteúdo relacionados na mesma página, sem recarregar.
Maria Clara Fernandes
(19) 99876-5432 · cadastrada em 12/03/2025
'use client';
import { Tab, TabList, TabPanel, Tabs } from '@/components/ui/tabs';
export default function TabsDemo() {
return (
<Tabs className="w-full max-w-md">
<TabList aria-label="Dados do paciente">
<Tab id="dados">Dados pessoais</Tab>
<Tab id="consultas">Consultas</Tab>
<Tab id="financeiro">Financeiro</Tab>
</TabList>
<TabPanel id="dados" className="rounded-lg border p-4">
<p className="font-medium">Maria Clara Fernandes</p>
<p className="text-muted-foreground">(19) 99876-5432 · cadastrada em 12/03/2025</p>
</TabPanel>
<TabPanel id="consultas" className="rounded-lg border p-4">
<p className="font-medium">Próxima consulta</p>
<p className="text-muted-foreground">Limpeza e avaliação — 14/10, às 09h30 com Dra. Renata.</p>
</TabPanel>
<TabPanel id="financeiro" className="rounded-lg border p-4">
<p className="font-medium">Sem pendências</p>
<p className="text-muted-foreground">Última fatura paga via Pix em 02/09.</p>
</TabPanel>
</Tabs>
);
}Instalação
npx shadcn@latest add @lenstech/tabsUso
import { Tab, TabList, TabPanel, Tabs } from "@/components/ui/tabs"<Tabs>
<TabList aria-label="Dados do paciente">
<Tab id="dados">Dados pessoais</Tab>
<Tab id="consultas">Consultas</Tab>
</TabList>
<TabPanel id="dados">Nome, CPF e telefone.</TabPanel>
<TabPanel id="consultas">Histórico de consultas.</TabPanel>
</Tabs>O id liga a aba ao painel
Cada Tab e seu TabPanel precisam do mesmo id. Dê também um aria-label à TabList
para descrever o grupo de abas. As setas do teclado movem entre as abas.
Exemplos
Sublinhado
variant="underline" desenha uma linha sob a aba ativa — bom para cabeçalhos de página e listas filtradas.
'use client';
import { Tab, TabList, TabPanel, Tabs } from '@/components/ui/tabs';
export default function TabsUnderline() {
return (
<Tabs variant="underline" className="w-full max-w-lg">
<TabList aria-label="Pedidos">
<Tab id="abertos">Em aberto</Tab>
<Tab id="enviados">Enviados</Tab>
<Tab id="entregues">Entregues</Tab>
</TabList>
<TabPanel id="abertos" className="py-2 text-muted-foreground">
8 pedidos aguardando separação.
</TabPanel>
<TabPanel id="enviados" className="py-2 text-muted-foreground">
5 pedidos a caminho do cliente.
</TabPanel>
<TabPanel id="entregues" className="py-2 text-muted-foreground">
124 pedidos entregues neste mês.
</TabPanel>
</Tabs>
);
}Vertical
'use client';
import { Tab, TabList, TabPanel, Tabs } from '@/components/ui/tabs';
export default function TabsVertical() {
return (
<Tabs orientation="vertical" className="w-full max-w-xl">
<TabList aria-label="Configurações da loja">
<Tab id="geral">Geral</Tab>
<Tab id="notificacoes">Notificações</Tab>
<Tab id="integracoes">Integrações</Tab>
</TabList>
<TabPanel id="geral" className="rounded-lg border p-4 text-muted-foreground">
Nome fantasia, CNPJ e logotipo da loja.
</TabPanel>
<TabPanel id="notificacoes" className="rounded-lg border p-4 text-muted-foreground">
Lembretes de pedido por WhatsApp e e-mail.
</TabPanel>
<TabPanel id="integracoes" className="rounded-lg border p-4 text-muted-foreground">
Google Agenda, Mercado Pago e emissão de NFS-e.
</TabPanel>
</Tabs>
);
}Com ícones
'use client';
import { CalendarDays, CreditCard, User } from 'lucide-react';
import { Tab, TabList, TabPanel, Tabs } from '@/components/ui/tabs';
export default function TabsWithIcons() {
return (
<Tabs className="w-full max-w-md">
<TabList aria-label="Minha conta">
<Tab id="perfil">
<User /> Perfil
</Tab>
<Tab id="agenda">
<CalendarDays /> Agenda
</Tab>
<Tab id="plano">
<CreditCard /> Plano
</Tab>
</TabList>
<TabPanel id="perfil" className="p-2 text-muted-foreground">
Nome, foto e e-mail de contato.
</TabPanel>
<TabPanel id="agenda" className="p-2 text-muted-foreground">
Horários de atendimento de segunda a sábado.
</TabPanel>
<TabPanel id="plano" className="p-2 text-muted-foreground">
Plano Essencial — renovação em 01/11.
</TabPanel>
</Tabs>
);
}Aba desabilitada
Passe os ids em disabledKeys no Tabs.
'use client';
import { Tab, TabList, TabPanel, Tabs } from '@/components/ui/tabs';
export default function TabsDisabled() {
return (
<Tabs disabledKeys={['relatorios']} className="w-full max-w-md">
<TabList aria-label="Financeiro">
<Tab id="receitas">Receitas</Tab>
<Tab id="despesas">Despesas</Tab>
<Tab id="relatorios">Relatórios (em breve)</Tab>
</TabList>
<TabPanel id="receitas" className="p-2 text-muted-foreground">
R$ 18.420,00 recebidos neste mês.
</TabPanel>
<TabPanel id="despesas" className="p-2 text-muted-foreground">
R$ 6.310,00 em despesas lançadas.
</TabPanel>
<TabPanel id="relatorios" className="p-2 text-muted-foreground">
Disponível em breve.
</TabPanel>
</Tabs>
);
}Controlado
Use selectedKey + onSelectionChange para saber (ou mudar) a aba ativa a partir do seu código.
endereco'use client';
import { useState } from 'react';
import type { Key } from 'react-aria-components';
import { Button } from '@/components/ui/button';
import { Tab, TabList, TabPanel, Tabs } from '@/components/ui/tabs';
export default function TabsControlled() {
const [aba, setAba] = useState<Key>('endereco');
return (
<div className="flex w-full max-w-md flex-col gap-4">
<Tabs selectedKey={aba} onSelectionChange={setAba}>
<TabList aria-label="Checkout">
<Tab id="endereco">Endereço</Tab>
<Tab id="entrega">Entrega</Tab>
<Tab id="pagamento">Pagamento</Tab>
</TabList>
<TabPanel id="endereco" className="p-2 text-muted-foreground">
Rua Barão de Jaguara, 1200 — Centro, Campinas/SP.
</TabPanel>
<TabPanel id="entrega" className="p-2 text-muted-foreground">
Entrega expressa em até 2 dias úteis.
</TabPanel>
<TabPanel id="pagamento" className="p-2 text-muted-foreground">
Pix, cartão de crédito ou boleto.
</TabPanel>
</Tabs>
<div className="flex items-center justify-between gap-4 text-sm">
<span className="text-muted-foreground">
Aba atual: <code className="font-mono">{String(aba)}</code>
</span>
<Button size="sm" variant="outline" onPress={() => setAba('pagamento')}>
Ir para pagamento
</Button>
</div>
</div>
);
}selectedKey, não value
Diferente do shadcn (Radix), a aba ativa é controlada por selectedKey/onSelectionChange
(ou defaultSelectedKey sem controle), e não por value/onValueChange.
Quando usar
- Use para alternar conteúdos do mesmo contexto: dados do paciente, histórico e financeiro de um cliente.
- Não use para navegar entre páginas — use
LinkouSidebar. - Não use para etapas sequenciais obrigatórias (checkout, cadastro em passos) — prefira um fluxo com botões "Voltar/Continuar".
- Conteúdo longo que o usuário precisa comparar ou ler em sequência? Considere
Accordion.
API
Tabs
Aceita todas as props do Tabs do React Aria, mais:
Prop
Type
TabList, Tab e TabPanel
TabList— props doTabList. Informearia-label.Tab—id(obrigatório),isDisabled,href(vira link) eclassName.TabPanel—id(igual ao daTab),shouldForceMounteclassName.