< BLOG />
Shadcn UI: conoce tu hipoteca
Qué estás firmando realmente cuando copias componentes de shadcn/ui dentro de tu repositorio y en qué proyectos compensa
< INICIO />
TL;DR;
Este post es de los largos, si quieres ir al grano, aquí tienes un resumen:
shadcn/ui no es una librería de componentes tradicional: su CLI copia en tu repositorio componentes React estilados con Tailwind y construidos sobre Radix, Base UI, React Aria y otras librerías especializadas (React Day Picker...).
Fijate que antes de empezar debes elegir qué base utilizar —Radix, Base UI o React Aria—. No es una decisión meramente visual: condiciona las dependencias, las APIs y parte del comportamiento de los componentes.
Eso quiere decir que el código que metas ahí es tuyo, y que las dependencias y su mantenimiento pasan a ser responsabilidad de tu equipo.
Su registry es probablemente una de sus ideas más potentes: permite distribuir componentes, patrones y pantallas completas, e incluso crear un catálogo corporativo privado. A cambio, el código copiado no conserva la trazabilidad ni el sistema de actualizaciones propio de un paquete npm.
Esto para un prototipo o un desarrollo rápido está muy bien, porque además se lleva de maravillas con herramientas IA y vas como una moto.
Para un desarrollo más serio, tienes que saber que enfoque tomar, porque si no, puedes acabar con un proyecto difícil de mantener y actualizar.
Otro tema importante para elegir shadcn/ui, es el tipo de proyecto que tengas entre manos: si es un proyecto de gestión, con funcionalidades de tablas avanzadas, puede que no sea la mejor opción.
Y después de está zambullida rápida, mi consejo, es que leas este post, veas los detalles técnicos y con eso formes tu opinión sobre si shadcn/ui encaja en tu proyecto, espero que te sea útil 😊.
Intro
Le pides a tu asistente de IA una pantalla de login. O una tabla de usuarios. O un formulario de alta con validación.
Y sin que tú hayas dicho nada, en la terminal aparece esto:
npx shadcn@latest add button card input formCinco segundos después tienes una pantalla preciosa. Modo oscuro incluido. Accesible. Con animaciones. Y tú piensas: qué maravilla, esto es como Material UI pero con Tailwind y sin el olor a Google.
Pues no exactamente.
Porque acabas de hacer algo que con MUI, con Ant Design o con PrimeReact no habías hecho nunca: te has metido código ajeno dentro de tu repositorio. No en node_modules. En tu src/. En tus Pull Requests. En tu responsabilidad.
Y ahí está la gracia del asunto: shadcn/ui no es una librería de componentes. Lo dice su propia documentación, pero es una de esas frases que todo el mundo lee y se la salta mientras empieza a añadir componentes como si no hubiera un mañana.
Cuando te bajas un botón, no has instalado el botón como una dependencia encapsulada. Has copiado su código dentro de tu proyecto. Y acabas de firmar una hipoteca.
La cuota inicial es ridícula: cinco segundos y generas una pantalla estupenda. Las cuotas que te pueden costar digerir empiezan más tarde, cuando alguien de tu equipo pregunta "oye, ¿y esto cómo se actualiza?" y descubrís que la respuesta no es npm update.
Antes de continuar con este post, dejemos un punto muy claro: shadcn/ui es una herramienta excelente. Su popularidad tiene motivos sólidos y en Lemoncode la usamos dependiendo del escenario que nos encontremos.
Esto no va de si es buena o mala, va de las dos preguntas que casi nadie se hace antes de escribir ese comando:
¿Qué parte del coste estoy pagando hoy y qué parte estoy dejando para dentro de dos años?
Y, sobre todo, ¿es esta la herramienta adecuada para el tipo de aplicación que estoy construyendo?
Porque esa segunda pregunta es donde te puedes meter en un buen jardín y la que menos se plantea. Una hipoteca no es buena ni mala en abstracto: depende de la casa que estés comprando. Hay proyectos donde shadcn es la mejor decisión que vas a tomar en todo el desarrollo, y hay proyectos donde te va a costar un año de trabajo extra ya que no sabías que estabas firmando. Y no se distinguen por el tamaño del equipo ni por el presupuesto, sino por el tipo de interfaz que tienes delante.
A eso le dedicamos la segunda mitad del post, con nombres y apellidos.
Como en cualquier hipoteca, el problema no tiene por qué estar en la entrada, muchas veces te la encuentras en la letra pequeña.
Qué vas a encontrar aquí
En este post no vamos a hacer otro tutorial de instalación y primeros pasos, vamos a abrir el capó y ver qué estás metiendo realmente en tu proyecto.
Esto es el post que a mí me habría gustado leer antes de meter shadcn en un proyecto grande:
- De dónde sale ese nombre tan raro (y por qué el nombre ya te está avisando de algo).
- Qué es de verdad, y por qué llamarlo "librería de componentes" te confunde.
- Qué ocurre exactamente cuando ejecutas
add. - Lo que hace muy bien.
- Lo que te va a doler, con casos concretos.
- Para qué tipo de aplicación puede ser una elección magnífica, y para cuál un error caro.
Y una cuota de la hipoteca que llegó al buzón hace un mes y que demuestra toda la tesis del post mejor que cualquier argumento que planteemos.
Vamos allá.
Parte 1: El nombre ya te está contando algo
En resumen shadcn no es un acrónimo ni una marca: es el nick del autor. Se escribe
shadcn/ui, en minúsculas y con barra. Y no tiene nada que ver con la funcióncn()que verás en el código. Que el proyecto se llame como una persona y no como un producto no es casualidad.
Empecemos por la pregunta que todo el mundo se ha hecho y nadie explica: ¿qué demonios significa "shadcn"?
No es un acrónimo. No significa "SHAred Design Components Network" ni nada por el estilo. No hay un departamento de marketing detrás.
Es, sencillamente, el nick de una persona.
El tipo del avatar de Rick y Morty
El proyecto lo creó un desarrollador conocido públicamente como shadcn, a secas. El nombre del proyecto viene de su alias, no de un acrónimo de componentes ni de la función cn().
Y hasta ahí llega lo que se puede afirmar. Sus perfiles se identifican simplemente como shadcn: en GitHub no hay nombre ni ubicación, solo la bio "I own a computer" y su pertenencia a Vercel, donde entró como design engineer en agosto de 2023, cuando el proyecto ya se estaba comiendo el mundo.
Circulan atribuciones de identidad por foros y artículos, pero no hay fuente primaria que las respalde y él nunca ha confirmado ninguna. Así que aquí no vamos a jugar a eso. El misterio, de hecho, forma parte del personaje: un avatar de Rick y Morty se ha convertido en una de las marcas más reconocibles del frontend sin que haga falta saber quién hay detrás.
💡 El malentendido que hay que aclarar aquí
Y ahora el detalle bueno, el que hace que mucha gente diga "anda, pues yo pensaba que...".
Cuando instalas tu primer componente, aparece un fichero lib/utils.ts con esto dentro:
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}Esa función cn la vas a ver en absolutamente todos los componentes. Y hay mucha gente convencida de que el proyecto se llama shad-cn por ella.
Pues no. Son cosas completamente distintas.
cnla función es el nombre que la comunidad lleva años usando para el helper que combina clases CSS — la convención viene de la libreríaclassnames, y shadcn no la inventó: se la encontró hecha.cndel nombre es la segunda mitad de un alias.
Coincidencia. Pero como las dos cosas conviven en el mismo proyecto, el lío está servido.
Ah, y se escribe en minúsculas
No es "ShadCN". Ni "Shadcn UI". Ni "SHADCN".
Es shadcn/ui, en minúsculas y con barra, tal y como lo escribe el propio proyecto en todas partes. Es el mismo tipo de detalle que distinguir npm de NPM: no cambia nada técnico, pero delata si has leído la documentación o solo tutoriales de terceros.
(Sí, en el título del post lo he escrito con mayúsculas. Culpa mía y del SEO. A partir de aquí, bien escrito.)
Y ahora, lo que de verdad importa del nombre
Fíjate un momento en cómo se llaman los demás:
- Material UI se llama como un sistema de diseño (el de Google).
- Ant Design se llama como un sistema de diseño (el de Alibaba).
- Fluent UI se llama como un sistema de diseño (el de Microsoft).
- shadcn/ui se llama como el alias de su creador.
Y eso no es una anécdota, pero el motivo no es el que parece.
Los tres primeros implementan una especificación que existe fuera de ellos. Material Design es un documento público, con sus reglas de elevación, de espaciado y de movimiento. MUI es la encarnación en React de ese documento. Si su Button no se parece a lo que dice la especificación, es un bug de MUI: hay una autoridad externa a la que apelar, y no eres tú.
shadcn/ui no implementa una especificación externa como Material Design o Fluent Design. Lo que ofrece es un sistema coherente de convenciones y valores predeterminados: radios, sombras, espaciado, tokens semánticos, variantes y patrones de composición. Excelentes, pero modificables.
Existe una implementación upstream y una documentación que describen cómo se comportan los componentes originales. Pero, en el momento en que el código entra en tu repositorio y empiezas a modificarlo, esa referencia puede dejar de describir tu versión.
A partir de ahí, la especificación efectiva pasas a ser tú: tu código, tus decisiones y, si lo has hecho bien, tu documentación y tus pruebas.
Si dentro de dos años nadie recuerda por qué vuestro Dialog tiene ese padding, no podrás asumir que la respuesta siga estando en la documentación de shadcn. Puede que esa decisión la tomara alguien de tu equipo y solo sobreviva en una línea de Tailwind.
Esa es la asimetría de verdad, y no depende de cuánta gente mantenga el proyecto.
Y ya que estamos, una foto actualizada
Porque conviene no arrastrar la imagen de 2023. shadcn/ui nació como la propuesta personal de un desarrollador, y ese origen explica buena parte de su personalidad: criterio visual muy marcado, decisiones coherentes y una velocidad difícil de conseguir cuando cada detalle pasa por tres comités.
Pero hoy la cosa es distinta. El footer de la documentación dice "Built by shadcn at Vercel", Vercel lo describe como "a Vercel-supported open-source project", el repositorio va por más de 460 contribuidores y el proyecto está estrechamente integrado con productos de la casa como v0. Ya no es el proyecto solitario de una persona.
Ojo, que eso no lo convierte en un producto comercial: no hay SLA, ni soporte de pago, ni compromiso contractual de compatibilidad. Es MIT, como siempre. Pero tampoco es justo hablar de él como si fuera el hobby de alguien.
Parte 2: Qué es realmente shadcn/ui
En resumen No es una librería de componentes: es una plataforma de distribución de código fuente. Con MUI instalas una dependencia. Con shadcn te copias el código dentro de tu repo. A partir de ahí ese código es tuyo. Con todo lo bueno y todo lo malo que eso implica.
Esta es la parte importante. Si solo te llevas una cosa del post, que sea esta.
La explicación que te han contado
"shadcn/ui es una colección de componentes accesibles y personalizables construidos con Tailwind CSS y Radix."
Es verdad. Y es tan incompleta que resulta engañosa.
La explicación que da el propio proyecto
Vete a la documentación oficial y lee la primera línea:
shadcn/ui es "a set of beautifully-designed, accessible components and a code distribution platform".
Una plataforma de distribución de código. Lee esas cuatro palabras otra vez, porque son la clave de todo.
Y por si quedaba alguna duda, dos líneas más abajo está la frase que resume el proyecto entero:
"This is not a component library. It is how you build your component library."
(Esto no es una librería de componentes. Es cómo construyes tu librería de componentes.)
Ahí está. En la página uno de la documentación, más claro agua...
El problema nunca ha sido que shadcn engañe a nadie. *El problema es que esa frase se lee en dos segundos e igual la entiendes a los dos años, cuando ya tienes todo el código empantanado*
El contraste, en dos líneas de código
Aquí se ve todo:
npm install @mui/material # entra en node_modules. Sigue siendo de MUI.
npx shadcn@latest add button # entra en tu src/. Ya es tuyo.Con MUI, después importas desde la dependencia:
import Button from "@mui/material/Button";Ese botón vive en node_modules. No lo has escrito tú, no lo revisa tu equipo, no aparece en tus PRs. Si tiene un bug, abres un issue. Si quieres la corrección, subes de versión. Y si quieres personalizarlo, usas las APIs que MUI te ofrece: tema, props, slots, slotProps, sx.
Con shadcn, después importas desde tu propio código:
import { Button } from "@/components/ui/button";
export const SaveButton = () => <Button variant="outline">Guardar</Button>;Fíjate en el @/. Ese botón está en src/components/ui/button.tsx. Son unas 50 líneas de TSX que ahora mismo puedes abrir, leer y reescribir enteras.
Y también: que entran en tu control de versiones, las revisa tu compañero en el PR, las cuentan tus métricas de código y las mantienes tú.
La analogía, ya que estamos con hipotecas
MUI es alquilar. Pagas la renta cada mes (dependencia, versiones, alguna limitación estética). A cambio, si se rompe la caldera llamas al casero. No puedes tirar tabiques, pero tampoco te preocupa el tejado.
shadcn es comprar con hipoteca. La casa es tuya. Puedes tirar tabiques, cambiar la cocina y pintar lo que quieras. Nadie te dice nada.
Y la caldera también es tuya.

