Saltar al contenido

Directivas de template: set:html, is:inline, define:vars

Las directivas que gobiernan los casos especiales: set:html y set:text para inyectar contenido crudo o escapado, is:inline para dejar scripts y estilos sin procesar, y define:vars para cruzar datos del servidor al codigo que corre en el cliente.

set:html y set:text: crudo o escapado

Por defecto, {valor} escapa el HTML: una cadena con <b> se pinta como texto literal, no como negrita. Es la red de seguridad contra la inyección. set:html es la renuncia deliberada a esa red: inyecta la cadena tal cual, interpretando sus etiquetas. Su pareja, set:text, hace lo contrario y fuerza el escapado, de modo que aunque la cadena traiga etiquetas se muestran como texto.

ASTRO
---const desdeCMS = '<strong>Oferta</strong> por tiempo limitado';---<div set:html={desdeCMS} /><Fragment set:html={desdeCMS} />

set:html es una puerta abierta a la inyección si la cadena no es de confianza: nunca la uses con contenido que venga de un usuario sin sanearlo antes. Su terreno legítimo es el contenido que tú controlas —Markdown ya renderizado, HTML de un CMS previamente saneado—.

Conviene ver set:text no como una directiva exótica sino como la forma explícita del comportamiento por defecto de la llave: {cadena} y <span set:text={cadena} /> producen exactamente el mismo texto escapado. Su valor está en la intención declarada —dejar constancia en el código de que ahí quieres texto, pase lo que pase con el contenido— y en la posibilidad de colgarlo de un <Fragment> para escapar sin envoltorio. Es el par simétrico de set:html: donde uno confía y abre, el otro desconfía y sella.

is:inline: scripts y estilos sin procesar

Por defecto Astro procesa los <script> y los <style>: empaqueta el JavaScript, resuelve sus imports, lo mueve y lo deduplica; y aísla el CSS al componente para que no se filtre. is:inline le dice a Astro que deje la etiqueta exactamente donde está, verbatim: sin empaquetar, sin resolver imports, sin aislar, en línea y en su sitio.

ASTRO
<script is:inline>  // se emite tal cual, sin empaquetar ni resolver imports  console.log('Corro inline, en mi sitio exacto');</script>

Lo necesitas para snippets de terceros que deben ir literales, para un bloque de JSON incrustado, o para un script que debe correr antes que cualquier hidratación. El precio es que renuncias a todo lo que el procesado te daba: si el componente se repite, el script se duplica; los imports no se resuelven; no hay TypeScript. Es una salida de emergencia útil, pero con coste, y por eso conviene usarla solo cuando el procesado por defecto de verdad estorba.

Hay un matiz que sorprende a los recién llegados: un <script> normal en Astro no es el script en línea clásico, sino un módulo que Astro procesa, empaqueta y suele mover fuera del punto donde lo escribiste. Si tu intención era el viejo comportamiento —este código, aquí, tal cual, ahora— is:inline es precisamente lo que lo restaura. No es un modo raro: es el que muchos esperan por defecto, sin saber que Astro hace algo más sofisticado a menos que le pidas lo contrario.

El ejemplo canónico es el snippet de un tercero —una analítica, un chat de soporte— que viene con instrucciones de pegarlo literalmente y que se rompería si Astro intentara empaquetarlo:

ASTRO
<script is:inline src="https://analitica.example/tag.js"></script><script is:inline>  window.dataLayer = window.dataLayer || []  dataLayer.push({ evento: 'carga' })</script>

El mismo is:inline vale para los estilos: un <style is:inline> se emite sin el aislamiento por componente que Astro aplica por defecto, tal cual y con alcance global. Se usa poco —el aislamiento suele ser justo lo que quieres—, pero existe para los casos en que de verdad necesitas un bloque de CSS crudo, global y en su sitio exacto, sin que el compilador lo toque.

define:vars: del servidor al cliente

El frontmatter corre en el servidor; un <script> de cliente corre en el navegador. Son dos mundos que no comparten memoria: la variable que calculaste arriba no existe abajo. define:vars es el puente sancionado: serializa variables del servidor y las inyecta como declaraciones al principio del bloque, ya sea un script o un estilo.

ASTRO
---const usuario = 'Ada';const intentos = 3;---<script define:vars={{ usuario, intentos }}>  console.log(`${usuario} lleva ${intentos} intentos`);</script><style define:vars={{ acento: '#89b4fa' }}>  a { color: var(--acento); }</style>

En el script, Astro antepone las declaraciones serializadas —const usuario = "Ada"; const intentos = 3;— para que el código de cliente las use. En el estilo, las convierte en variables CSS —--acento— que referencias con var(). El caso del <style> es especialmente elegante para temas: pasas un color calculado en el servidor como propiedad personalizada y lo consumes con var(--acento) en reglas normales, aisladas al componente, sin sembrar atributos style en línea por todo el marcado. El dato viaja una sola vez, como variable CSS, y todo el estilo del componente puede leerlo.

ASTRO
<script define:vars={{ tema: 'oscuro' }}>  document.documentElement.dataset.tema = tema</script><!-- Astro antepone  const tema = "oscuro";  antes de tu codigo -->

Lo que cruza la frontera es una copia serializada, no una referencia viva: solo sobreviven valores serializables a JSON —nada de funciones ni objetos con métodos—, y el valor es una fotografía del instante del render. Y como se escribe en el HTML de la página, jamás metas secretos por ahí.

El límite de la serialización conviene tenerlo presente. Una fecha viaja como su representación en texto, no como un objeto Date con métodos; un Map, un Set o una función no sobreviven al cruce. Si el cliente necesita reconstruir algo complejo, pasa los datos primitivos con define:vars y rehaz el objeto allí, ya en el navegador, con el material que sí atravesó la frontera. Este es, además, el único cruce real de la frontera servidor-cliente en todo el nivel de templating: lo que sigue —las islas y las directivas client:*— vive al otro lado, y define:vars es la primera grieta por la que un dato del servidor llega al código que corre en el navegador.