En la lección anterior interpolaste tu primer valor dentro de una plantilla html, sustituyendo un texto fijo por un campo de instancia. Pero una interpolación dentro de ${} puede hacer mucho más que insertar un texto sencillo: puede rellenar un atributo HTML, activar o desactivar un atributo booleano, asignar directamente una propiedad de un elemento del DOM, o evaluar cualquier expresión JavaScript arbitraria. Esta lección recorre en detalle los distintos tipos de interpolación que admite Lit, con ejemplos concretos sobre <task-card>, y sienta las bases sintácticas que usarás durante el resto del curso.

Contenido

  1. Los cuatro destinos de una interpolación
  2. Interpolación de texto en el contenido de un nodo
  3. Interpolación de atributos HTML normales
  4. Interpolación de propiedades del DOM con el prefijo .
  5. Atributos booleanos con el prefijo ?
  6. Los eventos con @: una mención de paso
  7. Expresiones JavaScript arbitrarias dentro de ${}
  8. Anidar plantillas html dentro de otras
  9. Ampliando <task-card> con varios campos

  1. Los cuatro destinos de una interpolación

Cuando escribes ${...} dentro de una plantilla html, Lit necesita decidir cómo aplicar ese valor al DOM, y la decisión depende de dónde, sintácticamente, se ha colocado la interpolación. Existen cuatro destinos principales, cada uno con su propia sintaxis:

Destino Sintaxis en la plantilla Para qué sirve
Contenido de un nodo <p>${valor}</p> Insertar texto (o incluso otra plantilla) como contenido de un elemento
Atributo HTML <div id="${valor}"> Establecer el valor de un atributo del elemento, como cualquier atributo HTML
Propiedad del DOM <input .value="${valor}"> Asignar directamente una propiedad JavaScript del elemento, sin pasar por un atributo
Atributo booleano <button ?disabled="${valor}"> Añadir o quitar un atributo por completo según si el valor es "verdadero" o "falso"

A estos cuatro se suma un quinto destino, los manejadores de eventos con el prefijo @ (<button @click="${...}">), que se presenta brevemente en el apartado 6 de esta lección, pero cuyo estudio en profundidad corresponde al módulo 5, "Eventos y Comunicación entre Componentes". Los apartados siguientes desarrollan cada uno de los cuatro destinos principales.

  1. Interpolación de texto en el contenido de un nodo

Es el tipo de interpolación que ya usaste en la lección anterior: colocar ${...} directamente entre las etiquetas de apertura y cierre de un elemento.

render() {
  return html`<h3>${this.titulo}</h3>`;
}

Lit inserta el valor de this.titulo como contenido del <h3>, tratándolo siempre como texto plano, nunca como HTML. Esto tiene una consecuencia importante para la seguridad: si this.titulo contuviera, por ejemplo, la cadena '<script>alert(1)</script>', Lit lo mostraría literalmente como texto visible en pantalla (las etiquetas de apertura y cierre incluidas, como caracteres), en lugar de ejecutarlo como código. Esta es una diferencia deliberada respecto a innerHTML, ya comentada en la lección anterior, y es el comportamiento por defecto y recomendado en la inmensa mayoría de los casos.

Este destino también admite valores que no son cadenas de texto: números, true/false (mostrados literalmente como texto "true" o "false"), null y undefined (que no muestran nada), arrays de valores (se verá en la lección de renderizado de listas), e incluso el resultado de otra plantilla html anidada, como se explica en el apartado 8.

  1. Interpolación de atributos HTML normales

Cuando la interpolación aparece dentro de las comillas del valor de un atributo estándar de HTML, Lit la trata como una asignación de atributo:

render() {
  return html`<article id="${this.identificador}" class="${this.claseCss}"></article>`;
}

Este destino equivale, aproximadamente, a llamar a elemento.setAttribute('id', valor). Es el destino adecuado para atributos que existen en el HTML como texto: id, class, title, href, src, atributos data-* personalizados, atributos ARIA de accesibilidad, etc. Un detalle a tener en cuenta: si el valor interpolado es undefined, Lit elimina el atributo por completo en lugar de establecerlo con el texto "undefined"; este comportamiento resulta útil para atributos opcionales, aunque el módulo 7 presenta una directiva específica (ifDefined) pensada justamente para expresar esta intención de forma más explícita.

  1. Interpolación de propiedades del DOM con el prefijo .