La descripción precisa
Si tuviera que definirlo en una frase:
shadcn/ui es un sistema de distribución de código fuente para componentes de interfaz, acompañado de unas convenciones visuales, un CLI y uno o varios registries.
No es una librería. Es una fábrica de librerías. Y la librería que sale por el otro extremo es la tuya.
Eso puede ser exactamente lo que quieres. O puede ser lo último que necesitas. Depende del proyecto, y a eso dedicamos la segunda mitad del post.
Pero para decidirlo, primero hay que leer la letra pequeña.... vamos a ello.
Parte 3: Qué pasa de verdad cuando ejecutas add
En resumen El CLI lee tu
components.json, consulta el registry y te escribe ficheros dentro del proyecto. No baja un artefacto compilado: baja un pequeño árbol de código con sus dependencias. Y si tú ya habías tocado esos ficheros, aquí empiezan los problemas.
Vamos a abrir la caja negra. Ejecuta esto:
npx shadcn@latest add alert-dialogY el CLI hace, simplificando, seis cosas:
- Lee la configuración de tu proyecto, normalmente desde
components.json. - Consulta el componente en el registry que tengas configurado.
- Obtiene su definición: ficheros, contenido, dependencias y otros elementos del registry que necesita.
- Resuelve dependencias entre componentes. Por ejemplo,
AlertDialognecesitaButton. - Instala las dependencias externas que hagan falta: Base UI, Radix, React Aria, o la librería especializada de turno.
- Escribe los ficheros dentro de tu proyecto.
Fíjate en el paso 4, porque es el que nadie mira y el que más caro sale.
El registry no distribuye un paquete. Distribuye un pequeño árbol de código que el CLI injerta en tu repositorio, con sus ramificaciones.
El fichero que manda: components.json
Antes de seguir, cinco líneas sobre el fichero que gobierna todo esto, porque suele pasar desapercibido y tiene más poder del que parece:
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "base-nova",
"tsx": true,
"tailwind": {
"css": "src/index.css",
"baseColor": "neutral",
"cssVariables": true
},
"aliases": {
"components": "@/components",
"ui": "@/components/ui",
"utils": "@/lib/utils",
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}Esto es el contrato entre tu proyecto y el CLI. Le dice dónde escribir, con qué alias importar, qué librería de iconos y —muy importante, ya lo veremos— sobre qué familia de primitivas construir.
Y fíjate bien en ese "style": "base-nova", porque ahí está la decisión gorda escondida en un campo que parece cosmético. El formato es {base}-{estilo}: la primera mitad es la familia de primitivas y la segunda el estilo visual. El esquema oficial admite las 24 combinaciones de tres bases —radix, base y aria— por ocho estilos: Vega, Nova, Maia, Lyra, Mira, Luma, Sera y Rhea. Desde el CLI se elige con --base:
npx shadcn@latest init --base base # Base UI
npx shadcn@latest init --base radix # Radix UI
npx shadcn@latest init --base aria # React AriaSi en tu proyecto ves un "style": "new-york" a secas, es de la etapa anterior: son valores heredados que no codifican la base, porque cuando se crearon solo había una.
Un fichero de veinte líneas que decide la arquitectura de tu capa de UI — y una sola palabra dentro de él que decide de qué librería dependes. Merece una lectura consciente y no un init a ciegas. → documentación de components.json
💻 Si quieres ver el inventario exacto de lo que deja un
init+add—ficheros, líneas y las ocho dependencias nuevas— lo tienes contado en la demo01-que-copia-el-add.
Y ahora, el momento incómodo
Volvamos al paso 4. Situación real, de las que pasan en todos los proyectos:
Mes 1. Instalas button. Perfecto.
Mes 3. Necesitas una variante danger y un tamaño xs. Abres button.tsx y lo tocas. Ya de paso, quitas la prop asChild porque no la usabais y ensuciaba. Es tu código, faltaría más. Ese es justamente el argumento de venta.
Mes 7. Alguien necesita un diálogo de confirmación:
npx shadcn@latest add alert-dialogY AlertDialog depende de Button. De un Button que ha cambiado.
El CLI hace lo que puede: detecta que button.tsx ya está ahí y te pregunta si quieres sobrescribirlo. Y ahí te quedas tú, en un dilema que no tiene buena salida:
Si sobrescribes, pierdes tu trabajo de añadir personalizaciones. Y probablemente rompes quince pantallas que usaban tu variante
danger.Si no sobrescribes, te llevas un
AlertDialogescrito contra un contrato que tuButtonigual ya no cumple.
Si tu cambio era compatible (añadir una variante), lo más probable es que todo funcione. Si eliminaste props, cambiaste nombres o tocaste los exports, te va a explotar en tiempo de compilación — y ese es el caso bueno, porque al menos avisa.
El caso malo es el otro: otras incompatibilidades compilan perfectamente y se manifiestan después como diferencias de comportamiento, que son bastante más difíciles de detectar. Imagina que en tu Button metiste un preventDefault o envolviste el onClick para lanzar analítica. Los tipos cuadran, TypeScript calla, el build pasa — y el AlertDialog que acabas de bajar no se cierra al pulsar el botón. No hay error en ninguna consola. Solo un diálogo que se queda ahí plantado, y alguien descubriéndolo tres semanas después.
Y aquí no hay magia posible. El CLI puede detectar que el fichero existe. Lo que no puede es saber si tu Button sigue cumpliendo el contrato que esperaba el componente que acaba de bajar.
Desde el momento en que tocas el código, la compatibilidad interna del sistema es tuya.
⚠️ La diferencia que hay que interiorizar
Vamos a decirlo del modo más claro posible, porque es la cuota principal de esta hipoteca:
| Librería normal | shadcn/ui | |
|---|---|---|
| Actualizar es... | subir de versión | hacer un diff a mano |
| ¿Contra qué? | contra nada, lo hace npm | contra un fichero que tú has modificado |
| ¿Quién lo hace? | tu gestor de paquetes | una persona de tu equipo |
| ¿Cuándo te enteras de que hay novedades? | npm outdated |
cuando lo lees en Twitter |
Seamos justos: hay herramienta. El CLI trae una opción --diff en el comando add que te enseña las diferencias entre lo que hay en el registry y lo que tienes tú:
npx shadcn@latest add button --diffY ayuda, claro que ayuda. Pero fíjate en lo que hace y en lo que no:
- Te enseña la diferencia. No la fusiona. El criterio de qué parte del cambio quieres y cuál pisaría tu personalización lo pones tú, línea a línea.
- Tienes que preguntar componente a componente. No hay un
shadcn outdatedque te suelte la lista de los doce que se han quedado atrás. - Y sobre todo: tienes que acordarte de preguntar.
Que es exactamente el último punto de la tabla, y el más subestimado de todos: nadie te va a avisar de que el Select que copiaste hace ocho meses tiene ahora una corrección de accesibilidad.
Y conviene separar bien las dos mitades, porque se confunden constantemente. Tus herramientas de actualización de dependencias siguen funcionando: npm audit y Dependabot vigilan los paquetes npm que ese componente utiliza por debajo —las primitivas, clsx, tailwind-merge— y te avisarán de sus vulnerabilidades como siempre. Esa mitad está cubierta.
Lo que ninguna herramienta te dice es que tu copia de select.tsx se ha quedado desactualizada: que el fichero que bajaste hace ocho meses recibió después una corrección de accesibilidad o de comportamiento. Ese fichero, para tu tooling, no es una dependencia desactualizada. Es código tuyo. Y el código propio no aparece en ningún informe de dependencias, porque no hay nada que informar.
Lo bajaste, es tuyo, y el mundo siguió girando sin ti.
Esto no lo digo para asustar. Lo digo porque es perfectamente gestionable si lo sabes de antemano: revisar el changelog cada X semanas, tener los componentes base identificados, tests que protejan tus modificaciones. Es una práctica de equipo, y como toda práctica, hay que presupuestarla.
Lo que te puede romper por completo, es descubrir esto cuando llevas meses trabajando en tu proyecto.
💻 Este caso lo tienes montado y funcionando en la demo
02-el-conflicto. Es código real bajado del registry: ejecutanpm run buildy verás el error de TypeScript de verdad. Ojo al detalle: connpm run devla aplicación sí arranca, y el fallo no aparece hasta el build.
Parte 4: El registry no es npm (y ahí está la joya escondida)
En resumen El registry es un catálogo de recetas, no un gestor de paquetes: no te deja un lockfile que ate cada fichero copiado a una versión de origen. Si desapareciera mañana, tu aplicación seguiría funcionando. Y como el formato es abierto, cualquiera puede montar el suyo, este punto es muy interesante.
Primero, lo que le falta
El registry oficial de shadcn es un catálogo que entiende el CLI. Contiene las recetas para incorporar componentes. Pero no es npm, y las diferencias importan:
npm distribuye paquetes versionados. El registry distribuye código y configuración que pasan a formar parte de tu proyecto.
npm mantiene una relación explícita entre tu aplicación y una versión concreta, anotada en tu package-lock.json. Con shadcn, una vez copiado y modificado el componente, la relación con el origen es casi simbólica. No hay ningún sitio donde ponga "este dialog.tsx viene de la versión tal".
npm te avisa. El registry no.
A diferencia de npm, el registry oficial no deja en tu proyecto un lockfile que relacione cada fichero copiado con una versión concreta de origen. Y eso cambia la naturaleza de dos cosas que dabas por sentadas:
La auditoría deja de ser automática. No hay un comando que te diga qué versión de qué componente tienes. Lo sabes si lo has anotado tú.
El rollback pasa a depender de Git, no de una versión declarada. Vuelves atrás con un revert, como con cualquier código tuyo — que funciona perfectamente, pero es otro modelo mental: no estás bajando de versión un componente, estás deshaciendo un cambio en tu repositorio.
Un matiz importante: un registry propio sí puede implementar versionado. La documentación de namespaces cubre versionado con parámetros, canales, variables de entorno y rangos semver — puedes montarte un @acme/button?version=v2 o un semver: "^2.0.0" gobernado por entorno. Lo que no viene resuelto de serie es la trazabilidad en el destino: aunque tu registry sirva versiones, el modelo copy-and-own no anota en tu proyecto de cuál vino cada fichero. Eso te toca a ti.
Pero tranquilo: si el registry desaparece, no pasa nada
Esta pregunta sale siempre y conviene responderla con claridad, porque la respuesta es tranquilizadora:
Si mañana el registry oficial cerrara, cambiara de condiciones o pasara a ser de pago, tu aplicación no se enteraría.
Los componentes ya están copiados. El código está en tu repo. Las dependencias están en tu package.json. Los componentes ya copiados no dependen del registry ni durante el build ni en ejecución: el registry solo interviene cuando ejecutas add.
Otra cosa son las dependencias npm y el CSS compartido que haya incorporado el init. Y aquí hay un detalle que conviene conocer, porque va contra la idea de "todo el código es mío": desde Tailwind 4, el init añade a tu CSS global un @import "shadcn/tailwind.css", que son las utilidades compartidas entre los componentes. Eso significa que el paquete shadcn es una dependencia de build — pequeña, resuelta en tiempo de compilación y tree-shaken en producción, pero dependencia. Si no la quieres, tienes shadcn eject, que mete ese CSS en tu fichero global y elimina el paquete. Con su letra pequeña, cómo no: "This action is irreversible. After ejecting, future shadcn CLI updates to shadcn/tailwind.css will not apply automatically."
Lo que SI perderías es la comodidad de traerte componentes nuevos, y eso te puede afectar bastante.
Si te paras a pensar con librerías de componentes tradicionales, el registry es un lujo que no tienes con ninguna otra. Con MUI, Ant Design o PrimeReact, si mañana desaparecieran, tu aplicación seguiría funcionando — pero no podrías añadir un DatePicker nuevo ni actualizar el Button que ya tenías, salvo que te pusieras el casco de minero.
Y ahora lo bueno: el formato es tuyo
Aquí viene la parte que me parece más potente de todo el proyecto y de la que casi nadie habla.
El formato del registry es abierto y está documentado. Cualquiera puede montar el suyo. Y no hablamos solo de botones bonitos: un registry puede distribuir
Tu design system corporativo,
Componentes de negocio,
Pantallas completas (los llamados blocks),
Hooks y utilidades,
Convenciones de formularios y validación,
Código compartido entre varios productos.
Es decir: un mecanismo estándar para distribuir código fuente corporativo por CLI, que además tu equipo ya sabe usar porque es el mismo comando de siempre.
¿Y puede ser privado? Sí, y está muy bien resuelto
Esta es la pregunta que siempre sale en cuanto alguien de una empresa oye lo del registry propio: "ya, pero mi design system no lo puedo publicar en internet".
Tranquilo, que está contemplado. Los registries privados son ciudadanos de primera clase, con documentación propia de autenticación y traen soporte para los mecanismos habituales.
Funciona con namespaces. Declaras tus registries en components.json:
{
"registries": {
"@acme": {
"url": "https://registry.acme.com/r/{name}.json",
"headers": {
"Authorization": "Bearer ${ACME_REGISTRY_TOKEN}"
}
}
}
}El {name} se sustituye por el recurso que pidas, y ${ACME_REGISTRY_TOKEN} se expande desde tus variables de entorno — o sea que el token vive en tu .env.local y no acaba comiteado en el repo.
A partir de ahí, tu equipo trabaja así:
# El botón corporativo, del registry privado de la empresa
npx shadcn@latest add @acme/button
# Y puedes mezclar fuentes en el mismo comando
npx shadcn@latest add @acme/data-table @acme/use-permissions dialogFíjate en esa última línea, porque es la que enseña la idea completa: dos componentes tuyos, un hook tuyo y un componente del catálogo público, en una sola orden. Para quien lo escribe no hay diferencia entre lo corporativo y lo de fuera.
Y hay un par de comandos que redondean la experiencia y que mucha gente no conoce:
npx shadcn@latest view @acme/button # míralo antes de instalarlo
npx shadcn@latest list @acme # qué hay en nuestro catálogo
npx shadcn@latest search @acme -q "form" # busca en élEse view es de los que deberían ser obligatorios por política de equipo: leer el código antes de meterlo en el repo, que es justo la disciplina que esta forma de trabajar exige.
Además, en junio de 2026 se añadió soporte para registries alojados en GitHub, lo que baja aún más la barrera: basta con un repositorio público en github.com con un registry.json en la raíz, y el CLI ya sabe leerlo. Sin levantar infraestructura ninguna.
Ojo con el matiz, porque es el que más se malinterpreta: las direcciones de GitHub no valen para repositorios privados ni para GitHub Enterprise. La propia documentación lo dice sin rodeos: "Private repositories and GitHub Enterprise hosts are not currently supported by GitHub addresses". Si tu catálogo es interno —que es el caso de casi cualquier empresa— el camino es el de arriba: un namespace con URL propia y autenticación por cabeceras. Que está perfectamente soportado, pero es otra cosa; el atajo de GitHub es para catálogos abiertos.
Y como el MCP conecta con los registries que tengas configurados, tu asistente de IA también puede buscar e instalar componentes de tu catálogo interno. Que tu equipo pueda decir "móntame la pantalla de alta de cliente con nuestros componentes" y que la IA tire de los tuyos y no de los de internet es, para una organización con un design system, bastante más valioso de lo que suena.
💎 Toca mojarse, opinión
Para una empresa con varios productos, o para una consultora que arranca proyectos cada trimestre, esto es potencialmente más valioso que los propios componentes de shadcn.
Piénsalo. El botón te lo puedes hacer. Todos nos hemos hecho un botón. Lo que no te puedes hacer en una tarde es un mecanismo estandarizado para repartir tus patrones entre quince equipos, con su CLI, su resolución de dependencias y su documentación.
Eso es infraestructura de plataforma, y shadcn te la regala.
El pago, como siempre: infraestructura, gobierno, versionado, documentación y alguien que lo mantenga. No es gratis, es una inversión. Pero es una inversión con un retorno mucho más claro que "me copio un botón bonito".
Si trabajas en una organización grande y solo te llevas un ítem de acción de este post, que sea mirar el formato de registry con calma.
Parte 5: La primera cuota ya ha llegado
En resumen shadcn no implementa el comportamiento de sus componentes: se apoya en Radix, Base UI, React Aria, TanStack, DayPicker, Recharts... En julio de 2026, Base UI pasó a ser la base por defecto. Está impecablemente gestionado y Radix no está deprecado. Pero la decisión aterriza en tu repo, no en tu
package.json.
Llegamos a la parte más interesante del post. Y la más oportuna, porque lo que voy a contar pasó hace cinco semanas.
Primero: la ilusión de homogeneidad
Cuando miras la web de shadcn/ui, todo parece pertenecer a la misma familia. Mismo estilo, misma paleta, mismos radios, misma tipografía. Da la sensación de un sistema construido sobre un núcleo común.
Por debajo, la realidad es otra.
shadcn no implementa desde cero el comportamiento de sus componentes. Y es buena elección, porque hacerlo bien es carísimo: gestión del foco, navegación por teclado, portales, posicionamiento flotante, lectores de pantalla, ARIA, roles, colisiones con el viewport... Cada uno de esos problemas se ha llevado años-persona de trabajo en las librerías que lo han resuelto.
Así que se apoya en quien ya lo hizo. Según el componente:
- Radix UI, Base UI o React Aria para las primitivas de interacción.
- TanStack Table para tablas.
- React DayPicker para el calendario en las variantes de Base UI y Radix (la de React Aria lleva su propio calendario).
- Recharts para las gráficas.
- Embla para los carruseles.
- Y alguna más, según lo que instales.
No hay un motor único. Hay un mosaico de librerías especializadas con una capa de estilo común por encima.
El momento en que te das cuenta: el calendario
A mí el clic me llegó con el calendario.
Llevas rato leyendo sobre Radix y Base UI, ya te has hecho tu modelo mental de "vale, shadcn = primitivas headless + Tailwind", instalas el calendar y... aparece React DayPicker en tu package.json.
¿Perdona? ¿Otra librería más?
Y no es que lo escondan. Está en la primera línea de la documentación oficial del componente:
"The
Calendarcomponent is built on top of React DayPicker."
La propia página remata mandándote a leerte la documentación de React DayPicker para lo demás. Que es la señal más clara de todas: para dominar el calendario de shadcn tienes que aprenderte la API de otro proyecto.
Y hay un detalle que lo pone todavía más crudo. Ese DayPicker es el de las variantes de Base UI y Radix; si tu proyecto va sobre React Aria, el calendario es otro — el de React Aria, con @internationalized/date para las fechas y su propia API. Mismo nombre de componente, mismo aspecto en pantalla, motor distinto y documentación distinta que aprenderte.
Piensa un segundo en lo que eso significa: el campo style de tu components.json no solo decide de qué familia de primitivas dependes. En algunos componentes decide también qué librería de terceros acabas aprendiendo.
Y ahí lo entiendes todo. Un calendario es un bicho muy especializado: rangos, localización, meses, límites, semanas, teclado, accesibilidad, formatos. Apoyarse en una librería experta es la decisión correcta, no un atajo.
Pero revela la naturaleza real del proyecto:
shadcn/ui es una capa de integración y distribución. No es un toolkit construido sobre un núcleo propio.
Y de ahí se deriva lo importante: cuando adoptas un componente, adoptas también su dependencia, su API, su ciclo de vida y sus decisiones. Visualmente todos tus componentes parecen hermanos. Técnicamente son primos lejanos que se visten parecido.

