st.markdown: texto con formato completo
st.markdown es la función principal para mostrar texto con formato en Streamlit. Acepta la sintaxis completa de Markdown:
import streamlit as st
# Énfasis
st.markdown("**Negrita**, *cursiva*, ~~tachado~~ y `código inline`")
# Encabezados (dentro de markdown, no como función separada)
st.markdown("## Encabezado H2 dentro de markdown")
st.markdown("### Encabezado H3")
# Listas
st.markdown("""
- Elemento A
- Subelemento A1
- Subelemento A2
- Elemento B
- Elemento C
1. Primer paso
2. Segundo paso
3. Tercer paso
""")
# Cita
st.markdown("> Los datos no mienten, pero las visualizaciones mal diseñadas sí pueden engañar.")
# Enlace
st.markdown("[Documentación oficial de Streamlit](https://docs.streamlit.io)")
# Imagen
st.markdown("")
flowchart LR
A[Texto enriquecido] --> B{"Necesidad?"}
B -->|Markdown estandar| C[st.markdown texto]
B -->|HTML inline| D["st.markdown unsafe_allow_html=True"]
B -->|HTML completo| E[st.html bloque]
B -->|Etiqueta de estado| F[st.badge color y texto]
C --> G[Sintaxis bold cursiva listas tablas]
D --> H[Span color y atributos]
E --> I[Estructura div con CSS inline]
F --> J[Componente compacto]
G --> K[Render seguro sanitizado]
H --> L[Riesgo XSS si entrada usuario]
I --> L
Tablas en Markdown
Markdown permite crear tablas directamente:
st.markdown("""
| Tecnología | Curva de aprendizaje | Flexibilidad | Ideal para |
|---|---|---|---|
| **Streamlit** | Muy baja | Media | Prototipado, dashboards |
| **Dash** | Media | Alta | Apps de producción |
| **Flask** | Alta | Muy alta | Aplicaciones web completas |
| **Gradio** | Baja | Baja | Demos de IA |
""")
HTML inline con unsafe_allow_html
Por razones de seguridad, Streamlit filtra HTML por defecto. Para permitir HTML inline en st.markdown, usa el parámetro unsafe_allow_html=True:
import streamlit as st
# Color de texto con span
st.markdown(
"<span style='color: #FF4B4B; font-weight: bold;'>Alerta: umbral superado</span>",
unsafe_allow_html=True
)
# Destacado con fondo de color
st.markdown(
"<mark style='background-color: #FFF3CD; padding: 4px 8px;'>Dato importante a revisar</mark>",
unsafe_allow_html=True
)
# Texto con tamaño personalizado
st.markdown(
"<p style='font-size: 24px; color: #1F77B4;'>Métricas del día</p>",
unsafe_allow_html=True
)
Nota de seguridad: unsafe_allow_html=True permite la inyección de cualquier HTML, incluyendo scripts. Úsalo solo con contenido que controles completamente, nunca con datos del usuario.
st.html: HTML completo sin restricciones
st.html (disponible desde Streamlit 1.31) es la forma recomendada de inyectar HTML arbitrario. A diferencia de unsafe_allow_html, está diseñado explícitamente para este propósito:
import streamlit as st
# Tarjeta con estilo CSS
st.html("""
<div style="
background-color: #F0F2F6;
border-left: 4px solid #FF4B4B;
padding: 16px 20px;
border-radius: 4px;
margin: 8px 0;
">
<h4 style="margin: 0 0 8px 0; color: #FF4B4B;">⚠️ Advertencia</h4>
<p style="margin: 0; color: #262730;">
Los datos muestran una anomalía en las ventas del mes de febrero.
Revisar los registros entre el 15 y 28 de febrero.
</p>
</div>
""")
# Badge personalizado
st.html("""
<span style="
background-color: #00CC88;
color: white;
padding: 4px 12px;
border-radius: 12px;
font-size: 12px;
font-weight: bold;
">✓ ACTIVO</span>
""")
Creación de layouts HTML personalizados
st.html permite crear estructuras de layout que van más allá de lo que ofrecen los componentes nativos de Streamlit:
import streamlit as st
st.html("""
<div style="display: flex; gap: 16px; margin: 16px 0;">
<div style="flex: 1; background: #E8F4FD; padding: 16px; border-radius: 8px; text-align: center;">
<div style="font-size: 2em; font-weight: bold; color: #1F77B4;">1.230</div>
<div style="color: #666; font-size: 0.9em;">Usuarios activos</div>
</div>
<div style="flex: 1; background: #E8F8F5; padding: 16px; border-radius: 8px; text-align: center;">
<div style="font-size: 2em; font-weight: bold; color: #2CA02C;">98,5%</div>
<div style="color: #666; font-size: 0.9em;">Satisfacción</div>
</div>
<div style="flex: 1; background: #FFF3E0; padding: 16px; border-radius: 8px; text-align: center;">
<div style="font-size: 2em; font-weight: bold; color: #FF7F0E;">€ 45.230</div>
<div style="color: #666; font-size: 0.9em;">Ingresos mes</div>
</div>
</div>
""")
Markdown como cadena multilínea (buenas prácticas)
Para contenido largo, usa cadenas multilínea de Python:
import streamlit as st
st.markdown("""
## Guía de uso del dashboard
Este dashboard presenta el análisis mensual de ventas. Para aprovechar al máximo la herramienta:
1. **Selecciona el período** en el panel lateral izquierdo.
2. **Aplica filtros** por región, producto o canal de venta.
3. **Exporta** los datos filtrados con el botón de descarga.
### Métricas principales
| Métrica | Descripción | Frecuencia |
|---|---|---|
| Ingresos totales | Suma de todas las ventas del período | Diaria |
| Ticket medio | Importe promedio por pedido | Diaria |
| Tasa de conversión | % visitantes que compran | Diaria |
> **Nota**: Los datos se actualizan cada 24 horas a las 02:00 h (UTC+1).
""")
La combinación de st.markdown y st.html proporciona todo el control necesario para crear aplicaciones con una presentación visual profesional, manteniendo la simplicidad del código Python.
Contexto: por qué Streamlit filtra HTML por defecto
Streamlit se diseñó pensando en científicos de datos y analistas que escriben aplicaciones rápidamente sin preocuparse de la seguridad del frontend. Por eso, por defecto, cualquier HTML que coloques dentro de st.markdown se escapa como texto literal: verás las etiquetas <div> y <span> como si fueran palabras normales. Esta decisión protege contra ataques XSS accidentales cuando el contenido proviene de inputs de usuario o fuentes externas.
Cuando controlas el contenido al 100 % (por ejemplo, tarjetas decorativas con strings literales), puedes activar unsafe_allow_html=True para permitir el HTML. A partir de Streamlit 1.31 existe además st.html, que es la opción semánticamente correcta: hace explícito que estás inyectando HTML y no provoca la confusión de mezclar Markdown con etiquetas.
Explicación línea por línea del layout de tarjetas
El ejemplo del layout con tres tarjetas (usuarios, satisfacción, ingresos) se apoya en Flexbox puro:
display: flexconvierte el contenedor padre en un flex container para que los hijos se alineen horizontalmente.gap: 16pxañade separación entre tarjetas sin necesidad de margen manual.flex: 1reparte el espacio disponible en partes iguales entre las tres tarjetas.- Cada tarjeta tiene un
background-colorsemitransparente distinto para transmitir el contexto de su métrica (azul para usuarios, verde para satisfacción, naranja para ingresos). - Los
border-radius: 8pxsuavizan las esquinas para un aspecto moderno ytext-align: centeralinea el contenido. - Las métricas usan
font-size: 2empara destacar el número yfont-size: 0.9emmás suave para la etiqueta descriptiva.
Tabla de parámetros y comparativa
| Función | Parámetro clave | Cuándo usarla |
|---------|-----------------|---------------|
| st.markdown | unsafe_allow_html | Texto con Markdown estándar; HTML inline ocasional |
| st.html | (directo) | Contenido puramente HTML, tarjetas, layouts custom |
| st.badge | label, color, icon | Etiquetas breves de estado sin HTML manual |
| st.caption | help | Texto auxiliar en gris debajo de elementos |
Errores comunes
Las etiquetas HTML aparecen como texto. Olvidaste unsafe_allow_html=True en st.markdown o deberías usar st.html directamente. Streamlit no interpreta HTML si no se lo pides.
Scripts <script> no se ejecutan. Tanto st.markdown(unsafe_allow_html=True) como st.html eliminan las etiquetas <script> por seguridad. Para JavaScript real, necesitas streamlit.components.v1.html o un componente personalizado.
Estilos CSS que afectan a otros elementos. Si defines <style> dentro de st.html, las reglas se aplican globalmente a la app. Usa clases específicas con prefijos únicos (.mi-tarjeta) para evitar colisiones con el tema de Streamlit.
Contenido dinámico con XSS. Nunca interpoles directamente strings del usuario en st.html. Si necesitas mostrar contenido externo, escapa los caracteres especiales con html.escape() de la librería estándar.
Mejores prácticas
- Usa
st.htmlpara decoración yst.markdown(sinunsafe_allow_html) para contenido textual. Esta separación mantiene el código más legible. - Para tarjetas KPI simples, prefiere
st.metricantes que HTML custom: es más accesible y consistente con el tema. - Si repites el mismo bloque HTML varias veces, extráelo a una función que devuelva la cadena formateada con f-strings.
- Mantén los estilos inline cortos; para estilos complejos, inyecta una hoja de estilos una sola vez al principio del script con
st.html("<style>...</style>"). - Recuerda que los cambios en el HTML no respetan el tema oscuro/claro de Streamlit salvo que uses variables CSS o condicionales basados en
st.get_option("theme.base").
Alan Sastre
Ingeniero de Software y formador, CEO en CertiDevs
Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Streamlit es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.
Más tutoriales de Streamlit
Explora más contenido relacionado con Streamlit y continúa aprendiendo con nuestros tutoriales gratuitos.
Aprendizajes de esta lección
Usar st.markdown con toda la sintaxis Markdown incluidas tablas y listas anidadas. Inyectar HTML inline dentro de st.markdown con unsafe_allow_html=True. Crear contenido HTML complejo con st.html para layout y estilo personalizados. Aplicar estilos CSS inline con st.html para personalización visual. Combinar Markdown y HTML para crear contenido rico en dashboards profesionales.