A veces no interesa establecer un atributo HTML (que siempre es texto), sino asignar directamente una propiedad de JavaScript del elemento del DOM, que puede contener cualquier tipo de valor: un objeto, un array, un booleano nativo, una función. Para esto, Lit ofrece la sintaxis con un punto delante del nombre:

render() {
  return html`<input .value="${this.textoBusqueda}">`;
}

Aquí, .value="${this.textoBusqueda}" no crea ni modifica ningún atributo value en el HTML; en su lugar, ejecuta directamente elemento.value = this.textoBusqueda, asignando la propiedad JavaScript del elemento <input>. La diferencia entre atributo y propiedad puede parecer sutil, pero es importante: muchos elementos nativos del navegador (como <input>) mantienen su atributo HTML original (el que se escribió en el marcado) separado de su propiedad JavaScript actual (el valor que el usuario ha escrito en el campo), y en muchos casos interesa trabajar con la propiedad, no con el atributo.

Como referencia rápida para decidir cuál usar:

Situación Destino recomendado
El valor es siempre una cadena de texto simple y tiene sentido que aparezca en el HTML Atributo (attr="${valor}")
El valor es un objeto, un array, o cualquier tipo de dato que no sea texto Propiedad (.prop="${valor}")
Se trabaja con una propiedad específica del DOM que no tiene atributo HTML equivalente exacto (como .value en formularios avanzados) Propiedad (.prop="${valor}")

Este mismo mecanismo, el de pasar propiedades (no solo atributos) a un elemento, es también la base de cómo un componente Lit padre pasará datos complejos (objetos, arrays) a un componente Lit hijo; se retomará en detalle en el módulo 5 al hablar de comunicación entre componentes.

  1. Atributos booleanos con el prefijo ?

Algunos atributos de HTML son "booleanos" en un sentido especial: su sola presencia en la etiqueta activa el comportamiento, independientemente del texto que se les asigne. disabled, checked, hidden o required son ejemplos habituales. Escribir disabled="false" en HTML puro, de hecho, no desactiva el botón: el atributo sigue estando presente, así que el navegador lo interpreta igualmente como deshabilitado.

Para gestionar correctamente este tipo de atributos, Lit ofrece la sintaxis con el prefijo ?:

render() {
  return html`<button ?disabled="${this.estaBloqueado}">Guardar</button>`;
}

Con ?disabled="${this.estaBloqueado}", Lit añade el atributo disabled al elemento solo si this.estaBloqueado es un valor "verdadero" (true, o cualquier valor que JavaScript trate como verdadero), y lo elimina por completo del elemento si es "falso" (false, null, undefined, 0, cadena vacía...). Esto reproduce exactamente la semántica real de los atributos booleanos de HTML: lo que importa no es el texto del atributo, sino si está presente o no.

  1. Los eventos con @: una mención de paso

Existe un quinto tipo de interpolación, con el prefijo @, pensado para asociar manejadores de eventos del DOM a un elemento:

render() {
  return html`<button @click="${this.manejarClic}">Completar tarea</button>`;
}

Esta sintaxis equivale, de forma simplificada, a elemento.addEventListener('click', this.manejarClic). Se menciona aquí únicamente para que reconozcas la sintaxis si la ves en ejemplos o en la documentación oficial de Lit durante este módulo; su estudio completo —incluyendo cómo definir correctamente el método manejador, el problema del valor de this dentro de él, y cómo despachar eventos personalizados entre componentes— es el contenido íntegro del módulo 5, "Eventos y Comunicación entre Componentes". Por ahora, ninguna de las plantillas de este módulo usará todavía manejadores de eventos.

  1. Expresiones JavaScript arbitrarias dentro de ${}

Un aspecto que conviene interiorizar cuanto antes: dentro de ${} no solo se pueden colocar nombres de variables o propiedades sueltas (${this.titulo}); se puede colocar cualquier expresión válida de JavaScript que produzca un valor. Esto incluye operaciones aritméticas, concatenación de texto, llamadas a métodos, operadores ternarios, y cualquier combinación de ellos:

