Qué es MDX y cuándo usarlo
El salto de Markdown a MDX en Astro 7: qué capacidades añade —componentes, expresiones JavaScript e imports—, cómo instalar la integración @astrojs/mdx, y el criterio para elegir entre una página .md y una .mdx sin pagar de más por una potencia que no vas a usar.
Markdown fue diseñado para una sola cosa —escribir prosa con formato mínimo— y la hace de maravilla, pero se detiene justo donde empieza la interactividad. MDX borra esa frontera: es Markdown que entiende JSX, un formato donde un párrafo y un componente conviven en el mismo fichero sin cambiar de lenguaje. En Astro 7, activar MDX es añadir una integración; entenderlo es asumir que un documento puede ser, a la vez, texto que se lee y programa que se ejecuta.
- Distinguir las tres capacidades que MDX añade sobre Markdown: componentes, expresiones e imports.
- Instalar y configurar la integración
@astrojs/mdxen un proyecto Astro 7. - Decidir con criterio entre una página
.mdy una.mdxsegún lo que exija el contenido. - Reconocer el coste de la potencia de MDX: un parser más estricto y una compilación más cara.
De Markdown a MDX: qué se añade
Markdown es un lenguaje deliberadamente pobre, y esa pobreza es su virtud. Su gramática cabe en una cuartilla —almohadillas para títulos, asteriscos para énfasis, guiones para listas— y por eso cualquiera la escribe sin manual y cualquier herramienta la procesa sin sorpresas. El precio de esa simplicidad es un techo: en cuanto necesitas algo que no sea texto con formato —un aviso destacado, un gráfico, un contador que reaccione al clic— Markdown no tiene vocabulario para expresarlo y te empuja a incrustar HTML crudo, que ni compone ni reutiliza.
MDX rompe ese techo por el único sitio por el que se puede romper sin traicionar el formato: dejando entrar el lenguaje de componentes. Un fichero .mdx es Markdown normal hasta que aparece una etiqueta en mayúscula o una llave, y en ese punto pasa a ser JSX. No hay dos zonas separadas ni una sintaxis de escape: prosa y programa se entrelazan en la misma línea, y el compilador sabe en todo momento cuál está leyendo. Tres capacidades concretas nacen de esa unión:
- Componentes. Importas una pieza
.astroo de framework y la usas como una etiqueta más entre tus párrafos, con sus props y su contenido anidado. - Expresiones. Cualquier expresión JavaScript entre llaves se evalúa y su resultado se inserta en el texto, como
{new Date().getFullYear()}. - Imports y exports. El documento se comporta como un módulo: puede
importde otros ficheros yexportde valores hacia quien lo consuma.
---
title: Notas de la versión
---
import Aviso from '../components/Aviso.astro';
# Novedades de {new Date().getFullYear()}
<Aviso tipo="info">Esta versión cambia la configuración por defecto.</Aviso>
El resto sigue siendo Markdown normal, con **negritas** y listas.
Lo revelador de ese ejemplo es cuánto sigue siendo Markdown. MDX no te obliga a reescribir tu prosa en JSX: el noventa por ciento del fichero se teclea igual que siempre, y solo las islas de componente o de expresión adoptan la sintaxis nueva. Es una extensión, no un reemplazo, y esa continuidad es justo lo que hace que adoptarlo cueste tan poco.
Conviene matizar una creencia extendida: MDX suele describirse como un superconjunto de Markdown, pero no lo es al pie de la letra. Al reservar < y { como sintaxis, MDX rompe algunos usos que Markdown toleraba —un signo de menor que suelto, una llave literal en medio de un párrafo— que ahora hay que escapar o entrecomillar. Los bloques de código indentados con cuatro espacios tampoco se admiten: en MDX se usan siempre vallas de tres acentos graves. Y el HTML incrustado se parsea como JSX, así que cada etiqueta debe cerrarse. Son diferencias pequeñas, pero explican por qué un .md que funcionaba puede protestar al renombrarlo a .mdx: no has cambiado de dialecto, has cambiado a un parser más estricto.
Instalar la integración @astrojs/mdx
Astro no entiende .mdx de fábrica; la capacidad llega como una integración oficial. La vía recomendada es el asistente, que instala el paquete y edita la configuración por ti en un solo paso:
npx astro add mdx
Ese comando hace tres cosas: añade @astrojs/mdx a las dependencias, lo registra en el array integrations de tu configuración y confirma los cambios antes de escribirlos. Si prefieres el control manual, el resultado equivalente es instalar el paquete y declararlo tú mismo:
// astro.config.mjs
import { defineConfig } from 'astro/config';
import mdx from '@astrojs/mdx';
export default defineConfig({
integrations: [mdx()],
});
A partir de ese registro, cualquier fichero .mdx en src/pages se convierte en una ruta y cualquier .mdx en una colección se compila con el pipeline de MDX. La integración no vive aislada: por defecto hereda tu configuración de Markdown —los plugins de remark y rehype, el resaltador de sintaxis, las opciones de shiki— de modo que .md y .mdx comparten el mismo tratamiento base y no divergen sin que tú lo pidas.
Por debajo, ese pipeline no produce una cadena de HTML, sino un módulo de JavaScript. Cada .mdx se compila a un componente de Astro que exporta un Content renderizable y cualquier valor que hayas exportado en el cuerpo, más los metadatos del frontmatter. Esa es la razón profunda de que MDX pueda importar, computar y exportar: el resultado de compilarlo no es un documento pasivo, sino código que se ejecuta como cualquier otro módulo del proyecto. Lo verás con detalle en las lecciones siguientes, pero conviene fijar la idea desde ya, porque de ella se derivan casi todas las capacidades de MDX.
La integración MDX reutiliza tu bloque markdown de la configuración salvo que lo desactives con extendMarkdownConfig: false. Esa herencia es deliberada: garantiza que un enlace, un bloque de código o una nota al pie se rendericen igual vengan de un .md o de un .mdx. Si un plugin debe aplicarse solo a MDX, lo añades en el array recmaPlugins o rehypePlugins de la propia integración, que se suman a los heredados en lugar de sustituirlos.
.md o .mdx: cuándo cada uno
Tener MDX instalado no significa usarlo en todas partes. La regla es económica: .mdx cuesta más de compilar y activa un parser más exigente, así que se reserva para cuando de verdad necesitas su potencia. La pregunta que decide es simple: ¿este documento necesita componentes, expresiones o imports? Si la respuesta es no, .md es la elección correcta y más barata.
Usa .md
Contenido de pura prosa: un artículo, una página legal, una entrada de blog sin piezas interactivas. Compila más rápido, tolera más HTML crudo y basta para la inmensa mayoría del contenido.
Usa .mdx
Contenido que necesita insertar componentes entre el texto, evaluar expresiones o importar y exportar valores. La potencia justifica el coste solo cuando la vas a usar.
Ambos formatos comparten casi todo: los dos aceptan frontmatter YAML, los dos sirven como páginas en src/pages y como entradas de una colección, y los dos se consultan con la misma API de contenido. La diferencia no está en cómo se leen, sino en qué admiten dentro. Por eso migrar de .md a .mdx es trivial —renombras la extensión y sigue funcionando— mientras el fichero no use nada exclusivo de MDX.
Esa equivalencia se nota en las content collections: el glob loader que registra una colección recoge por igual los .md y los .mdx de una carpeta, así que una misma colección puede mezclar entradas de prosa pura y entradas con componentes sin ninguna configuración especial. Cada autor elige la extensión según lo que su documento necesita, y el esquema y la consulta tipada se aplican idénticos a ambos. La decisión .md frente a .mdx es, por tanto, por fichero, no por colección: conviven sin fricción bajo el mismo contrato.
flowchart TD MD[archivo punto md] --> BASE[prosa y HTML plano] MDX[archivo punto mdx] --> BASE MDX --> C[componentes JSX] MDX --> E[expresiones con llaves] MDX --> I[import y export ESM] style MDX fill:#89b4fa,color:#11111b style MD fill:#a6e3a1,color:#11111b
Astro ofrece una segunda integración para contenido enriquecido, @astrojs/markdoc, con una filosofía opuesta a la de MDX. Donde MDX abre el documento a JavaScript arbitrario, Markdoc lo cierra a un conjunto acotado de etiquetas y variables declaradas, sin ejecución libre. Esa restricción es una ventaja cuando el contenido lo escriben muchas manos o proviene de fuentes que no controlas: un documento Markdoc no puede importar ni ejecutar nada inesperado. La elección de fondo es la de siempre entre poder y seguridad: MDX cuando confías en el autor y necesitas toda la expresividad; Markdoc cuando prefieres un vocabulario cerrado y auditable.
El coste de la potencia
MDX no es Markdown gratis con superpoderes: cada capacidad que añade estrecha un poco la tolerancia del parser. En Markdown, un < suelto o una llave perdida son texto inofensivo; en MDX son sintaxis significativa —el inicio de una etiqueta o de una expresión— y un signo mal cerrado detiene la compilación con un error. Ese rigor es el mismo que te protege de un componente mal escrito, pero exige disciplina: en MDX, los caracteres < y { dejan de ser inertes.
Las asperezas concretas que encontrarás al escribir MDX se reducen a unas pocas, todas con la misma raíz —el parser espera JSX válido allí donde Markdown esperaba texto—:
- Un
<o un{literal en la prosa debe ir entre acentos graves, comoa < b, o el compilador lo tomará por sintaxis. - Toda etiqueta HTML incrustada tiene que cerrarse; un
<br>suelto rompe, y se escribe<br/>. - Los bloques de código deben usar vallas de tres acentos graves, nunca la indentación de cuatro espacios de Markdown clásico.
- El Markdown anidado dentro de un componente solo se procesa si lo separas con líneas en blanco, un detalle que verás a fondo más adelante.
Verlo junto fija el hábito: lo que es un signo literal se entrecomilla, y lo que es un componente de verdad no.
En prosa, un `<` o una llave `{` sueltos deben ir entre acentos graves para ser texto.
Un componente real como <Aviso /> se escribe sin acentos graves y sí se renderiza.
Ninguna de estas reglas es gratuita: todas nacen de que MDX espera JSX válido allí donde antes había texto libre. Interiorizarlas de una vez evita horas de desconcierto, porque convierte un error críptico del compilador en un descuido que reconoces al instante.
Hay además un coste de rendimiento real. Compilar .mdx implica arrancar el pipeline de JSX, resolver imports y evaluar el módulo, un trabajo que un .md de pura prosa se ahorra por completo. En un sitio con miles de documentos, convertirlos todos a .mdx por comodidad infla el tiempo de build sin dar nada a cambio en las páginas que no usan ni un componente. La elección de extensión es, en el fondo, una decisión de coste que se paga en cada compilación.
Durante décadas, el contenido y el código vivieron en mundos separados por un muro metodológico: el contenido era un dato inerte —una cadena, un fichero— que el código leía, transformaba y pintaba, y ese reparto de papeles se daba por natural. MDX borra ese muro, y la borradura es más profunda de lo que parece. Un fichero .mdx no es contenido que un programa procesa: es, él mismo, un programa —un módulo de JavaScript que importa, computa y exporta— que resulta estar escrito en su mayor parte con la comodidad de Markdown. El documento deja de ser el objeto pasivo del código para convertirse en un sujeto activo que ejecuta. Interiorizar esto reordena cómo piensas la arquitectura de un sitio con contenido. Deja de haber una capa de datos y una capa de presentación que alguien debe coser; hay una sola cosa, el documento-módulo, que trae su prosa y su comportamiento en la misma unidad y se compone con las demás como se compone cualquier módulo. Esa unificación es poderosa y peligrosa a la vez, y las dos caras nacen de la misma raíz. Poderosa, porque un redactor puede insertar una visualización compleja sin salir de su texto, y un ingeniero puede factorizar la presentación en componentes que el redactor reutiliza sin entenderlos por dentro. Peligrosa, porque cuando el contenido puede ejecutar código, un documento deja de ser inofensivo: heredas toda la responsabilidad de tratar el poder de cómputo con el cuidado que merece —revisar qué importa, no ejecutar MDX de fuentes en las que no confías, entender que un fichero de texto ya no es solo texto—. La lección de fondo no es sintáctica sino conceptual: MDX te enseña que la separación entre datos y código nunca fue una ley de la naturaleza, sino una elección de diseño, y que fundirlos abre un espacio de expresión enorme a cambio de exigirte la madurez de tratar tus documentos como lo que ahora son, programas.
- Crea
src/pages/nota.mdcon un par de párrafos y confirma que se sirve como página; renómbralo a.mdxy verifica que sigue funcionando sin tocar el contenido. - Añade en el
.mdxuna expresión{new Date().getFullYear()}dentro del texto y comprueba que se evalúa; escribe la misma línea en un.mdy observa que aparece literal. - Instala la integración con
npx astro add mdxen un proyecto limpio y localiza enastro.config.mjsla línea exacta que el asistente añadió al arrayintegrations. - Enumera qué documentos de un proyecto real dejarías en
.mdy cuáles ascenderías a.mdx, y justifica cada decisión por el coste de compilación que evitas o asumes.