Un momento: ¿qué son exactamente Radix, Base UI y React Aria?
Antes de seguir conviene aclarar esto, porque es una de las confusiones más habituales y sin ella no se entiende lo que viene después.
Ninguna de las tres es una librería de componentes con diseño. Las tres son headless: te dan el comportamiento y la accesibilidad, y cero estilos.
Pero ojo, porque headless significa cosas distintas según el sitio, y ahí está el lío. Hay tres niveles:
| Nivel | Qué te da | Ejemplos |
|---|---|---|
| Solo lógica | Estado y funciones. No pinta ni un <div>. |
TanStack Table, React Aria (hooks) |
| Componentes sin estilo | DOM, accesibilidad y comportamiento. Ni una línea de CSS. | Radix Primitives, Base UI, React Aria Components |
| Librería con diseño | Todo lo anterior más el aspecto visual | MUI, Ant Design, PrimeReact |
Radix, Base UI y React Aria viven en el nivel de en medio.
Si instalas un Dialog de Base UI tal cual y lo abres en el navegador, verás tu contenido plantado en la esquina superior izquierda, sin overlay, sin sombra y sin nada. Feísimo. Pero por debajo ya funciona todo lo caro: atrapa el foco, cierra con Escape, bloquea el scroll del fondo, devuelve el foco al elemento que lo abrió y anuncia lo que toca al lector de pantalla.
Eso es lo que compras. El aspecto lo pones tú.
Y ahí es donde encaja shadcn: coge ese componente sin estilo y le pega encima la capa de Tailwind con los tokens del tema. Nivel 2 más diseño igual a nivel 3, pero con el código en tu repo.
Dicho de otro modo: shadcn/ui es el CSS que le falta a Base UI, empaquetado de forma que te lo puedas llevar a casa.
⚠️ El detalle que despista a todo el mundo
Si has entrado alguna vez en la web de Radix y te ha parecido que sí tenía componentes con diseño, no te lo estabas inventando. Radix son dos productos distintos con el mismo nombre:
- Radix Primitives — las piezas sin estilo. Esto es lo que usa shadcn.
- Radix Themes — una librería de componentes ya diseñada, construida sobre las primitives. Esta compite con MUI, no con shadcn.
Ábrelas las dos y lo verás en tres segundos: una te enseña código, la otra te enseña una interfaz bonita.
Cuando en este post digo "Radix", me refiero siempre a las Primitives.
Le pasa exactamente lo mismo a Adobe: React Aria son las piezas accesibles sin estilo, y React Spectrum es su design system ya vestido, el que usa Adobe en sus propios productos.
¿Y son solo para React?
Sí, las tres. Sin excepción.
React Aria lo lleva en el nombre. Base UI lo dice sin rodeos en su documentación: "Base UI is a React library. It is not designed to be used without React." Y Radix Primitives igual.
Fíjate además en quién está detrás de Base UI, porque explica bastante de lo que viene ahora: lo construye un equipo formado por gente de Radix, Floating UI y Material UI. Tres de los proyectos que más saben de esto en el ecosistema React, remando juntos.
Existen adaptaciones para Vue (Reka UI) y Svelte (Bits UI) inspiradas en Radix, pero son proyectos independientes con otros equipos, igual que pasaba con los ports de shadcn. Volveremos sobre esto en la Parte 8.
Conclusión práctica: cuando eliges shadcn/ui estás eligiendo React hasta el fondo del stack. No es solo la capa de componentes. Es también el motor de comportamiento que hay debajo.
Y ahora, la noticia
En mis notas para este post, escritas hace un timepo, había una sección titulada "¿Qué ocurriría si shadcn dejara de priorizar Radix?". Era un ejercicio teórico, de esos que uno plantea para ilustrar un riesgo.
Ya no hace falta imaginárselo.
En julio de 2026, Base UI pasó a ser la base por defecto de shadcn/ui.
Los proyectos nuevos que hagan npx shadcn init salen con Base UI. La documentación abre por la pestaña de Base UI. shadcn/create lo ofrece como opción principal.
Y no fue un capricho. Los datos son bastante contundentes:
- Base UI iba ya por la versión 1.6.0 con más de 6 millones de descargas semanales.
- Los usuarios de
shadcn/createya elegían Base UI sobre Radix por 2 a 1, antes del anuncio oficial. - Varios de los ingenieros que construyeron Radix están detrás de Base UI. En palabras del propio shadcn: "the same folks who built Radix are building something new".
Y no era la primera señal. En febrero de 2026 ya hubo un aviso que en su momento pareció puramente cosmético: la consolidación de todos los paquetes @radix-ui/react-* en un único paquete radix-ui. Movimiento razonable, sin drama. Con perspectiva, era el prólogo.
Y hay más. Ese mismo mes de julio de 2026, React Aria entró también como base de primera clase.
O sea que ya no estamos hablando de "Radix o Base UI". Estamos hablando de tres familias de primitivas conviviendo en el mismo catálogo, con tres contratos distintos, tres ciclos de release y tres formas de hacer las cosas.
Lo han hecho bien. En serio.
Aquí es donde este post podría ponerse alarmista y no lo va a hacer, porque sería injusto y porque le quitaría toda la credibilidad a lo que viene después.
shadcn ha gestionado este cambio muy bien:
Radix no está deprecado. Textualmente: "Radix is not being deprecated. We still support it, and every update and new component will ship for both libraries."
Los proyectos existentes no tienen que tocar absolutamente nada. Tu app sigue funcionando igual mañana que ayer.
Puedes seguir eligiendo Radix en proyectos nuevos con un flag (
-b radix).No hicieron un adaptador con cinta aislante: reescribieron todos los componentes para Base UI manteniendo la misma abstracción.
Hay guía de migración progresiva, componente a componente, con tu proyecto verde y desplegable en todo momento.
Y hasta una skill de IA que hace la migración y te genera un informe con los cambios y las diferencias de comportamiento.
Si algún día te toca liderar un cambio de este calibre en un proyecto con millones de usuarios, cópiales el planteamiento. Es difícil hacerlo mejor.
🔮 Y jugemos a la bruja lola
Aviso por delante, porque esto es importante: lo que viene ahora no lo ha dicho shadcn en ningún sitio, es una opinión personal. No hay anuncio, ni deprecación, ni una nota escondida en el changelog. Al contrario, lo que hay escrito es justo lo opuesto: "every update and new component will ship for both libraries".
Pero si estás tomando una decisión de arquitectura que va a durar cinco años, no puedes planificar solo con lo que está anunciado. Tienes que mirar también hacia dónde apunta la inercia.
Y mi apuesta es esta: a medio plazo, lo razonable es que los componentes nuevos dejen de salir para Radix.
No por deslealtad ni por un cambio de estrategia. Por aritmética:
Mantener tres implementaciones de cada componente es un impuesto permanente. Cada componente nuevo hay que escribirlo tres veces. Cada bug, arreglarlo tres veces. Cada página de documentación, tres pestañas. Cada test, por triplicado. Y esto no lo hace una multinacional con doscientos ingenieros: lo hace un equipo pequeño.
La brecha de adopción solo se va a ensanchar. Base UI ya ganaba 2 a 1 antes de ser el default. Ahora los proyectos nuevos salen con Base UI salvo que alguien escriba un flag a propósito. Dentro de año y medio, los usuarios de Radix en proyectos nuevos serán una minoría pequeña. Y en cuanto un catálogo pesa poco, el coste de mantenerlo deja de justificarse solo.
El talento se ha movido. Varios de los ingenieros que construyeron Radix están hoy construyendo Base UI. La energía del ecosistema está donde está la gente.
Y hay un precedente reciente en este mismo proyecto. La consolidación de paquetes de febrero de 2026 pareció puramente cosmética en su momento. Con perspectiva era el primer peldaño. Los cambios de este tipo casi nunca se anuncian de golpe: se anuncian en capas, y cada capa parece inofensiva por separado.
Y una observación general del código abierto que conviene tener presente: "seguimos dando soporte" y "seguimos invirtiendo" no son la misma frase. Lo primero suele significar que las cosas siguen funcionando y se corrigen los fallos gordos. Lo segundo es donde va la gente, las ideas y los componentes nuevos. La segunda categoría se mueve al default siempre.
Podría equivocarme, y estos son los argumentos en contra
Para ser justos, hay razones sólidas para pensar que Radix se queda mucho tiempo:
- La abstracción de shadcn es la misma para las tres bases, así que el coste de mantener Radix es más bajo de lo que parece desde fuera.
- La base instalada de Radix es gigantesca, y romper con ella dañaría la confianza que shadcn ha construido.
- El proyecto ha demostrado justo lo contrario de lo que temo: cuando pudo tomar el atajo, reescribió todo a mano.
Así que trátalo como lo que es: una apuesta razonada, no una predicción.
💡 Y lo que de verdad importa: qué haces tú con esto
Independientemente de si acierto o no, la consecuencia práctica es la misma y es lo único que deberías llevarte:
Si arrancas hoy un proyecto, elige Radix solo si tienes un motivo concreto — un componente de terceros que lo exija, un design system interno ya montado sobre él. La nostalgia y el "es lo que conozco" no son motivos suficientes cuando la corriente va tan clara en la otra dirección.
Si ya estás en Radix, no planifiques una migración porque sí. Y aquí hay que ser honesto y dar la posición oficial, que no puede ser más clara: "Radix is not being deprecated". El equipo va más lejos todavía — dicen literalmente que ellos mismos no están migrando: "You do not need to migrate. Radix is a mature, tested library. We still run it in production today and we're not migrating. If your app works, keep shipping."
Difícil discutirle eso a quien mantiene las dos bases. Así que lo que toca no es una fecha en el roadmap, sino estar preparado:
Documenta la dependencia. Que esté escrito en algún sitio qué parte de tu UI se apoya en Radix y por dónde.
No acoples tu aplicación a sus contratos. Es el argumento de los wrappers de la Parte 6, ahora con un motivo más: si tus pantallas hablan con tu API y no con la de la primitiva, cambiar de base es un trabajo acotado en lugar de una reescritura.
Mantén la migración como escenario de contingencia, no como plan.
Si la inversión del ecosistema acaba desplazándose claramente a Base UI, estarás en condiciones de decidir sin prisas. Y si no se desplaza, no habrás gastado ni una hora en prepararte para algo que no llegó — porque todo lo de esa lista es, de todos modos, buena arquitectura.
Y ese es exactamente el tipo de decisión que con una librería convencional nunca habrías tenido que tomar. Que es justo a donde quería llegar.
Y aun así, aquí está la hipoteca
Porque ahora compara los dos mundos.
Si hubieras usado MUI y MUI decidiera cambiar sus primitivas internas: ese es problema de MUI. Su equipo lo resuelve, lo prueba, lo publica. Tú subes de versión y sigues con tu vida. Puede que ni te enteres.
Con shadcn, ese mismo cambio es:
- Una migración en tu repositorio.
- Sobre ficheros que además tú has modificado, así que la guía oficial te sirve a medias.
- Que alguien de tu equipo tiene que hacer, revisar y probar.
- En un sprint que ya estaba lleno.
Y hacia delante hay una presión más sutil, que a mí me parece la peor de todas: el ecosistema se mueve al default. Los ejemplos nuevos, los bloques de terceros, las respuestas de Stack Overflow, los tutoriales y —muy especialmente— lo que te genere tu asistente de IA van a asumir Base UI.
Tú puedes quedarte en Radix perfectamente. Pero cada vez que pegues código de fuera, va a venir con el contrato equivocado. Y mezclar familias es incoherencia, bugs sutiles y coste de mantenimiento.
⚖️ Un momento, que con MUI tampoco vives en el paraíso
Y aquí tengo que frenar y ser justo, porque releyendo lo anterior parece que con una librería convencional no pagas nada. Y eso no es verdad ni de lejos.
Si vamos a hablar de hipotecas, hablemos también de lo que tiene de malo alquilar.
El casero decide cuándo se reforma el edificio. Los que sufrimos la migración de MUI v4 a v5 sabemos de qué hablo. Cambió el motor de estilos entero: de JSS a Emotion. No fue subir un número: fue reescribir cómo estaba estilada la aplicación. Meses de trabajo en aplicaciones grandes, sin aportar ni una funcionalidad nueva al usuario. Todavía hoy hay equipos anclados en v4 porque nunca encontraron el hueco.
Y ahí está la trampa: no elegiste tú el momento. Elegiste entre migrar cuando a ellos les vino bien, o quedarte atrás.
Y quedarte atrás también se paga. La versión vieja deja de recibir correcciones, no le llegan los componentes nuevos y, tarde o temprano, choca con la versión de React que necesitas para otra cosa. No es una opción estable: es una cuenta atrás.
Cuando el bug es suyo, tú esperas. Esta es la peor, y a mí es la que más rabia me ha dado en proyectos reales. Aparece un fallo en un componente, justo en la pantalla que le enseñas al cliente el jueves. Con shadcn abres el fichero y lo arreglas en veinte minutos. Con MUI abres una issue y esperas. ¿A qué? A que lo prioricen, lo arreglen y lo publiquen. Puede ser una semana o pueden ser seis meses.
¿Y mientras tanto? Tus opciones son todas feas:
- Un apaño con
sxy selectores CSS peleándote contra las tripas del componente. patch-package, o sea parchearnode_modulesy rezar en cada actualización.- O copiarte el código del componente fuera de
node_modulesy mantenerlo tú.
Fíjate en esa última. Es exactamente el modelo de shadcn... pero hecho a la fuerza, a destiempo, sin bendición del proyecto y sin ninguna herramienta que te ayude. Lo peor de los dos mundos.
Y el ritmo lo marcan ellos. Material UI pasó directamente de la v7 a la v9, sin v8, para alinear numeración con MUI X. Es una decisión perfectamente razonable por su parte. Pero ilustra la idea: el calendario, la numeración y las prioridades no son tuyos.
Y en lo comercial, la letra pequeña también existe. La edición Community es MIT y gratis para siempre, sin truco. Pero en Pro y Premium conviene leerse las condiciones: la licencia anual funciona "forever in production" con las versiones publicadas durante tu periodo... pero cuando ese periodo termina, no puedes seguir usando esas versiones en desarrollo. Es decir: lo que ya está desplegado sigue vivo, pero tu equipo no puede seguir trabajando sobre ello sin renovar.
Nada de esto es abusivo, que conste. Es un negocio y es transparente. Pero es una dependencia más, y de las que se pagan con presupuesto anual.
🏠 Entonces, ¿cuál es la diferencia de verdad?
Que las dos opciones tienen hipoteca. Lo que cambia es cómo se paga:
| shadcn/ui | MUI y similares | |
|---|---|---|
| Cuándo pagas | Un poco, continuamente | Casi nada... hasta la derrama |
| Quién elige el momento | Tú | Ellos |
| Un bug en un componente | Lo arreglas hoy | Lo reportas y esperas |
| Una funcionalidad que falta | La escribes | La pides, o pagas el plan superior |
| Componentes ricos | Los construyes o los compras | Vienen hechos |
| El día que cambia todo | Migración progresiva, a tu ritmo | Una versión mayor, cuando toque |
| Coste en dinero | El core es gratis; el ecosistema, no siempre | Gratis en Community, licencia en Pro/Premium |
| Coste en gente | Continuo y tuyo | Puntual, pero concentrado y sin avisar |
💰 Ojo, que "shadcn es gratis" también tiene matices
Conviene aclarar esa fila, porque el "gratis" de shadcn se repite mucho y no es toda la historia.
El proyecto oficial sí lo es, sin truco: licencia MIT, todos los componentes, para siempre. Ahí no hay plan Pro ni funcionalidad capada.
Pero alrededor ha crecido un ecosistema comercial considerable, y es justo donde acabas mirando en cuanto necesitas ir más allá de un botón:
Colecciones de bloques y plantillas de pago. Shadcnblocks vende miles de bloques y plantillas en una horquilla de unos 49 a 199 dólares, con plantillas sueltas alrededor de 79. Otras como shadcn-ui-blocks van con modelo gratis + Pro.
Librerías con versión Pro. Aceternity y Magic UI regalan buena parte del catálogo y cobran por el acceso completo y las plantillas.
Marketplaces. 21st.dev funciona como plataforma comunitaria donde se publican componentes compatibles, con registry privado para bloques comerciales.
Y en la órbita Tailwind, Tailwind Plus (lo que era Tailwind UI) lleva años siendo de pago.
⚠️ Cuidado con una confusión que se está dando mucho: varios de estos sitios usan "shadcn" en su nombre o dominio y no tienen relación con el proyecto oficial. Son negocios de terceros construidos sobre el formato de registry, que es abierto justamente para eso. Ninguno habla en nombre de shadcn/ui.
Y aquí está la vuelta de tuerca
Ahora fíjate en la diferencia entre pagar en un sitio y pagar en el otro, porque no es la misma compra:
Cuando pagas MUI X Pro, compras un componente que ellos mantienen. El dinero te compra actualizaciones, correcciones y soporte durante la vigencia de la licencia.
Cuando compras un bloque premium de shadcn, compras código que se copia en tu repositorio. Y a partir de ese momento... es tuyo. Con su mantenimiento, sus actualizaciones y sus bugs.
O sea que en el peor de los casos pagas las dos monedas: dinero por adelantado y mantenimiento después.
No digo que sea mala compra —ahorrarte tres semanas de maquetación por 99 dólares suele salir muy a cuenta—, pero conviene saber exactamente qué estás comprando: estás comprando un punto de partida, no un producto mantenido.
🔌 ¿A alguien más le ha venido a la cabeza WordPress?
Porque a mí sí, y no es una comparación gratuita.
Catálogo enorme de código de terceros, calidad variabilísima, se instala en un clic, y en cuanto te descuidas tienes el proyecto lleno de piezas que nadie sabe muy bien de dónde salieron. Ese patrón lo hemos vivido todos, y no acabó bien.
Y hay una parte de la analogía que se cumple tal cual:
La calidad es una lotería. Entre esos miles de bloques hay trabajo excelente y hay cosas hechas en una tarde con la accesibilidad de adorno.
El vendedor puede desaparecer. Un proyecto de una persona que deja de mantenerlo, lo abandona o lo vende. Pasa constantemente.
La enfermedad de los 47 plugins tiene versión frontend. Vas tirando de seis registries distintos, cada uno con sus convenciones, sus dependencias y su idea de cómo se hace un formulario. Y acabas con un Frankenstein que parece homogéneo porque todo usa los mismos colores.
Así que sí, el miedo tiene fundamento. Pero ahora la buena noticia, que es más grande de lo que parece.
Dónde se rompe la analogía (y menos mal)
La diferencia es de naturaleza, no de grado:
Un plugin de WordPress se ejecuta en tu servidor. Es código vivo, con acceso a tu base de datos y a tus ficheros, corriendo en producción. Por eso el mayor agujero de seguridad de WordPress nunca fue el core: fueron los plugins.
Un bloque de shadcn es código fuente que entra por un Pull Request. Se compila en tu build, lo revisa una persona, pasa por tu linter y tus tests. Es texto que puedes leer antes de aceptarlo.
Un plugin de WordPress con las actualizaciones automáticas activadas te puede dejar con el culete al aire. Te acuestas con una versión y te levantas con otra que ha roto el checkout.
Un bloque que copiaste no cambia nunca. Ni para bien ni para mal. Lo que hay en tu repo hoy es lo que habrá dentro de tres años.
Y si el vendedor cierra la persiana, en WordPress te quedas con código sin parchear ejecutándose en producción. Con shadcn te quedas con... lo que ya tenías. Tu aplicación ni se entera.
De hecho, fíjate en la ironía:
Con shadcn, el escenario de "el proveedor abandona el producto" ya te ha ocurrido el día uno, por diseño. No puede pillarte por sorpresa, porque el mantenimiento era tuyo desde que ejecutaste
add.
Suena a chiste, pero es una ventaja real de este modelo.
⚠️ Aun así, dos disciplinas que no son opcionales
Que la analogía se rompa no significa que puedas ir a ciegas. Hay un momento crítico, y es el instante de instalar:
Un registry de terceros puede declarar las dependencias npm que quiera. El CLI las instala. Ese es el vector de verdad, y es el mismo problema de cadena de suministro que ya tienes con cualquier npm install — pero conviene tenerlo presente en vez de descubrirlo.
Por eso, dos reglas para cualquier registry que no sea el oficial ni el vuestro:
shadcn viewantes deshadcn add. Léelo. Son cien líneas de TSX, no un binario.- Mira qué dependencias arrastra antes de aceptar el PR.
Es exactamente la misma disciplina que aplicarías a cualquier dependencia nueva. La diferencia —y esta vez a favor— es que aquí puedes leerlo todo, porque es tu código desde el minuto uno.
Resumiendo la diferencia
Con shadcn pagas cuotas pequeñas todos los meses: cada add, cada revisión, cada componente que mantienes. Y de vez en cuando, algún pago suelto por bloques o plantillas.
Con MUI no pagas casi nada durante tres años... y un día llega la derrama. Una versión mayor que rompe, un bug que no puedes arreglar, una funcionalidad que no está en el roadmap de la librería.
Ninguna de las dos es gratis. Una te cobra en cuotas y la otra en derramas.
Y elegir bien no consiste en buscar la opción sin coste, porque no existe. Consiste en saber cuál de las dos formas de pagar encaja mejor con tu equipo, tu producto y tu calendario. Un equipo pequeño sin capacidad de mantenimiento sobrevive mejor a una derrama cada tres años que a una cuota mensual perpetua. Un equipo de plataforma con gente dedicada prefiere la cuota, porque a cambio controla el resultado.
Que es, otra vez, la pregunta de siempre: qué parte del coste pagas ahora y qué parte dejas para el futuro.
🎯 La conclusión de esta parte
Si te llevas una sola frase de aquí, que sea esta:
Elegir Radix, Base UI o React Aria no es una opción del instalador. Es una decisión de arquitectura.
Elígela a conciencia. Documéntala en el README. No la mezcles sin una razón muy clara. Y asume que dentro de dos años puede que tengas que revisarla, y que ese día el trabajo será tuyo.
Eso es la cuota. Ha llegado puntual, bien envuelta y con instrucciones. Pero ha llegado.
Parte 6: Tailwind por debajo, y hasta dónde llega el className
En resumen shadcn estila con utilidades de Tailwind y tokens en variables CSS. Cambias cuatro variables, cambia el producto entero. La función
cn()no es magia de la cascada: esclsx+tailwind-mergeresolviendo strings en JavaScript. Personalizar un botón es trivial. Personalizar un componente rico, no tanto.
📎 Antes de seguir: todo lo que viene ahora da por supuesto que sabes leer una clase de Tailwind. Si te suena a chino, para aquí y pásate por Tailwind: esto no es Bootstrap. Te explica el modelo de utilidades, las capas y el tema, que es justo el terreno sobre el que shadcn construye. Vuelve luego y esta parte te va a costar la mitad entenderla.
Cómo está escrito un componente
Un botón de shadcn, simplificado, es esto:
function Button({ className, ...props }: React.ComponentProps<"button">) {
return (
<button
className={cn(
"inline-flex items-center rounded-md bg-primary px-4 py-2 text-primary-foreground",
className,
)}
{...props}
/>
);
}Dos detalles que lo explican casi todo:
El className va al final. Eso es deliberado, y es lo que permite que quien use el componente pueda sobreescribir estilos desde fuera.
El estilo vive en el JSX. No hay un button.css, ni una clase .btn en @layer components. Las utilidades están ahí, a la vista, en el propio marcado. Con lo bueno (lo lees y lo cambias sin buscar en ningún sitio) y lo malo (el churro de clases).
cn() desmitificada
Ya la vimos en la Parte 1. Ahora toca entender qué hace:
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}Dos librerías trabajando en cadena:
clsx construye la cadena de clases a partir de strings, condiciones y objetos. Lo de toda la vida.
tailwind-merge hace lo interesante: detecta qué utilidades de Tailwind pelean por la misma propiedad y se queda con la última.
Con lo cual esto funciona:
<Button className="bg-green-600">Comprar</Button>El botón traía bg-primary. Tú pasas bg-green-600. tailwind-merge ve que ambas son del grupo "color de fondo", tira la primera y deja la segunda. Sale un botón verde.
⚠️ Y aquí el matiz que casi nadie explica bien:
Esto no es la cascada CSS. No es especificidad, no es orden de declaración, no es
@layer. Es resolución de strings en JavaScript, en tiempo de ejecución, antes de escribir el atributoclass.
¿Por qué importa la diferencia? Porque tiene consecuencias prácticas:
- Los valores arbitrarios corrientes funcionan bien. Que no cunda el pánico:
cn("bg-primary", "bg-[#00ff00]")resuelve perfectamente. La librería reconoce las utilidades estándar y buena parte de los valores arbitrarios. - Donde aparecen los problemas es en lo ambiguo. Cuando un prefijo puede significar dos propiedades distintas, la librería no puede adivinar: con
bg-[30%_30%]no sabe si le estás dando unbackground-positiono unbackground-size, y confont-[...]no distinguefont-weightdefont-family. La solución existe y es etiquetar el valor —bg-[position:30%_30%],font-[family-name:...]— pero hay que conocerla. - Y en tus utilidades personalizadas.
tailwind-mergefunciona con heurísticas sobre los nombres, no leyendo tu configuración de Tailwind. Si tienes utilidades propias o prefijos que no encajan en sus patrones, no las clasifica bien: o no resuelve el conflicto —y acabas con dos clases contradictorias en el DOM peleándose por especificidad de verdad— o lo resuelve donde no debía. Se arregla adaptando su configuración conextendTailwindMerge, que es justo el paso que nadie da hasta que algo falla. - Tampoco resuelve propiedades arbitrarias contra su utilidad equivalente, y es deliberado: lo dicen en sus limitaciones, para no engordar el bundle.
- Y todo esto tiene un coste, pequeño pero real, en cada render.
No es un problema. Es una pieza más que conviene saber que está ahí, porque el día que un estilo no se aplique y te vuelvas loco mirando el DevTools, la respuesta puede estar en JavaScript y no en CSS.
Lo mejor de shadcn: el tema
Y ahora vamos a por una de las cosas que shadcn hace muy muy bien.
Los componentes no usan colores concretos. Usan tokens semánticos definidos como variables CSS:
:root {
--primary: ...;
--primary-foreground: ...;
--background: ...;
--foreground: ...;
--muted: ...;
--muted-foreground: ...;
--destructive: ...;
--border: ...;
}
.dark {
--primary: ...;
--background: ...;
/* ... */
}Y en el marcado consumen bg-primary, text-muted-foreground, border-border.
¿Qué significa esto en la práctica? Que cambias un puñado de variables y cambia el producto entero. Sin tocar un solo componente. Y que el modo oscuro te sale prácticamente gratis, porque los componentes nunca hablan de colores, hablan de intenciones.
Esto está muy bien pensado, es un patrón que merece la pena copiar aunque no uses shadcn, y conecta directamente con lo que contábamos en el post de Tailwind sobre tokens propios. → documentación de theming
Y ahora el techo
Hasta aquí, todo maravilloso. <Button className="..."> se personaliza de fábula.
El problema aparece cuando el componente tiene tripas.
Un diálogo de confirmación no es un único elemento configurable. Es una composición de varias piezas:
- Disparador
- Contenedor
- Cabecera
- Título
- Descripción
- Pie
- Botón de cancelar
- Botón de confirmar
shadcn te entrega esas piezas, pero no una abstracción de alto nivel del tipo:
<ConfirmDialog
title="¿Eliminar elemento?"
description="Esta operación no se puede deshacer."
confirmText="Eliminar"
confirmVariant="destructive"
footerClassName="bg-muted"
/>Para personalizar una instancia concreta, el camino que propone de serie es componer explícitamente su estructura:
<AlertDialog>
<AlertDialogTrigger asChild>
<Button>Eliminar</Button>
</AlertDialogTrigger>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>¿Eliminar elemento?</AlertDialogTitle>
<AlertDialogDescription>
Esta operación no se puede deshacer.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter className="bg-muted">
<AlertDialogCancel>Cancelar</AlertDialogCancel>
<AlertDialogAction className="bg-destructive">
Eliminar
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>Sí, es composición y resulta flexible. Pero seamos honestos sobre el precio: para cambiar el aspecto de una pieza tienes que expresar buena parte de la estructura del diálogo.
Evidentemente, no deberías repetir esto en las treinta pantallas donde confirmas una operación. Lo razonable es construir tu propio ConfirmDialog, encapsulando también el estado de carga, la llamada asíncrona, los errores, los permisos y la telemetría.
Y ese es precisamente el punto: shadcn te proporciona las piezas; la abstracción reutilizable, su API y su mantenimiento corren por tu cuenta.
La comparación honesta con MUI
Una librería como MUI resuelve esto con un contrato uniforme: props, tema, slots y slotProps. Puedes meter mano en las partes internas de un componente rico sin reconstruir su composición entera. Un slotProps={{ paper: { sx: ... } }} y listo.
En shadcn no existe una API universal de slots. Y esto es importante entenderlo bien:
- Unos componentes aceptan
classNamey ya. - Otros exponen subcomponentes que montas tú.
- Otros delegan el contrato de personalización en la librería que llevan debajo (y entonces tienes que aprenderte la API de Base UI, o de DayPicker, o de Recharts).
La experiencia no es homogénea. Otra vez la misma historia: parecen hermanos, son primos.
La salida buena (y guarda esta frase)
¿Cuál es la solución mantenible? Un wrapper propio:
type ConfirmDialogProps = {
title: string;
description: string;
confirmLabel?: string;
variant?: "danger" | "default";
onConfirm: () => Promise<void>;
};
export const ConfirmDialog = ({ title, description, ... }: ConfirmDialogProps) => {
// Aquí dentro: la composición completa, el estado de carga,
// la gestión de errores y la telemetría. Una vez. En un sitio.
};Ahora tus treinta pantallas usan <ConfirmDialog> con cuatro props. La estructura y la lógica viven en un único fichero. Si mañana cambia el diseño, lo cambias una vez.
Es la decisión correcta. Sin discusión.
Pero fíjate bien en lo que acabas de hacer:
Has empezado a construir tu propia librería de componentes encima de shadcn.
Guarda esa frase. Vuelve en la Parte 11, y es la clave de todo el post.
💻 Tienes el mismo diálogo escrito de las dos maneras —con wrapper y sin él, uno al lado del otro— en la demo
03-wrapper-propio. Compara cuánto código necesita cada pantalla.
Parte 7: La tabla — donde la hipoteca se nota de verdad
En resumen La "tabla" de shadcn son los elementos visuales, más ejemplos de cómo casarlos con TanStack Table. TanStack es un motor headless extraordinario, pero no te da un grid terminado. Si tu aplicación necesita un data grid potente, esta es la cuota más cara del préstamo. Con diferencia.
Si hay un sitio donde he visto a más equipos pegarse el batacazo, es este.
Lo que crees que estás instalando
Vas a la web, ves la sección "Data Table", y hay una tabla preciosa con ordenación, filtros, paginación, selección de filas y menú de columnas. Piensas: estupendo, tengo el grid resuelto.
Lo que estás instalando en realidad
Los elementos visuales de una tabla (<Table>, <TableHeader>, <TableRow>, <TableCell>... básicamente HTML con estilo) más un ejemplo muy bien hecho de cómo combinarlos con TanStack Table.
Y TanStack Table es una librería extraordinaria. Es headless: te da el motor y ni una línea de interfaz.
Lo que TanStack te resuelve, y no es poco: modelo de filas y columnas, ordenación, filtrado, paginación, selección, agrupación, visibilidad de columnas, expansión de filas. El núcleo lógico de un grid, muy bien diseñado y muy bien mantenido.
Lo que tienes que construir tú:
- Edición inline
- Validación y persistencia de esa edición
- Reordenación de columnas por drag & drop
- Redimensionado de columnas
- Columnas fijadas a izquierda y derecha
- Virtualización (o tu tabla de 10.000 filas mata el navegador)
- Agregaciones y totales
- Navegación por teclado tipo hoja de cálculo
- Menús contextuales
- Exportación a Excel / CSV
- Estados de carga, vacío y error
- Accesibilidad (roles ARIA de grid,
aria-sort, anuncios a lectores de pantalla) - Densidad configurable
- Comportamiento responsive
Cada línea de esa lista es entre medio día y tres semanas de trabajo (por poner una cifra). Y algunas —virtualización combinada con columnas fijadas y filas expandibles, por ejemplo— son de esas que te comen un mes y te dejan un bug sutil para siempre.
⚖️ Ojo, que en MUI tampoco es todo gratis
Y ahora la parte que evita que este post parezca un panfleto pro-MUI, porque la comparación honesta tiene matices.
En MUI X Data Grid tampoco tienes todo incluido. La edición Community ya trae un grid muy digno —edición, ordenación, filtrado, paginación, selección, una API coherente— pero las capacidades avanzadas están repartidas entre los planes Pro y Premium. El agrupamiento real de filas, por ejemplo, está en Premium.
O sea que si necesitas lo gordo, igual te toca pasar por caja.
Pero la diferencia no está en el precio. Está en la naturaleza de la cosa:
Con MUI X tienes un producto. Con contratos documentados, actualizaciones coordinadas, un equipo que lo mantiene, tests de regresión que no has escrito tú y un roadmap. Pagas con dinero, por un producto y un mantenimiento.
Con shadcn + TanStack tienes las piezas para construir ese producto. Pagas con el tiempo de tus mejores desarrolladores durante varios meses, y luego con su mantenimiento para siempre. Y esa moneda no aparece en ninguna partida presupuestaria, pero se gasta igual.
| MUI X Data Grid | shadcn + TanStack Table |
|---|---|
| Un componente terminado | Un conjunto de piezas para construirlo |
| API y slots documentados | La API que diseñe tu equipo |
| Edición integrada | Estado, editor, validación y persistencia por conectar |
| Actualizaciones centralizadas | Código copiado y probablemente modificado |
| Comportamiento coordinado | Integración de varias librerías |
| Menos libertad interna | Control total y responsabilidad total |
| Cuesta dinero | Cuesta meses |
Las salidas prácticas
Que no cunda el pánico, que hay opciones intermedias muy razonables:
Proyectos que ya han hecho el trabajo. En el ecosistema hay iniciativas como ReUI que combinan la estética shadcn con TanStack Table, TanStack Virtual y drag & drop ya integrados. Te ahorran muchísimo. Eso sí, mantienen la misma filosofía de copiar el código, así que la hipoteca no desaparece: cambias de banco.
Grids especializados para lo que lo merezca. AG Grid, MUI X, React Data Grid. Productos maduros para problemas duros.
⚠️ Pero ojo, que estos no vienen integrados
Y aquí conviene ser muy claro, porque es el coste que nadie te cuenta cuando te recomiendan "pues métele AG Grid y listo":
Estos grids no saben nada de shadcn. No leen tus variables CSS, no conocen tu tema, no comparten tus tokens. Traen su propio sistema de diseño, y si los enchufas tal cual vas a tener un cuadrado con aspecto de otra aplicación en medio de tu pantalla.
Integrarlos es trabajo. Poco o mucho según cuál elijas:
Con AG Grid la cosa es bastante llevadera. Su Theming API va por parámetros de tema y variables CSS, así que puedes mapear tus tokens de shadcn sobre los suyos — colores, bordes, tipografía, densidad — y dejarlo razonablemente en casa. Incluso tiene una opción para meter sus estilos en una capa CSS (themeCssLayer), que si vienes del post de Tailwind sabes exactamente por qué importa: es lo que evita que sus estilos y los tuyos se peleen por especificidad.
Aun así es una tarde de trabajo, más los repasos. Y hay cosas que no vas a igualar del todo sin pelearte: los anillos de foco, los iconos, las barras de desplazamiento, los desplegables internos del propio grid.
Con MUI X el peaje es mayor, porque no te llevas solo el grid: te llevas el ThemeProvider de MUI y su motor de estilos. Acabas con dos sistemas de diseño y dos motores de estilado conviviendo en la misma aplicación. Funciona —lo hemos hecho— pero hay que asumir el bundle, la doble configuración de modo oscuro y la doble curva de aprendizaje del equipo.
💡 Y el consejo práctico: no intentes que sea idéntico
Este es el error que veo repetirse: alguien se empeña en que el grid quede pixel a pixel como el resto de la aplicación, y se van tres semanas persiguiendo un borde.
No merece la pena. Iguala lo que se nota de verdad:
- Colores y modo oscuro. Que no cambie la paleta.
- Tipografía y tamaños. Que sea la misma familia.
- Radios y densidad. Que respire igual.
- Estados de foco visibles. Por accesibilidad, no por estética.
Y olvídate del resto. Los usuarios de una pantalla de datos densa aceptan perfectamente que la zona del grid tenga su propia personalidad — de hecho muchos lo agradecen, porque señala "aquí se trabaja". Lo que no aceptan es que sea lento, que no puedan navegar con el teclado o que se rompa al filtrar.
Que es justo lo contrario de donde suele irse el presupuesto.
💡 El consejo que más me gusta de esta parte
Y aquí va algo que se dice poco porque el discurso tecnológico tiende a ser tribal:
No todo tu producto tiene que ser de la misma familia visual.
shadcn para el portal del cliente, un grid especializado para el módulo de intranet/backoffice. Es una arquitectura perfectamente sana, siempre que el tema esté bien integrado y el usuario no note el salto.
Si tu aplicación ya está montada sobre MUI, sustituir su Data Grid por una tabla estilo shadcn rara vez compensa. Y si tu portal público va con shadcn, meter AG Grid en el backoffice no te convierte en un hereje.
Nadie te va a dar un premio por la pureza de tu stack. Te lo van a dar por entregar.
💻 La tienes montada en la demo
04-tabla-tanstack: tabla de shadcn + TanStack Table v9 con ordenación, filtrado y paginación funcionando, y en la propia pantalla las dos listas enfrentadas — lo que te dan hecho y lo que construyes tú.
Parte 8: ¿Esto es solo para React?
En resumen shadcn/ui es React. Punto. Que haya guía para Astro no significa que sean Web Components: significa que metes React en las islas. Los ports para Vue y Svelte existen, pero son proyectos distintos con equipos distintos.
Parte corta, pero es de las dudas que más se buscan y donde el marketing genera más confusión.
shadcn/ui es React
El proyecto original vive en el ecosistema React. Sus componentes están escritos en React y sus primitivas también. No hay nada agnóstico aquí, por mucho que la documentación tenga guías para otros entornos.
Astro: sí, pero sabiendo qué compras
Puedes usar shadcn en Astro. La guía oficial existe y funciona.
Y no hay que rebuscar mucho para ver qué está pasando: está en el propio comando de instalación que te dan.
pnpm create astro@latest astro-app -- --template with-tailwindcss --install --add react --gitEse --add react es toda la respuesta. Lo primero que hace la guía de Astro es instalar la integración de React.
A partir de ahí, los componentes de shadcn entran por el modelo con el que Astro trabaja con otros frameworks: islas. Y merece la pena citar cómo lo describe su propia documentación, porque es muy explícito y va a favor de Astro:
"By default, Astro will automatically render every UI component to just HTML & CSS, stripping out all client-side JavaScript automatically."
"In Astro, it's up to you as the developer to explicitly tell Astro which components on the page need to also run in the browser."
Es decir: Astro no te manda JavaScript salvo que tú se lo pidas. Y se lo pides con las directivas client: — client:load, client:visible, client:idle.
Así que seamos precisos, porque el matiz importa: un componente de shadcn que no hidrates no te manda React al navegador. Un Card, un Badge, un Alert o un Separator se renderizan en el servidor y llegan como HTML y CSS. Ahí no hay coste de runtime, y Astro cumple exactamente lo que promete.
Y con el Button se ve perfectamente dónde está la frontera, porque es el caso límite. El componente en sí no tiene estado ni hooks: es un <button> con clases, y el servidor lo pinta sin problema. Pero un botón existe para que lo pulses. Si lo que quieres es un onClick, necesitas hidratar. Solo sale gratis en dos formas concretas: cuando en realidad es un enlace disfrazado de botón (con asChild sobre un <a href>, el típico CTA) o cuando es el submit nativo de un <form>, que funciona con cero JavaScript. En cualquier otro caso, ese botón se trae React consigo.
Pero en cuanto pones una directiva client: en un componente, ese componente se lleva consigo el runtime de React al navegador en esa isla.
Y aquí está lo que de verdad conviene pensar antes de elegir la combinación: ¿por qué ibas a traerte shadcn a Astro? Casi nunca por las tarjetas y los badges — eso lo resuelves con Tailwind y cuatro clases. Te lo traes por el Dialog, el Select, el Combobox, el Popover, el Accordion. Justo el catálogo que no funciona sin hidratar. La parte de shadcn que no cuesta nada en Astro es la que menos necesitabas, y la que necesitabas es la que hidrata.
Y puede ser una decisión perfectamente correcta. Un configurador complejo, un formulario multipaso, un buscador con filtros: son islas que justifican de sobra el coste. Para eso están las islas, literalmente.
Lo engañoso sería presentarlo como "soporte nativo multiframework" sin explicar qué pasa por debajo.
Porque si elegiste Astro precisamente para no meter React DOM y servir 20 KB de JavaScript, y acabas hidratando islas para un acordeón y un desplegable —cosas que <details> y <dialog> te dan de serie— has cerrado el círculo hacia atrás.
Para lo sencillo hay alternativas mejores. daisyUI, que ya salió en el post de Tailwind, te da botones, tarjetas y alertas como clases CSS sobre Tailwind, sin runtime de ningún framework. Para los componentes que necesiten comportamiento tendrás que poner tú el JavaScript, o tirar de elementos nativos como <dialog> y <details>, que en 2026 ya dan mucha guerra. Pero no metes React de tapadillo.
Laravel: es React con otro sombrero
Laravel aparece en la documentación porque puede servir de backend con React en el frontend, vía Inertia o los starter kits oficiales.
Ahí shadcn funciona perfectamente, claro. Pero no porque shadcn hable PHP sino porque el frontend sigue siendo React. La distinción parece obvia escrita así, pero he visto a gente sorprenderse.
Vue, Svelte y compañía: familiares lejanos
Existen ports muy dignos: shadcn-vue, shadcn-svelte y algunos más. Reproducen la filosofía, la estética y buena parte del catálogo.
Pero seamos claros sobre lo que son: implementaciones diferentes, con equipos diferentes, contratos diferentes y ritmos de actualización diferentes.
No es una librería universal con varios adaptadores. Son proyectos independientes inspirados en el original.
Y esto tiene consecuencias muy concretas para tu hipoteca:
- Cuando shadcn/ui saca un componente nuevo, el port puede tardar en llegar o incluso no llegar.
- Cuando shadcn/ui cambia de base por defecto (¿te suena?), el port decide por su cuenta qué hacer.
- Cuando buscas ayuda en Google, el 90% de lo que encuentres será de la versión React y no te servirá tal cual.
- Y la comunidad, el ecosistema de bloques y los registries de terceros están la mayoría en React.
Son buenos proyectos y mucha gente los usa en producción con éxito. Solo ten presente que el tipo de interés de esa hipoteca es más alto, porque dependes de un equipo más pequeño que además va a remolque de decisiones que toma otro.
Parte 9: shadcn y la IA (o por qué esto se ha comido el mundo)
En resumen El código está en tu repo, en texto plano: tu asistente lo lee, lo entiende y lo modifica. Eso es una ventaja brutal frente a una API de librería. Por eso la IA genera shadcn por defecto, se lo pidas o no. Y por eso mismo la deuda se acumula más rápido que nunca.
Si has llegado hasta aquí preguntándote "vale, pero ¿por qué esto ha arrasado tantísimo?", esta es la parte que lo explica.
Hay muchas razones: llegó en el momento justo, tiene un gusto exquisito, encaja como un guante con Tailwind, la documentación es magnífica. Todo eso cuenta.
Pero hay una razón que pesa más que todas las demás juntas, y que casi nadie menciona.
Por qué tu asistente adora shadcn
Ponte en la piel de un modelo de lenguaje al que le pides "añade un estado de carga a este diálogo".
Con MUI, el asistente trabaja contra una API pública: props, tipos, slots, slotProps, tema y documentación. Tiene que:
- Conocer la API exacta de ese componente en tu versión concreta,
- Saber si esa prop existe o si la renombraron en la v6,
- Entender cómo funciona el sistema de temas.
Y aquí conviene no exagerar, porque es un argumento que se cae solo si lo estiras: MUI no es una caja negra. Es open source. El asistente puede leer los tipos instalados, el código de node_modules, el repositorio y la documentación de tu versión. Puede mirar dentro perfectamente.
La diferencia está en otro sitio: ese código no forma parte de tu aplicación y no está pensado para que lo modifiques. Vive en node_modules, que es territorio desechable — lo que toques ahí desaparece en el siguiente npm install. Así que el asistente puede leerlo para entender, pero para actuar sigue teniendo que pasar por la API que MUI expone, adivinando qué prop hace lo que necesitas. Por eso alucina props que no existen con una alegría notable: no es que no pueda ver: es que el camino para hacer cambios está mediado por una capa diseñada precisamente para ocultar el interior.
Con shadcn, la implementación concreta que usa tu aplicación está en tu repositorio, junto a vuestros cambios. El asistente:
- abre
components/ui/dialog.tsx, - lee exactamente la versión que tienes y cómo la habéis modificado,
- ve qué contrato expone ahora,
- lo modifica.
Ya está. No hay API intermedia que acertar, no hay versión que adivinar, no hay que reconstruir mentalmente qué habéis personalizado. El código está delante de sus narices, en texto plano, en tu repositorio — y modificarlo es el uso previsto, no un parche que se pierde.
La frontera entre "usar un componente" y "editar un componente" simplemente desaparece.
Esta es una ventaja real y objetiva, no un argumento de marketing. Si trabajas con asistentes de IA a diario —y en 2026 eso es casi todo el mundo— la diferencia de productividad es enorme y se nota desde el primer día.
Y el proyecto ha remado en esa dirección
No ha sido casualidad. shadcn se ha posicionado deliberadamente como la capa de UI de la era de los agentes:
Servidor MCP oficial desde agosto de 2025, junto con el CLI 3.0: tu asistente puede navegar, buscar e instalar componentes de cualquier registry hablando en lenguaje natural. "Añade el botón, el diálogo y la tarjeta" y lo hace.
Registry pensado para máquinas: la propia documentación dice que puedes usar el esquema para distribuir componentes "or have AI generate completely new components". El formato del registry item es un JSON plano, deliberadamente fácil de leer y de escribir por una máquina.
Búsqueda dinámica en el registry (julio de 2026): los catálogos grandes pueden filtrar en el servidor en lugar de mandarle el catálogo entero al CLI. Es decir, pensado para que un agente encuentre la aguja en un pajar de miles de componentes.
Una skill oficial de shadcn/ui que le da a tu asistente el contexto de tu proyecto: framework, versión de Tailwind, alias, qué base de primitivas usas y qué componentes tienes instalados. Se añade con
pnpm dlx skills add shadcn/ui.Y una skill específica para la migración a Base UI, componente a componente, generando un informe de cambios y diferencias de comportamiento.
Mientras las librerías tradicionales seguían optimizando para desarrolladores humanos leyendo documentación, shadcn optimizó para agentes leyendo código.
Visto con perspectiva, es una jugada maestra.
🚨 Y ahora la vuelta de tuerca
Aquí es donde toca aplicar la tesis del post a todo esto que acabo de elogiar.
El mismo rasgo que hace que la IA trabaje tan bien con shadcn —que todo es tuyo y editable— es el que hace que la deuda se acumule a toda velocidad.
Piénsalo despacio:
Antes, para dejar tus componentes base personalizados hacían falta seis meses y varias personas tocando. Había fricción natural: alguien tenía que abrir el fichero, entenderlo, decidir el cambio. Esa fricción actuaba de freno.
Ahora, un agente puede reescribir tus doce componentes base en dos tardes. Sin fricción. Con muy buena letra, además, y pasando todos los tests.
Y el mes que viene ninguno de esos componentes se parecerá lo suficiente al original como para poder aplicarle una actualización del registry.
La velocidad es real. La deuda también. Y ahora las dos van a la par.
💎 Qué hacer con esto
No es un argumento para no usar IA, obviamente. Es un argumento para poner raíles:
Traza una frontera clara y hazla explícita. Qué carpeta contiene "lo que baja del registry" y qué carpeta contiene "lo nuestro". components/ui/ es territorio de shadcn; components/ es tuyo. Y esa frontera va escrita en el README y en las instrucciones de tu asistente.
Los componentes base se tocan poco y a conciencia. Cada modificación en ui/ es una decisión de arquitectura que alguien revisa, no un -- fix rápido de una IA a las once de la noche.
Todo lo demás va en wrappers. Que es donde la IA puede correr todo lo que quiera, porque ahí el código ya es tuyo por diseño y no hay ninguna actualización futura que te vaya a pisar.
Y tests. Si vas a permitir que una máquina modifique tus componentes base, los tests dejan de ser una buena práctica y pasan a ser el único mecanismo que te queda para saber si algo sigue funcionando.
Es importante tener claro este principio: la IA escribe rápido; el criterio sigue siendo cosa tuya.
Parte 10: ¿Te conviene hipotecarte?
En resumen shadcn brilla en interfaz de producto con identidad visual propia. Sufre en aplicación de gestión densa, donde los componentes aburridos son los caros. Y el escenario peligroso no es ninguno de los dos: es el de en medio.
Vamos a lo práctico. ¿Para qué proyectos es una elección magnífica y para cuáles puede ser un error caro?
✅ Donde encaja de maravilla
- Portales de viajes
- Compra de entradas y reservas
- Comercio electrónico
- Landing pages y sitios de marketing
- Áreas de cliente
- Procesos de contratación y onboarding
- Formularios y asistentes por pasos
- SaaS con interfaz de complejidad moderada
- Prototipos y MVP
El patrón común: productos donde abundan botones, tarjetas, formularios, desplegables y diálogos, y donde quieres identidad visual propia.
Aquí shadcn es sencillamente estupendo. Te da una base preciosa y accesible, y la libertad total para adaptarla. Poder abrir el componente y tocar el markup directamente es una ventaja de verdad cuando el diseño manda.
Y hay un detalle que conecta con el post anterior: si te acuerdas del "olor a Bootstrap" de 2015 —todas las webs iguales, reconocibles a un kilómetro—, existe una versión 2026 de ese fenómeno. Bastantes SaaS de hoy tienen un inconfundible olor a shadcn: los mismos radios, la misma paleta neutra, las mismas sombras suaves.
La diferencia es que aquí sí puedes quitártelo de encima, porque el código es tuyo. Pero hay que dedicarle su tiempo, y mucha gente no lo hace.
❌ Donde sufre
- ERP y CRM
- Aplicaciones contables
- Gestión logística
- Administración sanitaria
- Herramientas internas con mucho dato
- Cualquier cosa con grids, árboles y edición masiva
- Sistemas con paneles redimensionables, menús contextuales y uso intensivo del teclado
Lo que en el sector llamamos "aplicaciones de escritorio web": herramientas densas orientadas a productividad, donde el usuario pasa ocho horas al día y conoce los atajos de memoria.
Y aquí está la frase que quiero que te lleves de esta parte:
En las aplicaciones de gestión, los componentes que parecen aburridos son los más caros de construir bien.
Nadie presume en LinkedIn de haber hecho un tree grid. No hay dribbbles de un selector de rango de fechas con validación de festivos. Pero cada una de esas piezas se come un trimestre de un equipo bueno.
Lo que necesitas en estos productos no es libertad estética. Es:
- Data grids avanzados de verdad
- Tree views y tree grids
- Selectores complejos de fecha y rango
- Autocompletados con decenas de miles de registros
- Densidad configurable (el usuario quiere ver 40 filas, no 12)
- Internacionalización seria
- Accesibilidad y navegación por teclado completa
- APIs uniformes de eventos y personalización
Todo eso se puede construir con shadcn. La pregunta no es si se puede. La pregunta es si quieres construirlo y mantenerlo durante los próximos cinco años.
Para estos productos suelen ser bastante más productivas MUI X, Ant Design, PrimeReact, DevExtreme, KendoReact o AG Grid.
En una aplicación de gestión, una librería más cerrada y más aburrida puede ser la decisión más productiva que tomes en todo el proyecto.
🚨 Y ahora la zona peligrosa: el medio
Los dos escenarios anteriores están claros. El problema es que la mayoría de los proyectos reales no están en ninguno de los dos.
El caso que veo una y otra vez:
Mes 1. MVP. shadcn es la elección perfecta, y lo es de verdad. Vais rapidísimo.
Mes 8. El producto funciona, entran clientes, el equipo pasa de 3 a 9 personas. Cada uno importa los componentes de ui/ directamente desde donde le viene bien, y los toca cuando le hace falta.
Mes 18. Hay cuatro variantes distintas del mismo diálogo. Tres formas diferentes de hacer un formulario. El Select está modificado de una manera que nadie recuerda por qué. Y el cliente grande pide una pantalla de administración con un grid en condiciones.
Mes 20. Alguien propone actualizar los componentes base para aprovechar mejoras del registry. Se estima. Se aparca.
En ese punto no tienes:
- La sencillez de actualizar una librería normal — porque tu código está modificado.
- Una API estable y homogénea — porque nunca la diseñasteis.
- La disciplina de una librería corporativa — porque nunca hubo frontera.
- Ni tiempo presupuestado para la transición — porque nadie la vio venir.
Y no es un defecto de shadcn, hay que tener claro que:
La deuda no aparece porque shadcn sea malo. Aparece porque su facilidad inicial te permite posponer decisiones de arquitectura que más tarde son caras.
Es exactamente igual que una hipoteca a tipo variable con dos años de carencia. No te engañó nadie. Estaba todo en el contrato. Simplemente el mes 25 llega igual, y si no estabas preparado... ¡sorpresa! La cuota es mucho más alta de lo que pensabas.
La frontera no es un muro
Un último matiz, porque la realidad nunca es tan limpia como los apartados de un post.
Un portal de viajes acaba teniendo un backoffice. Un ERP acaba necesitando un portal público para clientes. Un SaaS tiene una zona de marketing preciosa y una zona de administración densísima.
Convivir es normal y es sano. shadcn en el portal, un grid especializado en explotación, tokens compartidos entre ambos para que el usuario no note el salto.
Lo que es un problema, es no haber parcelado este desarrollo y tomado decisiones de arquitectura cuando tocaba.
Parte 11: Cómo se amortiza la hipoteca
En resumen Si vas a apostar por shadcn en un producto serio, no lo uses directamente: encapsúlalo. Elige una familia de primitivas, congela versiones, mete wrappers y tests. Y asume la paradoja: no estás adoptando una librería, estás construyendo la tuya.
Hasta aquí los riesgos. Ahora lo que puedes hacer el lunes.
Porque toda esta hipoteca tiene una forma de amortizarse, y no es complicada. Solo hay que decidirla pronto.
La arquitectura en capas
Todo se reduce a una idea: poner una frontera y respetarla.
Base UI / Radix / React Aria / TanStack / DayPicker / Recharts
↓
componentes de shadcn (tu carpeta ui/)
↓
TUS componentes corporativos (API propia, slots, variantes)
↓
tus aplicacionesLa regla de oro: tus pantallas consumen tus componentes, no directamente los ficheros de ui/.
Esa única regla lo cambia todo:
- Cuando shadcn cambie de primitivas, el impacto se queda en una capa.
- Cuando el diseño cambie, tocas un fichero y no cuarenta.
- Cuando entre alguien nuevo, tiene una API que aprender en vez de un catálogo que arqueologizar.
- Cuando la IA genere código, genera contra tu contrato.
¿Y hay que envolver literalmente todo? Los componentes que no tienen API ni comportamiento —un Separator, un Skeleton— envueltos no te aportan nada. Son un <div> con clases: no hay contrato que proteger ni implementación que quieras poder sustituir. Reexportarlos y seguir es perfectamente razonable.
Pero cuidado con dónde pones esa excepción, porque hay dos trampas.
La primera: reexportar no es encapsular. Un fichero que hace export * from "./ui/select" te da una cosa —cambiar rutas de import en un sitio— y ninguna de las que buscabas. Tus pantallas siguen escritas contra la API de shadcn, así que sustituir la implementación sigue siendo tocar las cuarenta pantallas. Es la apariencia de la frontera sin la frontera.
La segunda: una regla absoluta se puede verificar; una selectiva, no. no-restricted-imports sobre components/ui en tu ESLint cierra el debate para siempre. En cambio "envuelve los que sean ricos" obliga a decidir en cada import, y alguien con una entrega encima decidirá que su caso es de los simples — normalmente con el Select, que es justo de los que no lo son.
Así que la regla práctica: la frontera es obligatoria donde haya un contrato que proteger o una implementación que quieras poder cambiar —los componentes ricos, los patrones que se repiten, todo lo que toque negocio— y la excepción, si la haces, va escrita y acotada a una lista concreta de primitivas visuales. No al criterio de cada uno un viernes por la tarde.