render() {
  return html`
    <article>
      <h3>${this.titulo.toUpperCase()}</h3>
      <p>Prioridad: ${this.prioridad + 1} de 5</p>
      <p>${this.completada ? 'Tarea finalizada' : 'Tarea pendiente'}</p>
    </article>
  `;
}

En este ejemplo, ${this.titulo.toUpperCase()} llama al método toUpperCase() de la cadena antes de interpolar el resultado; ${this.prioridad + 1} realiza una operación aritmética; y ${this.completada ? 'Tarea finalizada' : 'Tarea pendiente'} usa un operador ternario para elegir entre dos textos según una condición. Este último patrón, el del operador ternario dentro de una interpolación, es tan habitual en Lit que la siguiente lección, "Renderizado Condicional", lo desarrolla en profundidad como técnica principal para mostrar u ocultar contenido.

Lo único que no se puede hacer dentro de ${} es escribir sentencias completas de JavaScript (un if con llaves, un bucle for, una declaración const): solo se admiten expresiones, es decir, código que se reduce a un único valor. Cuando se necesita lógica más elaborada que una simple expresión, la técnica habitual es extraerla a un método o función auxiliar de la clase, y llamar a ese método desde dentro de ${}, como se hace con ${this.titulo.toUpperCase()} en el ejemplo anterior.

  1. Anidar plantillas html dentro de otras

Como se apuntó en el apartado 2, el resultado de una llamada a html (un objeto TemplateResult) es un valor perfectamente válido para interpolar dentro de otra plantilla html. Esto permite dividir una plantilla grande en fragmentos más pequeños y combinarlos:

render() {
  const cabecera = html`<h3>${this.titulo}</h3>`;

  return html`
    <article>
      ${cabecera}
      <p>Estado: ${this.estado}</p>
    </article>
  `;
}

Aquí, cabecera es en sí misma el resultado de una llamada a html, y se interpola dentro de la plantilla principal exactamente igual que se interpolaría cualquier otro valor. Lit reconoce que se trata de otra plantilla (no de texto plano) y la inserta como HTML real, no como texto escapado. Esta capacidad de anidar plantillas es la base de dos técnicas que se estudiarán en las próximas lecciones: extraer fragmentos condicionales a funciones auxiliares (lección de renderizado condicional) y generar una plantilla por cada elemento de un array (lección de renderizado de listas).

  1. Ampliando <task-card> con varios campos

Con todos los tipos de interpolación ya vistos, es el momento de ampliar <task-card> para mostrar varios campos de la tarea a la vez: título, estado y prioridad. Recuerda, como se explicó en la lección anterior, que estos siguen siendo campos de instancia simples, no propiedades reactivas todavía.

import { LitElement, html } from 'lit';

class TaskCard extends LitElement {
  constructor() {
    super();
    this.titulo = 'Preparar la demo del sprint';
    this.estado = 'En curso';
    this.prioridad = 2;
    this.urgente = false;
  }

  render() {
    return html`
      <article ?data-urgente="${this.urgente}">
        <h3>${this.titulo}</h3>
        <p>Estado: ${this.estado}</p>
        <p>Prioridad: ${this.prioridad} de 5</p>
        <p>${this.urgente ? '⚠ Requiere atención inmediata' : 'Sin urgencia especial'}</p>
      </article>
    `;
  }
}

customElements.define('task-card', TaskCard);

Repasemos las novedades:

  • ${this.titulo}, ${this.estado} y ${this.prioridad} de 5 son interpolaciones de texto sencillas, como ya se vio en la lección anterior; la de la prioridad combina el valor interpolado con texto literal adicional dentro del mismo nodo.
  • ?data-urgente="${this.urgente}" usa la sintaxis de atributo booleano vista en el apartado 5: el atributo data-urgente solo aparecerá en el <article> cuando this.urgente sea true. Esto es útil, por ejemplo, para que unos estilos CSS (que se verán en el módulo 4) puedan aplicar un aspecto distinto a las tarjetas urgentes basándose en la presencia de ese atributo.
  • La última línea usa un operador ternario, como se explicó en el apartado 7, para mostrar un texto distinto según el valor de this.urgente.

Al recargar la página, la tarjeta mostrará ahora varios datos combinados. Igual que en la lección anterior, si modificas estos campos desde la consola del navegador, la pantalla no se actualizará automáticamente: esa pieza sigue perteneciendo al módulo 3.

