R Academy · Lección

Interfaz de usuario dinámica con renderUI e insertUI

Genere elementos de la interfaz de usuario sobre la marcha según la entrada del usuario y el estado del servidor.

Lección 3 de 413 pasos

Interfaz de usuario dinámica con renderUI e insertUI es una lección gratuita de R Academy en CoddyKit. Esta es la lección 3 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de R Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de R Academy incluye 4 lecciones en total.

Interfaz dinámica — Por qué y cuándo

A veces, la interfaz que necesita no puede determinarse por completo al iniciar la aplicación: depende de las selecciones del usuario, de los datos cargados o de las condiciones de ejecución. Shiny ofrece dos mecanismos: renderUI() para reemplazar todo el contenido de un marcador de posición, e insertUI()/removeUI() para añadir o quitar elementos sin reemplazarlo todo.

library(shiny)

# Static UI: columns always shown
# ui <- fluidPage(selectInput('x', ...), selectInput('y', ...))

# Dynamic UI: columns depend on which dataset was loaded
# ui <- fluidPage(fileInput('upload', ...), uiOutput('var_selectors'))

renderUI() y uiOutput()

renderUI({...}), en el servidor, construye y devuelve un elemento de interfaz de forma reactiva. uiOutput('id'), en la interfaz, crea un marcador de posición que muestra lo que produzca renderUI. Todo el contenido del marcador se reemplaza cada vez que se vuelve a ejecutar el código reactivo.

ui <- fluidPage(
  selectInput('dataset', 'Dataset:', choices = c('iris', 'mtcars')),
  uiOutput('column_selector')   # placeholder
)

server <- function(input, output, session) {
  output$column_selector <- renderUI({
    df <- get(input$dataset)  # reactive dependency on dataset
    selectInput('col', 'Choose Column:',
                choices = names(df))
  })
}

shinyApp(ui, server)

renderUI() con varios controles

Devuelva un tagList() desde renderUI() para representar varios elementos de interfaz a la vez. Este es el patrón habitual cuando el número de controles depende de los datos; por ejemplo, al generar un filtro por cada columna de un conjunto de datos.

server <- function(input, output, session) {
  output$filters <- renderUI({
    df    <- get(input$dataset)
    nums  <- names(df)[sapply(df, is.numeric)]

    # One slider per numeric column
    sliders <- lapply(nums, function(col) {
      rng <- range(df[[col]], na.rm = TRUE)
      sliderInput(
        inputId = paste0('filter_', col),
        label   = col,
        min     = rng[1], max = rng[2],
        value   = rng
      )
    })
    tagList(sliders)   # render all sliders
  })
}

insertUI() — Añadir elementos dinámicamente

insertUI(selector, where, ui) inserta un elemento de interfaz en el DOM sin reconstruir toda la página. Use selectores CSS para indicar el punto de inserción. where puede ser 'afterEnd', 'beforeEnd', 'afterBegin' o 'beforeBegin'.

server <- function(input, output, session) {
  item_count <- reactiveVal(0)

  observeEvent(input$add_item, {
    item_count(item_count() + 1)
    id <- paste0('item_', item_count())

    insertUI(
      selector = '#item_container',  # CSS selector
      where    = 'beforeEnd',
      ui       = div(
        id    = id,
        style = 'padding: 5px; border: 1px solid #ccc; margin: 3px;',
        textInput(id, paste('Item', item_count()), value = '')
      )
    )
  })
}

removeUI() — Eliminar elementos

removeUI(selector) elimina el primer elemento del DOM que coincide con el selector CSS. Use multiple = TRUE para eliminar todos los elementos coincidentes. Es el complemento de insertUI() para interfaces basadas en listas en las que los usuarios pueden añadir y eliminar elementos.

server <- function(input, output, session) {
  observeEvent(input$remove_last, {
    # Remove the last item div
    n <- isolate(item_count())
    if (n > 0) {
      removeUI(selector = paste0('#item_', n))
      item_count(n - 1)
    }
  })

  observeEvent(input$clear_all, {
    # Remove all items matching a class
    removeUI(selector = '.dynamic-item', multiple = TRUE)
    item_count(0)
  })
}

