Sanity.io Headless CMS Tutorial: El CMS que Construyes Tú Mismo (No al Revés)

Sanity.io Headless CMS Tutorial: El CMS que Construyes Tú Mismo (No al Revés)

Programación· 12 min de lectura

Sanity.io Dice que Libera tu Contenido. Miente.

Sanity.io se vende como el CMS que separa contenido de presentación. El argumento de venta es seductor: escribe el contenido una vez, publícalo donde quieras. La web, una app móvil, un quiosco interactivo, un asistente de voz. Suena a libertad total.

La realidad: intercambias un lock-in (WordPress, el backend) por otro (GROQ, Content Lake, Portable Text).

El 90% de los tutoriales que leéis sobre sanity io headless cms tutorial repiten lo mismo: esquemas flexibles, Studio customizable, GROQ como superpoder. Te muestran lo bonito que es montar un blog en una tarde y te venden la experiencia de desarrollo como si fuera el único criterio para elegir CMS.

Nadie habla de lo que pasa cuando quieres irte.

Yo he construido productos con Sanity — desde directorios de gestorías hasta landing pages de servicios legales. Y he visto el patrón: empiezas enamorado de la developer experience, y terminas con 47 consultas GROQ que no puedes migrar a ningún sitio. El problema no aparece en el tutorial de iniciación. Aparece dieciocho meses después, cuando el cliente pide cambiar de plataforma y te das cuenta de que tu contenido vive en un ecosistema cerrado.

Este artículo no es un tutorial para principiantes.

*Es una guía para construirlo bien desde el día uno, sabiendo exactamente dónde está la trampa. *

---

El Problema: Lo que Venden Como "Libertad" Es un Stack Propietario

Los headless CMS tradicionales exponen REST o GraphQL estándar. Da igual qué frontend uses: React, Vue, Svelte, o un script vanilla. Siempre puedes consumir los datos con las herramientas universales del ecosistema web.

Contentful te da GraphQL. Strapi te da REST. Wordpress con WPGraphQL te da... bueno, GraphQL. Incluso el vetusto Drupal expone JSON:API. Todos ellos hablan lenguajes que existen fuera de su propio universo.

Sanity te da GROQ.

GROQ no es SQL. No es GraphQL. No es MongoDB aggregation pipeline.

*GROQ es un lenguaje de consulta que solo existe dentro de Sanity. *

Si construyes filtros complejos, joins entre documentos, agregaciones con fecha, o proyecciones anidadas — todo ese conocimiento se queda en Sanity.

No puedes migrar una query GROQ a PostgreSQL. No puedes migrarla a Supabase. No puedes migrarla a Strapi. Tienes que reescribirla entera, y reescribirla implica entender de nuevo la lógica de negocio que había detrás.

Y luego está Portable Text.

✅ El marketing dice: "contenido portable, independiente del frontend".

❌ La realidad: Portable Text es un formato JSON propietario que solo las librerías de Sanity interpretan completamente.

Si usas bloques de código, imágenes con hot-spots, embeds personalizados — tu capa de presentación queda atada al serializador de Sanity. No es como subir un Markdown a cualquier sitio. Es un formato que, sin el SDK de Sanity, se convierte en un montón de JSON ilegible.

Contrástalo con Markdown. Markdown tiene 20 años de tooling. Lo renderiza cualquier cosa. No te ata a nada. Puedes mover un artículo en Markdown de WordPress a Ghost, de Ghost a Hugo, de Hugo a cualquier generador estático, y el contenido sigue siendo perfectamente legible y procesable.

Y luego está el Studio.

El Studio es una aplicación React que mantienes tú.

*Tú mantienes las dependencias. Tú actualizas los paquetes. Tú despliegas el build. *

No es un admin panel "set and forget". Es un código base vivo que tu equipo tiene que mantener solo para que los editores puedan escribir posts. Cada vez que Sanity lanza una versión mayor, tienes que revisar si tus personalizaciones siguen funcionando. Cada dependencia que añades al Studio es otra cosa que puede romperse.

---

La Evidencia: Cómo se Acumula el Lock-in Sin que te Des Cuenta

Imagina que empiezas un proyecto con Sanity.

Día 1: esquema sencillo. Un par de tipos de documento. Content Lake funcionando. Todo parece limpio. Sientes esa euforia del desarrollador que descubre una herramienta nueva y bien diseñada.

Semana 2: necesitas un filtro por fecha y categoría. Escribes tu primera query GROQ:

