Migração Radix UI -> Base UI
Você migra wrappers shadcn, composições Radix feitas manualmente e seus
consumidores para @base-ui/react, mantendo o projeto compilável em cada
etapa. Seja preciso; nunca adivinhe um mapeamento. Quando uma prop ou parte
não estiver nestes arquivos de referência, verifique
node_modules/@base-ui/react/**/*.d.ts antes de transformar e registre as
lacunas no relatório.
Pré-voo (sempre)
npx shadcn@latest info --json(ou o runner do projeto): fornece a base atual, STYLE (ex.:radix-lyra), versão do Tailwind, aliases, componentes instalados e gerenciador de pacotes. Confie nele em vez de inferir.- Detecte o gerenciador de pacotes (campo packageManager / lockfile: pnpm-lock.yaml, bun.lock, yarn.lock, package-lock.json) e use ELE para cada instalação. Nunca deixe um lockfile obsoleto.
- Exija uma árvore git limpa; trabalhe em uma branch; um commit por componente.
- Verificação de linha de base ANTES de tocar em dependências: execute o typecheck/build do projeto para que falhas pré-existentes nunca sejam atribuídas a você.
- Instale
@base-ui/reactjunto com radix. Os pacotes Radix são removidos apenas após o ÚLTIMO componente ser migrado (ambos coexistem bem).
Estratégia: golden pair primeiro, mecanismo de transformação depois
- Golden pair via CLI (preferido). Se o projeto é shadcn com um
estilo conhecido (
radix-<style>), o próprio CLI do shadcn é o executor do golden pair:- Classifique CADA wrapper ui PRIMEIRO: compare o arquivo do usuário com
sua origem padrão, usando o estilo do components.json VERBATIM na URL
(
https://ui.shadcn.com/r/styles/<style>/<component>.json, files[0].content). Isso funciona para estilos prefixados (radix-nova) E legados não prefixados (new-york, new-york-v4, default), todos ainda servidos. - Modo PROJETO COMPLETO: altere o estilo
radix-<style>emcomponents.jsonparabase-<style>agora. Modo PROGRESSIVO: NÃO altere ainda (o projeto ainda está majoritariamente radix; a alteração acontece uma vez, após o último componente); busque as variantes base diretamente pela URL (https://ui.shadcn.com/r/styles/base-<style>/<component>.json). - Wrappers ÍNTEGROS, modo projeto completo:
shadcn add <component> --overwriteentrega a variante base com a resolução exata de ícone/fonte/preset do projeto. Nunca faça--all --overwriteem lote; vá componente por componente, ou você se afogará em deriva de versão não relacionada do registro. Modo PROGRESSIVO: nunca use--overwrite(ele destrói o original que os consumidores ainda importam); escreva o conteúdo da variante base buscada em<component>-base.tsx. - Wrappers PERSONALIZADOS: busque a variante base e replique o diff do
usuário nela (suas personalizações devem SOBREVIVER;
--overwriteas destruiria). Implementação mecânica que funciona em escala:git merge-file user.tsx radix-golden.tsx base-golden.tsx(merge de três vias, radix golden como ancestral) resolve automaticamente a maioria dos arquivos; resolva conflitos manualmente com as tabelas de referência. - VARREDURA OBRIGATÓRIA de resíduos em CADA arquivo golden pair,
incluindo os que mesclaram "limpo":
grep -n "radix-ui\|@radix-ui\|IconPlaceholder"por arquivo. O registro às vezes reordena funções entre variantes, o que faz o merge de três vias relatar zero conflitos enquanto deixa fragmentos radix obsoletos no lugar. Um merge limpo NÃO é prova de um arquivo limpo. Isso é mais confiável do que reconstruir transformações; use-o sempre que o par existir. Código de consumidor/app não tem mecanismo via CLI: sempre migre manualmente contraconsumer-props.md.
- Classifique CADA wrapper ui PRIMEIRO: compare o arquivo do usuário com
sua origem padrão, usando o estilo do components.json VERBATIM na URL
(
- Estilos legados (new-york, new-york-v4, default): classificação apenas,
sem replay. Estes não têm contraparte base (não existe base-new-york), e
redirecionar para uma variante base- reformataria o app do usuário.
Use o radix golden APENAS para detectar personalizações, depois execute o
mecanismo de transformação no PRÓPRIO arquivo do usuário: reconecte
primitivas, mantenha suas classes exatas, aplique renomeações de
class-mapping. A aparência deles permanece a mesma. No final de uma
migração de projeto completo legada, SINALIZE (não corrija): o nome do
estilo ainda aparece como radix para o CLI, então futuros
shadcn addentregarão variantes radix; o usuário decide se muda o estilo ou adiciona manualmente. - Mecanismo de transformação (fallback). Código radix feito manualmente,
projetos não shadcn, estilos desconhecidos: transforme usando
universal-patterns.md(imports em AMBAS as formas:radix-uie@radix-ui/react-*; asChild->render com o exemplo trabalhado; Portal>Positioner>Popup; a regra FORWARD do positioner; renomeações de partes), as tabelas de props por família (overlays.md,menus.md,form-controls.md,disclosure.md,display-misc.md),class-mapping.mdpara reescritas de data-attribute/CSS-var, ewrapper-shapes.mdpara formas alvo exatas (seta de tooltip, defaults de SubContent, anatomia de select).
Modos
Progressivo (padrão). "Migrar accordion" = um componente, strangler-fig:
- Detecte estado em andamento primeiro: um
<component>-base.tsxexistente, consumidores divididos entre importações antigas/novas. Os arquivos SÃO o estado; retome, nunca recomece. - Se o componente importa outros wrappers ui ainda em radix (select -> button), PARE e recomende migrar aqueles primeiro, de baixo para cima.
- Escreva a versão migrada em
<component>-base.tsx(original intocado; conteúdo golden pair buscado por URL, ou transformado manualmente, de acordo com a estratégia acima); faça typecheck. Reaponte os consumidores UM DE CADA VEZ (importações + as props de ponto de chamada emconsumer-props.md); faça typecheck em cada um. Quando nenhum consumidor importar o original: exclua-o, renomeie-base-> original, reverta as importações, verificação final, commit. Quando o ÚLTIMO wrapper radix no projeto for finalizado, alterecomponents.jsonparabase-<style>e remova as dependências radix.
Projeto completo (apenas quando explicitamente solicitado): mesmo
trabalho por componente em ordem de dependência (wrappers de folha/compartilhados
como button e label primeiro). Após os wrappers, varra TODO o código do app
contra consumer-props.md — a superfície de quebra no ponto de chamada é
muito maior do que asChild. Em seguida, remova as dependências radix,
instale, build completo.
Regras rígidas
- NUNCA toque em bibliotecas não Radix ou seus wrappers: cmdk (command), vaul (drawer), sonner, input-otp, react-day-picker (calendar), recharts (chart). Relate-os como intencionalmente não tocados.
- Sem contraparte Base UI: AspectRatio -> div com aspect-ratio CSS; Label
->
<label>nativo; VisuallyHidden ->sr-only; Direction -> Direction Provider (propdirection, nãodir). Popover Anchor e NavigationMenu Indicator não têm equivalente: passagem inerte + sinalização. button.tsxmigra para a primitiva REAL@base-ui/react/button, nunca um wrapper useRender feito manualmente.- Deltas de comportamento são SINALIZADOS, nunca corrigidos silenciosamente (ativação manual de tabs, itens de menu não fechando ao clicar, delay de 50ms do nav-menu). O alvo é Base UI idiomático correspondendo ao registro base do shadcn.
- Relato honesto: arquivos pulados/revertidos são listados como sinalizados, nunca como migrados. Falhas pré-existentes são nomeadas como pré-existentes.
Verificar e relatar
Typecheck por arquivo, build por lote, build completo no final vs linha de base.
Os relatórios residem em um diretório .migration/ na raiz do projeto, UM
ARQUIVO POR COMPONENTE: .migration/<component>.md (ex.:
.migration/accordion.md). Regras:
- Cada execução escreve (ou sobrescreve completamente) o arquivo para cada componente que migrou. Reexecutar um componente substitui seu relatório; nunca toque nos arquivos de outros componentes.
- Uma execução com vários componentes ("migrar alert-dialog e dropdown-menu") escreve um arquivo por componente, cada um autocontido; notas de varredura de consumidor compartilhadas são repetidas em cada arquivo afetado.
- Modo projeto completo escreve os arquivos por componente mais
.migration/project.md(troca de dependência, resumo da varredura de código do app, resultado do build final). - NÃO há arquivo de índice. O status da migração é derivado do disco, não
mantido: escaneie o diretório ui do projeto (o alias
uido shadcn info, ex.: components/ui ou src/components/ui) em busca de importações radix restantes quando perguntado "o que ainda falta". Termine cada resumo de execução com essa contagem derivada ("N wrappers permanecem em Radix").
Cada .migration/<component>.md usa EXATAMENTE esta estrutura (é
documentado publicamente; os relatórios devem corresponder a ele):
# <component>
<data, estratégia usada (golden pair via CLI / merge / mecanismo), veredito de uma linha>
## Alterado
<todos os arquivos tocados, com o que mudou e por quê; inclua arquivo:linha
para qualquer coisa notável. Confirme que a varredura de resíduos está
limpa:
grep -n "radix-ui\|@radix-ui" nestes arquivos do componente>
## Deixado de lado
<arquivos que parecem relacionados mas foram intencionalmente não tocados,
com o motivo (cmdk/vaul/sonner não são radix; deriva não relacionada; etc.)>
## Mudanças de comportamento
<diferenças que compilam bem mas agem de forma diferente; sinalizadas,
nunca corrigidas (ativação de tabs, fechamento ao clicar em menu,
delays...). Seção vazia se nenhuma>
## Verificar manualmente
<pequena lista de verificação de QA manual para esta família de primitivas:
retorno de foco em diálogos, navegação por teclado + typeahead em
menus/select, sensação de delay de tooltip, eventos de commit de slider.
Passos concretos, um minuto de cliques>