Las prácticas concretas
Elige y documenta tu familia de primitivas. Base UI, Radix o React Aria. Escríbelo en el README, con la fecha y el porqué. Dentro de dos años alguien lo va a agradecer muchísimo — probablemente tú.
Congela versiones a conciencia. De las primitivas y de las librerías especializadas. Y ten un momento asignado para revisarlas, no un "cuando podamos".
No mezcles implementaciones sin una razón muy clara. Un componente de terceros escrito contra el Dialog de Radix no va a funcionar sobre un Dialog de Base UI solo porque los dos se llamen igual. Conceptos parecidos, contratos distintos.
Encapsula todo detrás de componentes propios. Sí, es una capa más. Sí, al principio parece burocracia. Es lo que separa un proyecto que envejece bien de uno que se acaba cayendo a cachos.
Revisa manualmente lo que baja del registry. Cada add es una PR con código nuevo escrito por alguien de fuera. Trátalo como tal.
Tests, y de los que importan. Interacción, accesibilidad y regresión visual. Son los que protegen tus modificaciones, que es exactamente lo que ninguna librería va a proteger por ti.
Y si tienes varios productos, monta tu registry. Es la pieza que convierte todo lo anterior en algo que escala más allá de un equipo.
Las preguntas que hay que responder antes del primer add
Si estás a punto de arrancar un proyecto, siéntate diez minutos con el equipo y responded a esto:
- ¿Qué estamos construyendo? ¿Un prototipo, un producto de larga duración o un design system?
- ¿Qué familia de primitivas usaremos? ¿Base UI, Radix o React Aria? ¿Y por qué?
- ¿Permitimos modificar los componentes base? ¿Quién lo aprueba?
- ¿Las pantallas importan de
ui/directamente, o solo de nuestros wrappers? - ¿Cómo incorporaremos las mejoras futuras del registry? ¿Cada cuánto, y quién?
- ¿Necesitamos grids, árboles o edición masiva? Si la respuesta es sí, decide ahora con qué.
- ¿Qué tests protegen nuestras modificaciones?
Si estas preguntas no tienen respuesta, no es que no tengas una arquitectura. Es que estás aceptando una hipoteca cuyo importe todavía desconoces.
Comparación rápida de enfoques
Para cerrar, un resumen de las opciones sobre la mesa:
| Enfoque | Ventaja principal | Coste principal |
|---|---|---|
| shadcn directo | Velocidad y libertad total | Mantenimiento del código copiado |
| shadcn detrás de wrappers | Contratos propios y reutilización | Construir y mantener una capa más |
| Registry corporativo | Distribución coherente entre productos | Gobierno, versiones, infraestructura |
| MUI / Ant Design / PrimeReact | Librería rica y actualizable | Menos control interno, estética condicionada |
| daisyUI | Componentes visuales ligeros, sin runtime | Menos comportamiento complejo resuelto |
| TanStack Table | Motor headless muy potente | Toda la experiencia visual la pones tú |
| MUI X / AG Grid | Componentes de datos de nivel industrial | Dependencia fuerte y licencias de pago |
🎯 Y la paradoja final
Si haces todo lo que acabo de contar —eliges primitivas, congelas versiones, montas wrappers con su API, escribes tests, mantienes un registry— habrás construido algo excelente.
Pero fíjate en qué es exactamente lo que habrás construido:
No has adoptado una librería de componentes. Has construido la tuya.
Usando shadcn como acelerador inicial, como referencia de diseño y como mecanismo de distribución.
Y eso puede ser exactamente lo que querías. Para una organización grande con varios productos y un equipo de plataforma, es probablemente la mejor base disponible hoy para hacerlo.
Pero conviene saberlo desde el principio, porque tiene un coste en personas, en tiempo y en disciplina que hay que presupuestar.
Que es justo lo que dice la documentación oficial en su primera línea, y que ahora, ochenta párrafos después, seguramente se lee distinto:
"This is not a component library. It is how you build your component library."
Cerrando la hipoteca
Hemos hecho el recorrido completo. Del nick de un señor con avatar de Rick y Morty hasta el diseño de un design system corporativo. Toca hacer cuentas.
Lo que shadcn hace de escándalo
Sin regatear nada, porque se lo ha ganado:
Resultado visual excelente. Gusto exquisito, sin comité de diseño de por medio. Se nota en cada detalle.
Velocidad brutal. De cero a una pantalla decente en minutos. Para un MVP no hay nada mejor ahora mismo.
Encaje perfecto con Tailwind. Tokens semánticos, modo oscuro casi gratis, un tema que cambia el producto entero.
Acceso total al código. Cero cajas negras. Cuando algo falla, lo abres y lo ves.
Accesibilidad heredada de gente que sabe. Radix, Base UI y React Aria son proyectos serios hechos por especialistas. No es poca cosa.
Un ecosistema enorme. Bloques, plantillas, registries de terceros, comunidad gigantesca.
Y la mejor experiencia que existe hoy trabajando con IA. Por diseño, no por casualidad.
Lo que estás pagando a cambio
El mantenimiento no desaparece: cambia de manos. El código no tiene coste de licencia. Su cuidado, sí. Y ahora es tuyo.
Las actualizaciones son diffs, no versiones. Para el código copiado no hay npm update ni nadie que te avise: tus herramientas seguirán vigilando los paquetes npm de debajo, pero los ficheros de components/ui/ son código tuyo y no salen en ningún informe.
La homogeneidad es visual, no técnica. Debajo hay un mosaico de librerías con contratos, APIs y ciclos de vida distintos.
La personalización profunda te lleva sí o sí a construir tu propia capa. No es opcional en un producto que dure. Es el peaje.
Y las decisiones de arquitectura del proyecto aterrizan en tu repositorio. Julio de 2026 lo demostró: aunque se haga todo bien, aunque se matenga la compatibilidad, aunque haya guía de migración... el trabajo lo haces tú.
La pregunta que de verdad importa
Después de todo esto, la conclusión no es "shadcn sí" o "shadcn no". Esa pregunta está mal formulada desde el principio, igual que lo estaba "¿Tailwind o Bootstrap?".
La pregunta real es esta:
¿Qué parte del coste estoy pagando ahora y qué parte estoy dejando para el futuro?
Si es un MVP que igual no llega a marzo: paga poco ahora, hipotécate hasta las cejas y corre. Es la decisión correcta.
Si es un producto que va a durar cinco años y crecer a treinta pantallas: paga la entrada grande. Wrappers, frontera, primitivas decididas, tests. Duele el primer mes y te ahorra el año tres.
Si es una aplicación de gestión densa llena de grids y árboles: párate y pregúntate si de verdad quieres ser tú quien construya eso. Hay librerías especializadas que te van a ahorrar un año de trabajo, y nadie te va a quitar puntos por usarlas.
Y si eres una organización grande con varios productos: mira el registry con calma. Puede que ahí esté lo más valioso de todo el proyecto, y no en los componentes.
Esa es la hipoteca. No es buena ni mala. Es un instrumento financiero. Lo importante es leer las condiciones antes de firmar y saber cuánto vas a pagar cada mes.
El mensaje final
Tu asistente de IA va a seguir generando npx shadcn@latest add se lo pidas o no. La cabra tira al monte, y ya hemos visto por qué en su caso tira con especial entusiasmo.
La diferencia está en el piloto. Ahora sabes:
- Que no estás instalando una dependencia, sino copiándote código a tu repo.
- Que actualizar será un diff a mano y conviene presupuestarlo.
- Que elegir Base UI, Radix o React Aria es arquitectura, no una opción del instalador.
- Que debajo de la estética uniforme hay media docena de librerías distintas.
- Que si tu producto vive de un data grid, esta puede no ser tu batalla.
- Y que en cuanto escribas tu primer wrapper, ya has empezado a construir tu propia librería. Mejor hacerlo a propósito.
La IA escribe rápido. La hipoteca la firmas tú.
Y esa firma, ni shadcn, ni MUI, ni el proyecto de moda del año que viene te la van a leer.
Referencias
Todo lo que he citado, para que lo compruebes tú mismo:
Oficiales de shadcn/ui
- Documentación e introducción — la definición oficial y el "this is not a component library"
- Changelog: Base UI as the Default (julio 2026)
- Changelog completo
- Guía de migración Radix → Base UI
- Registry · Namespaces · Autenticación de registries privados
components.json· CLI · Theming- Servidor MCP · Skills · Dynamic Search · formato de registry item
Las primitivas (las tres, sin estilos y solo para React)
- Base UI — "Unstyled UI components for accessible design systems"
- Radix Primitives — que no es lo mismo que Radix Themes
- React Aria — que no es lo mismo que React Spectrum
Las librerías especializadas de debajo
- Calendar de shadcn/ui — donde dice literalmente que va sobre React DayPicker
- React DayPicker · Recharts · Embla Carousel
Tablas y datos
El ecosistema comercial (de terceros, sin relación con el proyecto oficial)
Astro y las islas
- Guía oficial de shadcn/ui para Astro — fíjate en el
--add reactdel comando - Islands architecture en Astro · directivas
client:· integración de React
Del ecosistema Tailwind
- tailwind-merge
- daisyUI
- Tailwind: esto no es Bootstrap — el post anterior de esta serie
💻 Todo el código de los ejemplos, listo para clonar y trastear: Lemoncode/demos-post-lemoncode-shadcn-ui.
Son cuatro proyectos independientes (React 19 + Vite + Tailwind 4 + shadcn/ui sobre Base UI):
01-que-copia-el-add— qué deja de verdad un soloadden tu repo02-el-conflicto— tocas elButton, llega un componente que esperaba el contrato original. No compila, y es a propósito03-wrapper-propio— la salida mantenible, y la paradoja de acabar construyendo tu librería04-tabla-tanstack— lo que TanStack te da hecho y lo que no
¿Necesitas ayuda con tu proyecto?
En Lemoncode llevamos un montón de proyectos a nuestras espaldas construyendo aplicaciones de gestión y productos web. Con shadcn/ui, con Material UI, con Tailwind, y con varios de ellos conviviendo en paz.
Los desafíos que van más allá de este post ya los tenemos resueltos y rodados:
- Sistemas de diseño y tokens corporativos
- Cómo encapsular shadcn para que no se te vaya de las manos
- Elegir librería de componentes con criterio, y no por lo que se lleve
- Rescatar proyectos que ya están en la zona peligrosa
- Autenticación, formularios, datos globales y caché
- Cómo encajar las herramientas de IA en el flujo del equipo sin perder el criterio
Si te toca arrancar (o rescatar) una aplicación de este tipo y quieres ir sobre seguro, ofrecemos servicios de consultoría y desarrollo.
Escríbenos a info@lemoncode.net y cuéntanos tu caso. Estaremos encantados de escucharte.