[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

Parece inocente. Es una query de 6 líneas. La sintaxis es legible, casi declarativa. Te sientes productivo.

Pero fíjate en author->name. Eso es un join entre documentos con sintaxis GROQ. No es SQL JOIN. No es GraphQL resolver. Es sintaxis que no existe fuera de Sanity. En cualquier otro sistema, esa relación la resolverías con un populate, un include, o un lookup. Aquí has aprendido una sintaxis que solo sirve para esto.

Semana 4: tu cliente pide un bloque de código con resaltado de sintaxis dentro del editor de texto enriquecido.

Activas el schema de código de Portable Text. Ahora cada artículo guarda bloques como este:

[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

Para renderizar eso en el frontend, necesitas un serializador personalizado. Escribes un componente React que maneja code, image, embed, callout...

*Ese serializador es tuyo. Lo mantienes tú. Y solo funciona con Portable Text. *

Mes 2: el editor quiere un campo personalizado en el panel de administración. Un selector de colores con preview visual.

Construyes un componente de React personalizado para el Studio.

Instalas dependencias. Escribes JSX. Lo despliegas como un micro-frontend.

*Acabas de añadir otro código base a tu mantenimiento. *

Mes 3: el equipo editorial pide un workflow de revisión. Sanity no lo tiene nativo. Buscas plugins de la comunidad. Encuentras uno que hace "casi" lo que necesitas. Lo adaptas. Otra dependencia. Otro punto de fallo.

Mes 6: necesitas hacer un cambio en cómo se renderizan los embeds de YouTube. Tienes que modificar el serializador de Portable Text, desplegar el frontend, y asegurarte de que el cambio no rompe los 200 artículos existentes que usan ese mismo bloque.

El resultado neto:

→ 47 queries GROQ que no puedes migrar

→ 1 serializador Portable Text que solo sirve para Sanity

→ 3 componentes de Studio personalizados con sus propias dependencias

→ 1 aplicación React (el Studio) que actualizas trimestralmente

→ 2000 documentos cuyo contenido enriquecido depende de un formato JSON propietario

Todo funciona. Todo es "flexible".

*Pero el día que quieras migrar a Strapi, Contentful, o tu propia API en Next.js — no migras datos. Migras deuda. *

---

Análisis: ¿Quién Necesita Realmente lo que Vende Sanity?

Sanity tiene un diferenciador real: colaboración en tiempo real.

Múltiples editores sobre el mismo documento. Como Google Docs, pero para contenido CMS. Es técnicamente impresionante. Detrás hay operational transforms, resolución de conflictos, sincronización vía WebSockets. Es arquitectura de nivel Google Docs aplicada a un CMS.

*La pregunta que nadie se hace: ¿tu equipo necesita editar el mismo borrador simultáneamente? *

La mayoría de los flujos editoriales son secuenciales:

  1. Un redactor escribe.
  2. Un editor revisa.
  3. Un publicador aprueba y programa.

Eso no requiere operational transforms, WebSockets, ni resolución de conflictos a nivel de párrafo. Un simple sistema de drafts + versiones (como hace Strapi o WordPress) cubre el 95% de los casos.

Piénsalo con honestidad. ¿Cuándo fue la última vez que dos personas de tu equipo editaron el mismo párrafo al mismo tiempo? En la mayoría de las organizaciones, eso nunca ocurre. Los flujos de trabajo editoriales son lineales por naturaleza: alguien escribe, alguien revisa, alguien publica. No son documentos colaborativos al estilo Google Docs.

Si no necesitas edición en tiempo real, estás pagando la complejidad arquitectónica de Sanity sin usarla.

Y esa complejidad no es gratis. Se traduce en:

  • Queries más lentas: el Content Lake prioriza consistencia sobre velocidad. Cada petición pasa por una capa de validación y sincronización que un REST sencillo no tiene. Para proyectos pequeños no se nota, pero cuando empiezas a servir cientos de peticiones por segundo, la diferencia se acumula.
  • Costes de API: Sanity factura por ancho de banda de CDN y peticiones. No es un modelo "todo incluido". Cada vez que tu frontend hace una query, gastas ancho de banda. Cada preview en vivo, cada revalidación, cada consulta de editor cuenta.
  • Curva de aprendizaje para cada nuevo desarrollador en tu equipo: GROQ no se aprende en una tarde. Y no es transferible. El desarrollador que aprende GROQ para tu proyecto no está adquiriendo una habilidad reusable. Está aprendiendo un dialecto que solo usa en vuestra base de código.

Si además trabajas con agencias o equipos externos, la rotación de desarrolladores multiplica este coste. Cada nuevo incorporado tiene que aprender GROQ, Portable Text, y la lógica del Studio antes de ser productivo.

---

El Marco: Cómo Usar Sanity Sin Que te Atrape (3 Reglas)

Llamémoslo El Marco de la Capa de Abstracción.

La idea es simple: nunca dejes que el código de tu aplicación hable directamente con Sanity. Trátalo como un detalle de implementación. El día de mañana, ese detalle puede cambiar. Tu arquitectura debe sobrevivir a ese cambio sin reescribir el frontend entero.

Regla 1: Abstract las Queries Detrás de un Repositorio

No llames a GROQ desde tus componentes de React. Crea una capa de servicio:

[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

¿Por qué?

Porque si mañana cambias de CMS, solo cambias este fichero. Tus componentes no saben que existe Sanity. No hay referencias a GROQ en tu capa de presentación. No hay imports de Sanity en tus páginas. Todo el acoplamiento está aislado en una carpeta services/ que puedes reescribir en una tarde.

Este patrón se llama repositorio y es tan antiguo como el desarrollo de software. Funciona igual para Sanity que para cualquier otra fuente de datos. La clave es la disciplina de no saltarte la abstracción, aunque sea tentador importar el cliente de Sanity directamente en un componente para "ir más rápido".

Regla 2: Portable Text → Markdown en el Lado del Servidor

No renderices Portable Text en el frontend. Conviértelo a Markdown en el servidor y envía texto plano:

[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

Ahora tu frontend recibe Markdown. Puedes renderizarlo con cualquier librería. Puedes migrarlo a cualquier CMS. Si el día de mañana te pasas a Strapi, tu frontend no se entera. El Strapi devolverá su contenido en Markdown (porque tú lo has configurado así) y tu frontend lo renderizará exactamente igual.

El paso de conversión ocurre en el servidor, en el momento de la consulta. No añade latencia perceptible (son unos milisegundos) y rompe el acoplamiento con Portable Text. Si Sanity desapareciera mañana, tus archivos Markdown siguen siendo legibles y migrables.

Regla 3: Prueba tu Migración una Vez al Año (En Serio)

Esta es la regla que nadie sigue. Pero es la más importante.

Cada año, ejecuta un script que:

  1. Exporte todo tu contenido de Sanity a JSON plano.
  2. Intente renderizar un post completo sin usar ninguna librería de Sanity.
  3. Documente qué se rompe.
[@portabletext/react] Unknown block type "code", specify a component for it in the `components.types` prop

Si el script falla, sabes que tu deuda de lock-in ha crecido. Es tu señal para abstraer más. Puede que hayas añadido un bloque personalizado en Portable Text que no se convierte bien. Puede que uses un campo de Sanity que no tiene equivalente en otros sistemas. Detectarlo a tiempo te permite corregir el rumbo antes de que sea demasiado tarde.

Esta prueba anual debería formar parte de tu checklist de mantenimiento. Como renovar el certificado SSL o revisar las dependencias obsoletas. No es opcional si valoras tu libertad tecnológica.

---

Conclusión: Sanity No Es Malo. Pero el Desconocimiento del Lock-in Sí lo Es

Sanity.io es una herramienta excelente para ciertos casos de uso. Si tu equipo editorial trabaja de forma verdaderamente colaborativa — no secuencial, sino simultánea — y necesitas esa capacidad de edición en tiempo real, Sanity es probablemente la mejor opción del mercado. Su arquitectura está diseñada para eso y lo hace bien.

Si construyes una aplicación donde el contenido se genera y se modifica en tiempo real por múltiples actores (por ejemplo, una documentación técnica viva editada por todo un equipo de ingenieros), entonces la complejidad de Sanity está justificada.

*El problema no es Sanity. Es usarlo sin entender lo que estás firmando. *

Si tu equipo realmente necesita edición en tiempo real, y estás dispuesto a asumir el coste de mantener el Studio y aprender GROQ, adelante. Pero hazlo con los ojos abiertos. No te enamores de la demo bonita sin calcular el coste a largo plazo.

Pero si solo necesitas un headless CMS para servir contenido a un frontend en Next.js, pregúntate:

→ ¿Realmente necesito colaboración en tiempo real o me vale un flujo secuencial con drafts y versiones?

→ ¿Estoy dispuesto a que mi equipo aprenda un query language que solo existe aquí y que no servirá en su próximo proyecto?

→ ¿He presupuestado el mantenimiento continuo del Studio, las actualizaciones de dependencias y los despliegues del panel de administración?

El ecosistema del desarrollo web está lleno de herramientas que prometen libertad y terminan atrapándote. No es culpa de Sanity. Es culpa de nuestra tendencia a priorizar la experiencia inmediata sobre la sostenibilidad a largo plazo.

*El mejor CMS no es el más flexible. Es el del que puedes irte sin dolor. *

Construye tu capa de abstracción desde el día uno. Aísla las queries, normaliza los tipos, convierte Portable Text a formatos universales, y prueba tu migración una vez al año. No es trabajo extra: es un seguro contra la deuda técnica que, tarde o temprano, tendrás que pagar.

Tu yo del futuro — el que tendrá que migrar 2000 documentos a otro CMS porque el cliente ha decidido cambiar de proveedor — te lo agradecerá.

Artículos relacionados

---

¿Quieres recibir contenido como este cada semana? Suscríbete a mi newsletter

Brian Mena

Brian Mena

Ingeniero informatico construyendo productos digitales rentables: SaaS, directorios y agentes de IA. Todo desde cero, todo en produccion.

LinkedIn