Errores Comunes y Consejos

  • Usar .prop cuando bastaría un atributo normal: si el valor es siempre texto simple, no hay necesidad de usar la sintaxis con punto; el atributo normal (attr="${valor}") es más legible y suficiente en la mayoría de los casos.
  • Usar un atributo normal para un booleano: escribir disabled="${this.estaBloqueado}" (sin el ?) no produce el resultado esperado, porque Lit convertiría el valor a texto y lo asignaría como atributo con ese texto, en lugar de añadir o quitar el atributo según su condición. Para atributos booleanos reales, siempre ?attr.
  • Intentar meter una sentencia completa dentro de ${}: código como ${if (this.urgente) { return 'Sí'; }} no es válido, porque ${} solo admite expresiones, no sentencias. La alternativa correcta es un operador ternario o extraer la lógica a un método aparte.
  • Olvidar que el contenido de texto se escapa automáticamente: si necesitas mostrar contenido HTML real generado dinámicamente (no simple texto), interpolar directamente no basta; existe un mecanismo específico para ese caso concreto (la directiva unsafeHTML) que se estudiará, con sus riesgos de seguridad, en módulos posteriores. Por defecto, y en el 99% de los casos, la interpolación de texto es la opción correcta y más segura.

Ejercicios

  1. Añade a <task-card> un nuevo campo de instancia this.etiqueta (por ejemplo, 'Backend') e interpólalo como el valor del atributo data-etiqueta del <article>.
  2. Añade un campo this.progreso (un número de 0 a 100) y muéstralo interpolado dentro de un párrafo junto con el símbolo de porcentaje, por ejemplo: "Progreso: 40%".
  3. Modifica la interpolación booleana de this.urgente del apartado 9 para que, en lugar de un atributo data-urgente, controle el atributo booleano real hidden de un pequeño <span> con el texto "URGENTE" (es decir, que el <span> solo aparezca cuando la tarea sea urgente).

Soluciones

render() {
  return html`
    <article data-etiqueta="${this.etiqueta}">
      ...
    </article>
  `;
}
render() {
  return html`
    <article>
      ...
      <p>Progreso: ${this.progreso}%</p>
    </article>
  `;
}
render() {
  return html`
    <article>
      ...
      <span ?hidden="${!this.urgente}">URGENTE</span>
    </article>
  `;
}

Nótese el uso de la negación (!this.urgente): como hidden oculta el elemento cuando está presente, hay que añadir el atributo precisamente cuando la tarea no es urgente, de ahí la negación del valor original.

Conclusión

En esta lección has recorrido los distintos destinos que puede tener una interpolación dentro de una plantilla html: contenido de texto, atributos HTML normales, propiedades del DOM con el prefijo ., y atributos booleanos con el prefijo ?, además de una mención de paso a los manejadores de eventos con @ que se detallarán en el módulo 5. Has visto también que dentro de ${} cabe cualquier expresión JavaScript, no solo nombres de variables sueltos, y que las plantillas html se pueden anidar unas dentro de otras. Con todo ello, <task-card> ahora combina varios campos interpolados: título, estado, prioridad y un indicador de urgencia.

En la siguiente lección, "Renderizado Condicional", te centrarás en una de las expresiones que ya has empezado a usar de pasada —el operador ternario dentro de ${}— y la desarrollarás en profundidad como técnica principal para mostrar contenido distinto según condiciones, aplicándola a una insignia de estado en <task-card> que cambiará según la tarea esté pendiente, en progreso o hecha.

Curso de Lit

Módulo 1: Introducción a Lit y Web Components

Módulo 2: Plantillas Reactivas y Renderizado

Módulo 3: Propiedades y Estado Reactivo

Módulo 4: Estilos en Componentes Lit

Módulo 5: Eventos y Comunicación entre Componentes

Módulo 6: Ciclo de Vida y Comportamiento Avanzado

Módulo 7: Directivas y Funcionalidades Avanzadas de Plantillas

Módulo 8: Integración, Interoperabilidad y Despliegue

Módulo 9: Pruebas y Buenas Prácticas

Módulo 10: Proyecto: Construyendo TaskFlow

© Copyright 2026. Todos los derechos reservados