conditionalPanel() — Visibilidad en el cliente

conditionalPanel(condition, ...) muestra u oculta elementos de la interfaz según una condición de JavaScript evaluada en el navegador. A diferencia de renderUI(), los elementos siempre están en el DOM; simplemente están visibles u ocultos. Es más rápido para una lógica sencilla de mostrar u ocultar, ya que no requiere una comunicación de ida y vuelta con el servidor.

ui <- fluidPage(
  selectInput('plot_type', 'Plot Type:',
              choices = c('scatter', 'histogram', 'boxplot')),

  # Only shown when scatter is selected
  conditionalPanel(
    condition = "input.plot_type == 'scatter'",
    selectInput('x_var', 'X Variable:', choices = names(mtcars)),
    selectInput('y_var', 'Y Variable:', choices = names(mtcars))
  ),

  # Only shown when histogram is selected
  conditionalPanel(
    condition = "input.plot_type == 'histogram'",
    sliderInput('bins', 'Number of bins:', 5, 50, 20)
  ),

  plotOutput('main_plot')
)

updateSelectInput() — Modificar entradas existentes

En lugar de reemplazar todo un control de entrada, use updateSelectInput(session, id, choices, selected) para actualizar únicamente las opciones o el valor seleccionado de un selectInput existente. Existen funciones de actualización similares para todos los tipos de entrada: updateSliderInput(), updateTextInput(), etc.

server <- function(input, output, session) {
  # When dataset changes, update the column selector
  observeEvent(input$dataset, {
    df <- get(input$dataset)
    updateSelectInput(
      session  = session,
      inputId  = 'column',
      choices  = names(df),
      selected = names(df)[1]
    )
  })

  # Reset slider when reset button pressed
  observeEvent(input$reset_btn, {
    updateSliderInput(session, 'n_items',
                     value = 20, min = 1, max = 100)
  })
}

updateSliderInput() y updateNumericInput()

Las funciones de actualización envían un mensaje al navegador para modificar el valor actual, el mínimo, el máximo o la etiqueta de una entrada existente. Esto es más eficaz que destruir y volver a crear el control con renderUI(), y conserva la posición del control en el diseño de la página.

server <- function(input, output, session) {
  # When data loads, set slider max to actual data length
  observeEvent(input$load_data, {
    data <- readRDS(input$upload$datapath)

    updateSliderInput(session, 'row_slider',
      min   = 1,
      max   = nrow(data),
      value = c(1, min(50, nrow(data)))
    )

    updateNumericInput(session, 'page_size',
      max   = nrow(data),
      value = min(10, nrow(data))
    )
  })
}

Acceder a las entradas de renderUI con req()

Las entradas creadas dinámicamente con renderUI() son NULL hasta que se representan. Use req(input$dynamic_input) en cualquier código del servidor que dependa de entradas creadas dinámicamente para evitar errores durante la inicialización.

server <- function(input, output, session) {
  # dynamic input: 'col' is created by renderUI
  output$column_selector <- renderUI({
    selectInput('col', 'Column:', choices = names(get(input$dataset)))
  })

  output$summary_stats <- renderPrint({
    req(input$col)           # wait until 'col' exists
    req(input$dataset)       # and dataset is chosen
    df <- get(input$dataset)
    summary(df[[input$col]])
  })
}

Pestañas dinámicas con appendTab()

appendTab(inputId, tab, select) añade una pestaña nueva a un tabsetPanel() durante la ejecución. removeTab(inputId, target) elimina una pestaña. Este patrón permite crear flujos de trabajo en los que los usuarios abren, cierran y cambian entre vistas de análisis generadas dinámicamente.

ui <- fluidPage(
  actionButton('add_tab', 'Open New Analysis Tab'),
  tabsetPanel(id = 'analysis_tabs', type = 'tabs')
)

server <- function(input, output, session) {
  tab_count <- reactiveVal(0)

  observeEvent(input$add_tab, {
    tab_count(tab_count() + 1)
    n <- tab_count()
    appendTab(
      inputId = 'analysis_tabs',
      select  = TRUE,
      tab = tabPanel(
        title = paste('Analysis', n),
        value = paste0('tab_', n),
        plotOutput(paste0('plot_', n)),
        actionButton(paste0('close_', n), 'Close')
      )
    )
  })
}

