47

I am developing a shiny application in which I use tabsetPanel, which is generated when user enters some particular input. Thus, I want to use renderUI function to make a tabsetPanel appear / disappear.

My struggle now is that number of tabPanel elements (arguments of tabsetPanel) also depends on user input, in the way: sometimes I need 1 one, other times I want more tabPanels.

How to make it? I tried including conditionPanel or including simple if()... condition in the tabsetPanel argument, but it (rather not surprisingly...) did not work.

Cœur
  • 32,421
  • 21
  • 173
  • 232
Marta Karas
  • 4,428
  • 10
  • 37
  • 70

2 Answers2

78

Here you go. The code is fairly self explanatory.

library(shiny)
runApp(list(
  ui = pageWithSidebar(
    headerPanel('Dynamic Tabs'),
    sidebarPanel(
      numericInput("nTabs", 'No. of Tabs', 5)
    ),
    mainPanel(
      uiOutput('mytabs')  
    )
  ),
  server = function(input, output, session){
    output$mytabs = renderUI({
      nTabs = input$nTabs
      myTabs = lapply(paste('Tab', 1: nTabs), tabPanel)
      do.call(tabsetPanel, myTabs)
    })
  }
))
Ramnath
  • 50,746
  • 13
  • 116
  • 150
  • **Ramnath**, this made my day, it works great! Thank you a lot!! – Marta Karas Oct 19 '13 at 20:19
  • Glad to be of help. If you have any questions on the code itself, post here. – Ramnath Oct 19 '13 at 21:40
  • Add on: Once you have the added tabs, what is the proper way to add the same `uiOutput` to all tabs, but make the values unique within each tab? Or is this different? If you add an `output$base_tab_ui` and render them in each, all of them hold the same `input$value` for each tab. Is there a way to make these unique with different input variable names? – Jenks Oct 20 '16 at 01:49
  • @Ramnath how would you go about populating each of these dynamic tabs with its own content? perhaps, say, a renderTable / tableOutput of a data.frame which can be filtered using the same object I used to populate the tab names (ie `nTabs = length(lags_7)` and `myTabs = lapply(sprintf('%s', lags_7), tabPanel)`) (possible answer here: https://stackoverflow.com/questions/39276104/r-shiny-how-to-add-data-tables-to-dynamically-created-tabs) – d8aninja Jan 25 '18 at 15:35
  • @d8aninja, assuming that you have some plots added to a list called myPlots, you can do: `nTabs = length(myPlots); myTabs = lapply(1:nTabs, function(x){tabPanel(paste0("Tab ",x), renderPlot(myPlots[[x]]))}); do.call(tabsetPanel, myTabs)` – Alpi Murányi Dec 03 '19 at 18:20
4

There is a way to dynamically add tabPanels without renderUI, which might not be as obvious as the version with renderUI. I wrote a function addTabToTabset which will append any (list of) tabPanel(s) to a tabset/navbar.

This approach has a set of advantages over using renderUI:

  • Existing tabPanels are not re-rendered each time a new panel is added. (faster)
  • Thus, not resetting all the input variables inside existing Panels. (no variable storing workarounds needed)
  • Structure of the panel contents can be chosen individually. (In the lapply - renderUI version, all panels have to be somewhat uniform.)

The solution and code sample can be found in the answer here. If requested, I could also post the code here.

Community
  • 1
  • 1
K. Rohde
  • 8,325
  • 26
  • 47