Resaltado nativo: Shiki integrado y cero JavaScript
Astro trae Shiki incrustado y enciende el resaltado de código sin instalar nada: escribes un bloque con tres backticks y un lenguaje, y la coloración ocurre en el build usando las mismas gramáticas TextMate que VS Code. El resultado es HTML estático con estilos en línea, sin una sola librería que se descargue ni un script que corra en el navegador del visitante.
Escribes un bloque de código en un .md o un .mdx, le pones un lenguaje detrás de los tres backticks, y al abrir la página el código ya llega teñido —palabras clave, cadenas y comentarios en su tono— sin que se haya descargado ninguna librería ni ejecutado un solo script en el cliente. Ese pequeño milagro cotidiano lo firma Shiki, el resaltador que Astro trae integrado y enciende sin que instales ni configures nada. Entender qué hace, cuándo lo hace y por qué no cuesta ni un byte de JavaScript es el cimiento de todo el nivel: es la diferencia entre colorear código en el navegador de cada visitante y hornear ese color una sola vez, en tu máquina, para siempre.
- Reconocer que Astro integra Shiki y resalta el código sin configuración previa.
- Activar el resaltado escribiendo bloques con tres backticks y un identificador de lenguaje.
- Comprender que la coloración sucede en el build y no envía JavaScript al cliente.
- Situar
syntaxHighlighty las gramáticas TextMate como la maquinaria bajo el resultado.
El resaltado que ya está encendido
Lo primero que sorprende es lo que no hay que hacer. No instalas un paquete, no importas un tema, no añades una etiqueta de script al final del documento. Astro llega con Shiki dentro y con el resaltado activado por defecto para todo el Markdown y el MDX del proyecto. La opción que gobierna esa decisión es markdown.syntaxHighlight, y su valor de fábrica es 'shiki'.
// astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
markdown: {
syntaxHighlight: 'shiki', // valor por defecto, no hace falta escribirlo
},
});
Ese ajuste admite tres formas. La cadena 'shiki' usa el resaltador nativo; 'prism' cambia al motor alternativo que verás en la última lección; y false apaga el resaltado por completo, dejando el código en texto plano. Existe además una forma de objeto que permite afinar el comportamiento sin renunciar a Shiki.
// astro.config.mjs
import { defineConfig } from 'astro/config';
export default defineConfig({
markdown: {
syntaxHighlight: {
type: 'shiki',
excludeLangs: ['mermaid', 'math'],
},
},
});
Esa forma de objeto tiene un uso muy concreto en la práctica. Cuando integras diagramas o fórmulas, su fuente viaja también en bloques de código etiquetados con un lenguaje propio —mermaid, math—, pero no quieres que Shiki los coloree como si fueran código, porque otra herramienta los va a transformar después en un diagrama o una ecuación. Excluirlos con excludeLangs deja su contenido intacto para esa otra herramienta, sin renunciar al resaltado de todo lo demás. Pero salvo esos casos, lo normal es no tocar nada: el resaltado ya está, y está bien.
Conviene medir cuánto trabajo desaparece. En el mundo previo, dar color a un bloque de código exigía elegir un resaltador de cliente, incluir su fichero JavaScript, cargar además una hoja de estilos con el tema, y confiar en que ese script se ejecutara en cada visita para teñir el <pre> una vez cargada la página. Astro borra los cuatro pasos: ni script, ni CSS de tema, ni ejecución en el navegador, ni espera. El bloque llega coloreado en el propio HTML.
Ese resaltado cubre por igual el Markdown puro y el MDX, porque ambos comparten la misma tubería de contenido: la valla de código se procesa del mismo modo la escribas donde la escribas. Y conviene distinguirla desde ya del código en línea —el que envuelves con un solo backtick dentro de una frase—, que Astro emite como un simple <code> sin colorear, porque un fragmento suelto sin lenguaje no tiene estructura que analizar. El resaltado es cosa de los bloques con lenguaje; el código en línea es tipografía, no sintaxis.
Por defecto, ese resaltado usa el tema github-dark, una elección neutra que encaja en la mayoría de los diseños oscuros. No es una imposición: en la próxima lección cambiarás el tema, montarás un par claro/oscuro y afinarás cada detalle. Pero es revelador que el punto de partida ya sea razonable sin que hayas escrito una sola línea de configuración. Astro apuesta por defectos que sirven, de modo que configurar sea una mejora opcional y no un peaje de arranque; el resaltado bonito no es una recompensa que te ganas tras configurar, sino el estado inicial.
Tres backticks y un lenguaje
La interfaz para el autor es la más simple que existe en Markdown: una valla de tres backticks, y justo después el identificador del lenguaje. Ese identificador es la única señal que Shiki necesita para saber qué gramática cargar y cómo interpretar el texto.
const saludo: string = 'hola';
console.log(saludo.toUpperCase());
El bloque de arriba se abrió con la valla seguida de ts, y por eso Shiki lo lee como TypeScript: reconoce la anotación de tipo, la palabra const, la cadena y la llamada a método, y tiñe cada pieza según su papel. Si hubieras omitido el lenguaje, el bloque se trataría como texto plano —plaintext— y llegaría sin color, porque sin gramática no hay nada que interpretar. El identificador no es decorativo: es el que selecciona el analizador.
Los nombres de lenguaje admiten alias habituales —js y javascript, ts y typescript, sh, bash, shell—, todos resueltos contra el catálogo de gramáticas que Shiki empaqueta. Si escribes un identificador que Shiki no conoce, el resaltado degrada con elegancia a texto plano y te avisa, en lugar de romper el build. La regla práctica es sencilla: etiqueta siempre tus bloques con el lenguaje correcto, porque de esa etiqueta depende que el color sea exacto y no aproximado.
Lo que escribes tras el identificador —en la misma línea de apertura de la valla— es la cadena meta, un espacio para banderas y rangos que ciertos transformadores leen; la verás con detalle en la lección de transformadores. Por ahora basta con retener la anatomía completa de una valla: tres backticks, el lenguaje pegado a ellos, y opcionalmente una cadena meta después. De esos tres elementos, solo el lenguaje decide el color; los otros dos son estructura y metadatos.
La gramática detrás del color
Aquí está la diferencia técnica que separa a Shiki de los resaltadores clásicos. Muchos motores de cliente colorean con expresiones regulares: un puñado de patrones que buscan lo que parece una palabra clave o una cadena. Es rápido y ligero, pero impreciso, porque una regex no entiende de verdad la estructura del lenguaje. Shiki no adivina: usa gramáticas TextMate, las mismas que emplea VS Code, ejecutadas por el motor de expresiones regulares Oniguruma compilado a WebAssembly. El resultado es que el color que ves en tu documentación coincide, token a token, con el que verías en tu editor.
Una gramática TextMate no asigna colores; asigna ámbitos —scopes—, categorías semánticas como “esto es una palabra clave de control”, “esto es el nombre de una función”, “esto es una cadena literal”. El tema, que es un fichero JSON heredado del ecosistema de VS Code, es quien mapea cada ámbito a un color concreto. Esa separación en dos capas —una que entiende la estructura, otra que decide la apariencia— es la que da a Shiki su precisión y su flexibilidad, y será el eje de la próxima lección.
La precisión que esto compra se ve en los casos difíciles. Imagina un bloque de HTML con una etiqueta script que contiene JavaScript, que a su vez incrusta una plantilla con CSS dentro. Una expresión regular se pierde en ese anidamiento y colorea a ciegas; una gramática TextMate, en cambio, cambia de sub-gramática al entrar en cada región incrustada y tiñe cada capa con las reglas de su propio lenguaje. Esa capacidad de reconocer sintaxis dentro de sintaxis es la que separa un resaltador que entiende el código de uno que solo lo tantea, y es la razón por la que el color de Shiki coincide con el de tu editor incluso donde el código se complica.
Vale la pena nombrar las piezas con propiedad, porque reaparecerán en todo el nivel. Un token es cada fragmento contiguo al que la gramática asigna un mismo ámbito: la palabra const, el nombre de una variable o una cadena entre comillas son tokens distintos. Un ámbito es la etiqueta jerárquica que describe qué es ese token —algo como keyword.control, string.quoted o entity.name.function—, y su forma en segmentos separados por puntos permite que un tema apunte a categorías amplias o finas. Shiki produce, para cada línea, una secuencia de tokens con sus ámbitos, y el tema recorre esa secuencia asignando un color a cada uno. Ese es, desmontado, todo el mecanismo: tokenizar el texto en ámbitos, y mapear ámbitos a colores.
Que Shiki reutilice las gramáticas y los temas de VS Code no es un detalle menor de compatibilidad: significa que el enorme trabajo de la comunidad en describir cientos de lenguajes con precisión está disponible aquí sin coste. No hay una gramática “para web” distinta de la “para editor”; es la misma, y por eso lenguajes complejos —con plantillas anidadas, con sintaxis incrustada— se resuelven bien donde una regex se rendiría.
Coloreado en el build, cero JavaScript en el cliente
Llegamos al hecho arquitectónico que lo cambia todo. Shiki corre en el build —o en el render del servidor, para páginas bajo demanda—, nunca en el navegador. Cuando el visitante recibe la página, el bloque ya es HTML terminado: un <pre> con la clase .astro-code, y dentro un <span> por cada token con su color escrito directamente en un atributo style en línea. No viaja ningún fichero JavaScript de resaltado, ni ninguna hoja de estilos de tema. El color está cocido en el marcado.
Ese momento de cálculo se adapta al tipo de página sin cambiar su naturaleza. En una ruta estática el resaltado ocurre en el build y el HTML resultante se guarda tal cual, listo para servirse mil veces sin recomputar. En una ruta bajo demanda ocurre durante el render de la petición, en el servidor, justo antes de enviar la respuesta. En ninguno de los dos casos toca el navegador: la frontera que de verdad importa no es build frente a servidor, sino servidor frente a cliente, y el resaltado nativo se queda siempre, en cualquier modo, del lado del servidor.
Si abres el HTML generado, verás el color escrito en el propio marcado, sin ninguna clase que dependa de una hoja de estilos externa para tener sentido.
<pre class="astro-code github-dark" style="background-color:#24292e;color:#e1e4e8">
<code>
<span class="line"><span style="color:#F97583">const</span> <span style="color:#79B8FF">x</span> = <span style="color:#79B8FF">1</span>;</span>
</code>
</pre>
La mejor forma de interiorizar que no hay JavaScript de por medio es abrir las herramientas del navegador y mirar el HTML de un bloque: verás el <pre> con clase .astro-code, sus <span> con el color en un atributo style, y ni rastro de un script de resaltado en la pestaña de red. Lo que en otros sitios sería trabajo del cliente, aquí ya está hecho y guardado. Ver el color escrito en el propio marcado es la prueba tangible de que el cálculo ocurrió antes, lejos del visitante.
flowchart LR MD[bloque con tres backticks y lenguaje] --> BUILD[Shiki tokeniza en el build] BUILD --> HTML[pre astro-code con estilos en linea] HTML --> NAV[el navegador solo pinta HTML] NAV --> ZERO[cero JavaScript de resaltado] style BUILD fill:#89b4fa,color:#11111b style ZERO fill:#a6e3a1,color:#11111b
La consecuencia se nota en cada métrica que importa. La página pesa menos porque no arrastra un resaltador. Se pinta antes porque no espera a que un script se descargue, se compile y recorra el DOM tiñendo nodos. Y funciona igual con JavaScript deshabilitado, porque el color nunca dependió de él. El coste de resaltar se pagó una vez, en tu máquina, durante el build; el dispositivo del visitante no gasta ni un ciclo en ello.
Cero instalacion
Shiki viene dentro de Astro y se enciende por defecto; no instalas ni importas nada.
Lenguaje en la valla
El identificador tras los backticks selecciona la gramatica; sin el, es texto plano.
Gramaticas TextMate
El mismo motor que VS Code, movido por Oniguruma en WebAssembly, para un color exacto.
HTML terminado
El color viaja cocido en el marcado, sin script de resaltado ni hoja de estilos de tema.
Para ver por qué esto es más que una optimización conviene nombrar la propiedad que lo hace posible: el resaltado de sintaxis es una función pura. Dado el mismo código fuente, la misma gramática y el mismo tema, produce siempre exactamente el mismo HTML; no depende de la hora, ni del visitante, ni del dispositivo, ni de nada externo a sus tres entradas. Y todo lo que es puro y determinista comparte un privilegio: no necesita ejecutarse cada vez que se necesita su resultado, porque el resultado ya no cambiará. Se puede calcular una vez y guardar, que es justo lo que hace la memoización, lo que hace un compilador al plegar constantes, y lo que hace un build estático al hornear una página. La pregunta profunda que subyace no es “¿qué resaltador uso?”, sino “¿en qué momento del ciclo de vida debe correr un cálculo?”. Y la respuesta general, que gobierna la arquitectura entera de Astro, es tan simple como poderosa: un cálculo debe correr lo más pronto posible, en cuanto sus entradas estén disponibles, y su resultado debe reutilizarse tantas veces como haga falta sin volver a calcularlo. El código de un bloque estático se conoce en el build; su color, por tanto, pertenece al build. Empujarlo al navegador de cada visitante sería recalcular millones de veces —una por dispositivo, por visita— algo cuyo resultado se fijó desde el primer instante, y encima cobrarle a cada uno la descarga del motor que lo recalcula. La economía es abismal: colorear en el cliente reparte un coste fijo entre infinitos pagadores que no deberían pagar; colorear en el build lo concentra en un único pago que amortizas para siempre. Interiorizar esto —que lo que no depende de la petición no debe calcularse durante la petición— es dar el primer paso hacia pensar como Astro, y el humilde resaltado de un bloque de código es el ejemplo más limpio y cotidiano de esa idea.
- Crea una página
.mdxcon un bloque de código etiquetado comotsy otro sin lenguaje; observa que solo el primero llega coloreado. - Inspecciona el HTML generado y localiza el
<pre>con clase.astro-codey los<span>constyleen línea que llevan el color. - Abre la pestaña de red del navegador y confirma que no se descarga ningún fichero de resaltado ni una hoja de estilos de tema.
- Cambia
syntaxHighlightafalseenastro.config.mjs, reconstruye, y razona por qué el mismo bloque que antes no costaba nada al visitante habría costado CPU en cada visita si el resaltado corriera en el cliente.