Search code examples
rshinybslib

bslib theme toggle not working in R Shiny app


I'm trying to better grok Shiny so I can make some neat little apps or visualisations, but I am getting a confusing error when I try to add a dark-light mode toggle switch. I pretty much copied the text straight out of the article (https://rstudio.github.io/bslib/articles/theming/index.html#dynamic)

library(shiny)
library(bslib)

light = bs_theme(version = 5, bootswatch = "yeti")
dark = bs_theme(version = 5, bg = "black", fg = "white", primary = "purple")

ui <- fluidPage(
  theme = light,
    navbarPage(
        header = checkboxInput("dark_mode", "Dark Mode"),
      .... #yadda yadda yadda
  )
)

server <- function(input, output, session) {
  observe(session$setCurrentTheme(
    if (isTRUE(input$dark_mode)) dark else light
  ))
}

This gives me the following error:

Error message indicating an invalid version parameter

but I HAVE the version param set to 5, in both themes. I have tried ending my R session and clearing out the environment, moving the variables light and dark INTO the ui doesn't have any positive effect.


Solution

  • The reason why your code is not working is a Bootstrap version clash which comes from this: You try to use bslib::bs_theme(), which has version = 5, but on the other hand you try to initialize your app with Bootstrap 3 and this can't be changed.

    That is because all *Page functions from shiny rely on Bootstrap 3 by default. Although in your code you try to initialize the fluidPage with a version 5 theme, you don't set this inside the navbarPage, so this page gets initialized with Bootstrap 3. From ?navbarPage:

    theme : One of the following: ... NULL (the default), which implies a "stock" build of Bootstrap 3.

    In the other comment you mentioned

    Weirdly changing to the bslib syntax made it work, but that doesn't make sense to me bc according to Posit, shiny calls bslib so they should be interchangeable, as I understand it but if this is just one of those things I guess that's how it is.

    This is correct, but as explained the Bootstrap versions differ. This very probably also will not be changed in Shiny because then the backwards compatibility to many apps will break.

    So if you want to fix your code with a minimal adjustment, you have to insert e.g. theme = light also within the navbarPage, such that this also gets initialized with a Bootstrap 5 theme. However, I strongly discourage this because in particular new apps should in my opinion generally default to Bootstrap 5 and in particular bslib, so I would use it in a way as you also suggested in your comment (however, nesting page_fluid() and page_navbar() is maybe also not needed, but I stick with your code):

    library(shiny)
    library(bslib)
    
    light = bs_theme(bootswatch = "yeti")
    dark = bs_theme(bg = "black", fg = "white", primary = "purple")
    
    ui <- page_fluid(
      theme = light,
      page_navbar(title = "TitleNav",
        header = checkboxInput("dark_mode", "Dark Mode"),
        theme = light
      )
    )
    
    server <- function(input, output, session) {
      observe(session$setCurrentTheme(
        if (isTRUE(input$dark_mode)) dark else light
      ))
    }
    
    shinyApp(ui, server)
    

    Please see also the related discussion in rstudio/shiny#4075.