renderUI frente a insertUI — Cuándo usar cada uno

Elija la herramienta adecuada según su caso de uso:

  • renderUI(): reemplaza reactivamente todo el contenido de un marcador de posición. Es la mejor opción cuando todo el bloque cambia a la vez.
  • insertUI(): añade elementos nuevos al contenido existente. Es la mejor opción para listas que crecen y en las que no desea volver a representar todo.
  • updateXxxInput(): modifica una entrada existente directamente. Es la mejor opción para cambiar opciones o valores sin volver a representarla.
  • conditionalPanel(): muestra u oculta sin comunicación de ida y vuelta con el servidor. Es la mejor opción para una lógica de visibilidad sencilla.
# Decision guide:
# - 'Show filter only when advanced mode' -> conditionalPanel
# - 'Populate column choices from loaded CSV' -> updateSelectInput
# - 'Show different controls per selected tab' -> renderUI
# - 'Add a row editor for each row user adds' -> insertUI
# - 'Remove a row editor' -> removeUI

# Combining strategies:
server <- function(input, output, session) {
  # Use renderUI for column selectors (replaces whole block)
  output$col_selectors <- renderUI({ ... })
  # Use insertUI for dynamically added rows
  observeEvent(input$add_row, { insertUI(...) })
  # Use conditionalPanel for show/hide (in UI, no server code)
}

Comprobación rápida

¿Cuál es la diferencia principal entre renderUI() e insertUI() al crear una interfaz dinámica en Shiny?

Repaso de la interfaz dinámica

Conceptos clave de Interfaz dinámica con renderUI e insertUI:

  • renderUI({}) + uiOutput('id'): reemplaza reactivamente todo el contenido de un marcador de posición.
  • Devuelva tagList() desde renderUI() para representar varios elementos a la vez.
  • insertUI(selector, where, ui): inserta elementos en cualquier ubicación del DOM.
  • removeUI(selector): elimina elementos mediante un selector CSS.
  • conditionalPanel(condition, ...): muestra u oculta elementos en el cliente sin usar el servidor.
  • updateSelectInput(), updateSliderInput(): modifican entradas existentes directamente.
  • Use siempre req(input$dynamic_id) para evitar valores NULL antes de que se representen las entradas dinámicas.
# Typical pattern: dynamic column selector
ui <- fluidPage(
  fileInput('upload', 'Upload CSV'),
  uiOutput('col_ui'),          # placeholder
  plotOutput('col_plot')
)

server <- function(input, output, session) {
  df <- reactive({
    req(input$upload)
    read.csv(input$upload$datapath)
  })

  output$col_ui <- renderUI({
    req(df())
    selectInput('col', 'Column:', choices = names(df()))
  })

  output$col_plot <- renderPlot({
    req(input$col)
    hist(df()[[input$col]])
  })
}
Gratis para empezar

Aprende R con un tutor de IA — gratis

Escribe y ejecuta código real en tu navegador, obtén ayuda instantánea de un tutor de IA disponible 24/7 y continúa donde lo dejaste en la web o en la aplicación.

Cursos
43
Lecciones
159

Preguntas frecuentes

¿La lección «Interfaz de usuario dinámica con renderUI e insertUI» es gratis?

Sí — el texto completo de «Interfaz de usuario dinámica con renderUI e insertUI» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de R Academy, actualiza a CoddyKit PRO. El curso de R Academy incluye 4 lecciones en total.

¿Qué aprenderé en «Interfaz de usuario dinámica con renderUI e insertUI»?

Genere elementos de la interfaz de usuario sobre la marcha según la entrada del usuario y el estado del servidor. Practicas R Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar R Academy?

No se requiere experiencia previa. R Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 3 de 4.

¿Cuánto tiempo toma la lección «Interfaz de usuario dinámica con renderUI e insertUI»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de R Academy?

Sí. Cada lección de R Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Profundización en la programación reactiva
  2. Módulos de Shiny para reutilizar código
  3. Interfaz de usuario dinámica con renderUI e insertUI
  4. Implementación de aplicaciones Shiny
← Volver a R Academy