shadcn/ui ha cambiado a Base UI. Esto es lo que cambia de verdad en tu código
shadcn/ui ahora usa Base UI por defecto en lugar de Radix. Todos los cambios de API con los que te vas a topar, la salida de emergencia -b radix y un orden de migración que no duele.
Preet Suthar · · 5 min de lectura
- React
- shadcn/ui
- Base UI
- Radix
El 3 de julio de 2026, npx shadcn init empezó a elegir Base UI en lugar de Radix. Si has leído los titulares y nada más, probablemente pienses una de dos cosas: que Radix está muerto, o que nada ha cambiado. Ninguna de las dos es cierta.
Mantengo HextaUI, un registro de shadcn/ui construido sobre Base UI, así que he pasado la mayor parte de este año dentro de ambas capas de primitivas. Esta es la guía que me habría gustado tener en julio: qué cambió, qué no, todas las diferencias de API con las que te vas a topar y el orden en que migrar si decides hacerlo.
Qué cambió, en un párrafo
Los proyectos nuevos de shadcn/ui reciben primitivas de Base UI. Radix sigue totalmente soportado. El equipo de shadcn dijo que cada actualización y cada componente nuevo se publica para ambas bibliotecas, salvo que un componente solo exista en Base UI. Los proyectos existentes no se tocan. Si quieres Radix en un proyecto nuevo, pasas un flag:
Si tienes CI o scripts que llaman a init de forma no interactiva y esperan Radix, añade ese flag ahora, antes de que un checkout limpio te sorprenda.
Por qué se movió el valor por defecto
Se dieron dos razones. Base UI había alcanzado una 1.6.0 estable con más de seis millones de descargas semanales, y los proyectos creados con shadcn/create ya elegían Base UI frente a Radix en una proporción de unos dos a uno. El valor por defecto siguió lo que la gente estaba eligiendo.
Base UI está construido por la gente detrás de Radix, Material UI y Floating UI, y el linaje se nota. Cubre primitivas que Radix nunca publicó, como Combobox, Autocomplete, Number Field, Toast y Preview Card. A septiembre de 2026 va por la 1.8.0.
¿Deberías migrar una app existente?
Probablemente no este trimestre, y no todo de golpe.
Quédate en Radix si la app es estable, no te bloquea ninguna primitiva que falte y nadie del equipo tiene una queja concreta. Nada de lo que dependes va a desaparecer.
Migra si necesitas Combobox o Number Field sin recurrir a una segunda biblioteca, si ya estás reescribiendo los componentes para Tailwind v4 de todos modos, o si quieres estar en la capa a la que apuntan primero los nuevos componentes de shadcn/ui.
En cualquier caso, lee la siguiente sección antes de decidir, porque las diferencias no son cosméticas.
Todas las diferencias de API con las que te vas a topar de verdad
Los he agrupado según cómo fallan. El primer grupo rompe tu build. El segundo compila sin problemas y cambia el comportamiento en silencio, que es peor.
Cambios que rompen el build
asChild pasa a ser render. Este es el primero con el que se topa todo el mundo.
La prop render acepta un elemento o una función. La forma de función te da el estado, lo que es útil para cosas como un trigger que cambia de icono cuando está abierto.
Los popups anclados ganan un Positioner. Menu, Select, Popover y Tooltip pasan de Portal > Content a Portal > Positioner > Popup. Dialog no tiene Positioner; usa Backdrop y Popup.
Overlay ahora es Backdrop. Renombra y sigue.
Los valores de Select admiten null. Un Select controlado guarda Value | null, no string. Tu useState<string>("") pasa a ser useState<string | null>(null), y cada sitio que leía el valor como string necesita una comprobación.
Los valores de Accordion y Toggle Group siempre son arrays. Incluso en modo single, value="a" pasa a ser value={["a"]}. Tabs conserva un valor único.
onOpenChange tiene un segundo argumento. Radix te deja llamar a event.preventDefault() para impedir un cierre. Base UI pasa (open, details) y llamas a details.cancel(). El campo details.reason te dice si fue la tecla Escape, una pulsación fuera o algo distinto, lo que viene bien para los diálogos de "cambios sin guardar".
Los selectores de estado abierto cambian. Las variantes data-[state=open]: pasan a ser data-open:. Este se esconde en tus strings de className y en las variantes de cva, así que busca data-[state= con grep antes de dar la migración por terminada.
Cambios de comportamiento silenciosos
Tabs se activa manualmente por defecto. En Radix, las flechas mueven el foco y activan la pestaña. En Base UI, las flechas mueven el foco y pulsas Enter o Espacio para activar. Si tus tests o tus usuarios esperan el comportamiento de Radix, pon activateOnFocus en la lista.
Los items de menú de tipo checkbox y radio mantienen el menú abierto. closeOnClick vale false por defecto en CheckboxItem y RadioItem. Los items normales siguen cerrándolo. Se puede defender que es el mejor valor por defecto, pero es un cambio que tus usuarios notarán.
Los menús de navegación se abren más rápido. El delay de hover de Base UI es de 50ms por defecto. El delayDuration de Radix era de 200ms por defecto. Los menús rápidos se sienten genial hasta que alguien barre el ratón por la cabecera y se disparan tres paneles seguidos. Ajústalo.
Un orden de migración que no duele
Mantén las dos bibliotecas instaladas. Migra un componente por commit. Ejecuta tus tests entre uno y otro. En este orden:
- Componentes hoja sin popup: Switch, Checkbox, Slider, Toggle, Progress. Casi todo es renombrar.
- Tabs y Accordion. Corrige los valores en array y decide sobre
activateOnFocus. - Dialog y Alert Dialog. Aprende aquí la estructura de
BackdropyPopupy el patróndetails.cancel(), donde no hay ningún Positioner que confunda las cosas. - Popover y Tooltip. Añade el
Positioner. - Select, Combobox, Dropdown Menu, Context Menu. Valores que admiten null,
closeOnClicky todo el árbol del popup. Hazlos al final, con más tests.
Sustituir un componente por la versión de Base UI es un comando de la CLI, y tu código fuente está en git, así que revisa el diff en lugar de fiarte:
Después git diff components/ui/dialog.tsx y lee cada línea antes de quedártelo.
Lo que yo no haría
No pasaría un codemod por todo el árbol y cruzaría los dedos. Los cambios que rompen el build son fáciles de encontrar. Los silenciosos no, y un codemod no puede decirte que tu página de ajustes ahora necesita dos pulsaciones para cambiar de pestaña.
Tampoco cambiaría de capa de primitivas y rediseñaría los componentes en el mismo PR. La gracia de shadcn/ui es que el código es tuyo. Que sea tuyo significa leer el diff.
Si quieres la migración ya hecha
HextaUI es un registro que añades a un proyecto de shadcn/ui. Cada componente ya está escrito sobre Base UI, conserva los nombres y props de shadcn/ui y usa los mismos tokens de tema, así que npx shadcn@latest add https://hextaui.com/r/dialog.json --overwrite deja un Dialog de Base UI en components/ui junto a tus otros archivos. Los componentes son MIT y gratuitos. La página HextaUI frente a shadcn/ui lista qué es distinto y cuándo shadcn/ui a secas es la mejor elección.
Preguntas frecuentes
¿Radix está obsoleto? No. shadcn/ui publica todos los componentes para ambas bibliotecas, y el equipo sigue usando Radix en producción.
¿Puedo mezclar componentes de Radix y Base UI en un mismo proyecto? Sí. Cargas con ambas dependencias mientras migras. Muchos proyectos seguirán mezclados durante mucho tiempo y no pasa nada.
¿Tengo que cambiar mis tokens de tema? No. --background, --primary, --radius y el resto no cambian. La capa de primitivas es independiente del lado de Tailwind.
¿Base UI soporta React Server Components? Las primitivas de Base UI son componentes de cliente, igual que Radix. Mantén la directiva "use client" donde la pone shadcn/ui.
¿Dónde está el registro de cambios oficial? La entrada de julio de 2026 está en ui.shadcn.com/docs/changelog/2026-07-base-ui-default.