Search code examples
ruser-interfaceshinydynamiclayout

How can I lay out dynamically generated UI elements in R/Shiny?


I have an app that needs a variable number of imageOutput elements. I managed to generate them dynamically, but I'm struggling to get them to lay out properly. In the MWE below they are just put on top of each other. Ideally they should be arranged in two columns, going left to right, then down.

I have seen some very roundabout solutions using eval parse, CSS or insertUI (which probably wouldn't even work for my case), but I'm wondering if there isn't a neat solution using Shinys basic layout facilities like fluidRow etc.

(For the following MWE to work, you'll have to place a jpg image with the name "some_image.jpg" in the working directory.)

library(shiny)
library(purrr)

ui <-
  fluidPage(sidebarLayout(sidebarPanel(
    sliderInput(
      "sliderInput",
      "Slider Input",
      min = 1,
      max = 10,
      value = 1
    ),
    width = 2
  ), mainPanel(uiOutput("uiOutput"))))

server <- function (input, output) {
  output$uiOutput <- renderUI({
    map(1:input$sliderInput, function(x) {
      imageOutput(paste0("imageOutput", x))
    })
  })
  observe({
    map(1:input$sliderInput, function(x) {
      output[[paste0("imageOutput", x)]] <-
        renderImage({
          list(src = "some_image.jpg",
               width = "400px")
        }, delete = FALSE)
    })
  })
}

Solution

  • The neatest solution would be to place all the generated items into a div with css grid to acheive a dynamic two column layout. try :

      output$uiOutput <- renderUI({
        div(style=" display: grid;
      grid-template-columns: 1fr 1fr;",
        tagList(map(1:input$sliderInput, function(x) {
          imageOutput(paste0("imageOutput", x))
        })))
      })