Layouts modernos con bslib
¿Por qué bslib?
Durante años el patrón estándar para dashboards en Shiny fue shinydashboard, un paquete con su propia maquinaria de cabeceras, sidebars y boxes. Funcional, pero atado a Bootstrap 3 y con estética envejecida.
En 2021 Posit lanzó bslib como el reemplazo moderno:
- Bootstrap 5 por defecto (responsive, mobile-first).
- Sintaxis composable: cards, columns, navsets construidos con la misma API que
shiny::fluidPage. - Themes nativos: cambias colores y fuentes con una función, sin tocar CSS.
- Integración con Quarto: la misma API funciona en documentos y apps.
install.packages("bslib")
library(shiny)
library(bslib)bslib es lo que recomienda Posit hoy y lo que vas a ver en código nuevo. shinydashboard sigue funcionando pero está en modo mantenimiento.
card: el bloque básico de la UI moderna
Una card es un contenedor con borde sutil y sombra que agrupa contenido relacionado:
card(
card_header("Ventas por región"),
plotOutput("grafico_ventas"),
card_footer("Datos actualizados: junio 2026")
)Estructura típica:
card_header(): título (opcional).card_body(): contenido principal (implícito si pasas otros widgets directamente).card_footer(): notas, fecha, créditos (opcional).
Argumentos útiles:
card(
height = 400, # altura fija
full_screen = TRUE, # botón para expandir a fullscreen
...
)Para layouts complejos, combinas cards en layout_columns() o layout_column_wrap():
layout_columns(
card(card_header("KPI 1"), value_box("Ingresos", "$12k")),
card(card_header("KPI 2"), value_box("Usuarios", "1.2k")),
card(card_header("KPI 3"), value_box("Conversión", "3.4%")),
col_widths = c(4, 4, 4)
)col_widths usa el sistema de 12 columnas de Bootstrap, 4+4+4 = 12 distribuye el ancho en tres.
value_box: KPIs decentes
Los KPI cards son el componente más común en dashboards corporativos. value_box() los hace sin pelearse con HTML:
value_box(
title = "Ingresos del mes",
value = "$12.4k",
showcase = bsicons::bs_icon("currency-dollar"),
theme = "success",
p("8.2% sobre el mes pasado")
)title: etiqueta superior.value: número grande destacado.showcase: icono o gráfico pequeño.theme: color preset ("primary","success","warning","danger"…).- Cualquier otro contenido va debajo del valor (descripciones, deltas, micrográficos).
Para micrográficos dentro de un value box (los famosos sparklines):
value_box(
title = "Tráfico semanal",
value = "1.2M",
showcase = plotOutput("sparkline_trafico", height = "80px"),
showcase_layout = "bottom"
)Es lo que distingue un dashboard amateur de uno profesional, KPIs con contexto visual, no solo números.
Themes y customización con bs_theme
Cambiar la apariencia de la app sin escribir CSS:
mi_theme <- bs_theme(
version = 5,
bg = "#FAF9F6", # fondo
fg = "#2A2A2A", # foreground (texto principal)
primary = "#5F8575", # color de acento
base_font = font_google("Inter Tight"),
heading_font = font_google("Newsreader")
)
ui <- page_sidebar(
theme = mi_theme,
title = "Mi app",
...
)bs_theme() expone los parámetros de Sass de Bootstrap como argumentos de R. Customizas tipografía, colores, espaciado, redondeo de esquinas, etc., sin tocar CSS.
font_google() carga la fuente de Google Fonts y la pasa a Bootstrap. Útil para alinear la app con la identidad de marca.
Para experimentar interactivamente:
bslib::bs_theme_preview(mi_theme)Abre una mini-app con todos los componentes de Bootstrap renderizados con tu theme, el equivalente a un style guide para validar antes de aplicar.
Comparación con shinydashboard
Equivalencias rápidas si vienes de shinydashboard:
shinydashboard |
bslib |
|---|---|
dashboardPage() |
page_sidebar() o page_navbar() |
dashboardHeader() |
argumento title en page_*() |
dashboardSidebar() |
sidebar() |
dashboardBody() |
contenido directo en page_*() |
box(title = ...) |
card(card_header(...)) |
valueBox() |
value_box() |
tabBox() |
navset_card_tab() |
menuItem() |
nav_panel() |
Si tienes apps en shinydashboard ya en producción: déjalas. Funcionan. Migrar tiene coste sin beneficio inmediato. Para apps nuevas, usa bslib.
Trampas habituales
page_sidebar()con muchísimo contenido directamente. Si el body crece sin estructura, mete cards. Un dashboard sin cards parece un blob. Con cards organizadas, tiene jerarquía.- Mezclar
bslibyshinydashboard. Cargar los dos en la misma app genera conflictos de CSS y resultados impredecibles. Compromete uno. value_box()con texto largo envalue. El campovalueestá optimizado para 1-3 palabras o un número. Si pones una frase, se desborda. Usa el cuerpo de la card para texto.- Olvidar
bs_theme_preview()antes de aplicar un theme custom. Aplicar un theme directamente sin previsualizarlo puede romper la legibilidad de componentes que no esperabas, confirma primero.
En la siguiente entrega
Has aprendido layouts. La siguiente pieza son las tablas interactivas, el componente más usado en dashboards reales. DT y reactable son las dos opciones modernas. Cada una con su filosofía. Lo siguiente.