Figure para Markdown
Tabla de contenido
Con Markdown podemos añadir imágenes a nuestro documento fácilmente con una sintaxis sencilla.

<p>
<img src="./carpeta/imagen.avif" alt="Descripción" title="Título" />
</p>
La imagen está envuelta en un párrafo porque el parser de Markdown distingue entre elementos en bloque y en línea. Si es bloque, no hay p extra; si es inline, se envuelve.Pero no podemos generar un marcado a medida, ni hay manera de usar la etiqueta figure para añadir un pie de foto, y es muy común su uso en artículos para rotular una imagen o vídeo. Quizá para describir algo que la imagen no muestra o añadir información contextual, como por ejemplo su autor, etc.
<figure>
<img src="./carpeta/imagen.avif" alt="Descripción" title="Título" />
<figcaption>Autor: Mamutlove — 2026</figcaption>
</figure>
Opciones
Dependiendo de tu stack, puedes elegir entre estas opciones:
Apariencia de pie de foto
Markdown reconoce las etiquetas que reconoce https://www.markdownguide.org/cheat-sheet/ y figure no es una de ellas, pero puedes utilizar CSS para estilar un texto que acompañe a la imagen y hacerla pasar por un pie de foto. Visualmente es el resultado esperado pero a nivel estructural no, porque no hay relación entre ambos elementos en el HTML y el árbol de accesibilidad tampoco representa tu intención.
HTML en MDX
Si estás utilizando MDX puedes embeber HTML directamente. Es la solución. Es expresiva e idiomática, pero aunque es directo, te darás cuenta de que puedes extraerlo a un componente tras escribirlo un par de veces.
Contenido por aquí bla, bla bla...
<figure>
<img src="./carpeta/imagen.avif" alt="Descripción" title="Título" />
<figcaption>Autor: Mamutlove — 2026</figcaption>
</figure>
Más contenido por acá...
La trampa de Astro
Si estás familiarizado con Astro, sabrás que una buena solución es crear un componente reutilizable y pasarlo a Content para no tener que importarlo en cada artículo, dejando así más limpio el contenido editorial separándolo de la forma.
---
import { render } from 'astro:content';
import Figure from '@components/Figure.astro';
const { post } = Astro.props;
const { Content } = await render(post);
---
<Content components={{ Figure }} />
Pero hay un par de detalles a tener en cuenta. ¿Qué forma debería tener el componente?
En Astro, puedes utilizar Image o img. Image es útil si necesitas tener mucho control sobre la imagen; diferentes tamaños, recortes o formatos alternativos. img es suficiente si tienes un tamaño definido y la imagen está optimizada.
La trampa es que img espera un string (ruta al archivo) pero Image espera un objeto (ImageMetadata) así que no está tan claro cuál usar en el componente.
<figure class="root">
<!-- srcForImage tiene que ser de tipo ImageMetadata -->
<image src="{srcForImage}" />
<!-- srcForImg tiene que ser un string -->
<img src="{srcForImg}" />
<figcaption class="caption">{caption}</figcaption>
</figure>
Con Image tienes que importar la imagen en el contenido, lo que significa que vas a manejar de forma diferente las imágenes con caption del resto. Es raro porque es una regla que no queda fijada en ningún sitio. Es un modelo mental partido. Nos obliga a recordar la regla: “si hay caption, cambia el pipeline”. Y las convenciones que dependen de memoria se rompen.
import imagenConCaption from './carpeta/imagen-con-caption.png';
# Title H1
Contenido por aquí bla, bla bla...

Más contenido por acá...
<Figure src={imagenConCaption} caption="Imagen CON caption" />
Más contenido por acá...
El problema de img es que al pasar la ruta como prop no se procesa y llega al componente el string crudo y la imagen no se renderiza.
¿Workarounds?
A mí me gusta que solo haya una forma de usar imágenes en el documento, así que prefiero pasarle al componente img con la ruta ya resuelta. El componente Figure es un wrapper que aporta la estructura semántica y tiene un slot.
---
interface Props {
caption: string;
}
const { caption } = Astro.props;
---
<figure>
<slot />
<figcaption>{caption}</figcaption>
</figure>
<Figure caption="Autor: Mamutlove — 2026">

</Figure>
En mi opinión, mejora el DX al evitar tratar las imágenes de forma diferente. Siempre van a ser imágenes con sintaxis Markdown y no llenamos el contenido con imports. Creo que en un artículo debería primar la facilidad de edición.
La salida es <p><img .../></p> porque img es un elemento en línea, y aunque no es el marcado típico, no es inválido. Por otro lado, ganamos otros casos de uso que podríamos necestiar.
<Figure caption="Autor: Mamutlove — 2026">
Bla bla bla\
Ble ble ble
</Figure>
<Figure caption="Autor: Mamutlove — 2026">
> Para atrás ni para darse impulso
</Figure>
El árbol de accesibilidad es correcto, así que no hay pegas.

Pero si no te convence porque img está envuelta p, siempre puedes desempaquetarlo.
---
interface Props {
caption: string;
}
const { caption } = Astro.props;
const slotted = await Astro.slots.render('default');
const media = slotted.replace(/^\s*<p>([\s\S]*?)<\/p>\s*$/, '$1').trim();
---
<figure>
<Fragment set:html={media} />
<figcaption>{caption}</figcaption>
</figure>
Yendo un poco más allá
Lo más habitual es que “de repente” surja la necesidad de meter negritas o cursivas en el pie pero un atributo string no se no se puede procesar, así que nuestro gozo en un pozo.
Para evitar tirar el componente a la basura, podemos modificarlo y añadirle un segundo slot y que acepte el atributo o un Fragment.
---
interface Props {
caption?: string;
}
const { caption } = Astro.props;
const hasCaption = Boolean(caption) || Astro.slots.has('caption');
---
<figure class="root">
<slot />
{
hasCaption && (
<figcaption class="caption">
<slot name="caption">{caption}</slot>
</figcaption>
)
}
</figure>
<Figure caption="Autor: Mamutlove — 2026">

</Figure>
<Figure>

<Fragment slot="caption">
Autor: *Mamutlove* — 2026
</Fragment>
</Figure>
Esta solución es interesante porque Fragment no genera nodo en el HTML y aunque el contenido del segundo slot estará envuelto con p, figcaption admite flow content, así que es válido igualmente.
Recursos
- Markdown Cheatsheet
- MDN — Figure
- Astro — Images
Si te ha parecido interesante
Tanto si tienes alguna duda o te apetece charlar sobre este tema, así como si el contenido te parece interesante o crees que pdemos hacer algo juntos, no dudes en ponerte en contacto con nosotros a través del email hola@mamutlove.com