Shiny bookmarked URL state fails, restores main app page

I'm trying to set up URL bookmarking in my Shiny app, which is created using shinyDashboard and shinyDashboardPlus.

The app is fairly complex, but the full code is here, and the app is deployed here.

The bookmarkButton() works fine, in the sense that it generates the popup window with a URL in it that seems to contain the app's parameters. But when I paste that URL into a new tab or window, it just brings up the initial load state of the app, without any of the same parameters that I bookmarked.

This post says to make sure that the UI is enclosed in a function, which mine is, and to give the navbarPage an id. I'm using a dashboardPagePlus, not a navbarPage, and looking at the help file it doesn't appear that the dashboardPagePlus can take an id argument. Maybe I'm wrong? (I'm noticing now, as I google, that there's a new breaking version of shinyDashboardPlus, which means I may have to re-write a bunch of this app code... but I don't think that's what's causing the URL bookmarking to fail.)

Thanks for any ideas!

Hi Kaija,

some time ago I had the same issue with shinydashboard
(I'm not sure about the influnce of shinydashboardPlus, though). I just checked: using dashboardPagePlus instead of dashboardPage isn't making a difference.

I've had a short look at your code and I think the problem is, that you are calling the dashboardSidebar (sidebar = LEFTSIDEBAR) function outside of your UI function. For the bookmarking to work it needs to be called inside of the ui function.

Let's have a look at the example given in the documentation.

This works (directly giving dashboardSidebar as an argument to dashboardPage):

library(shiny)
library(shinydashboard)

ui <- function(req) {
  dashboardPage(
    dashboardHeader(),
    dashboardSidebar(
      sidebarMenu(id = "tabs",
        menuItem("Dashboard", tabName = "dashboard", icon = icon("dashboard")),
        menuItem("Widgets", icon = icon("th"), tabName = "widgets"),
        menuItem("Charts", icon = icon("bar-chart-o"),
          menuSubItem("Sub-item 1", tabName = "subitem1"),
          menuSubItem("Sub-item 2", tabName = "subitem2")
        )
      )
    ),
    dashboardBody(
      bookmarkButton()
    )
  )
}

server <- function(input, output, session) {}

shinyApp(ui, server, enableBookmarking = "url")

This also works (dashboardSidebar is still called inside the ui-function):

library(shiny)
library(shinydashboard)

ui <- function(req) {
  
  sidebar <- dashboardSidebar(
    sidebarMenu(id = "tabs",
                menuItem("Dashboard", tabName = "dashboard", icon = icon("dashboard")),
                menuItem("Widgets", icon = icon("th"), tabName = "widgets"),
                menuItem("Charts", icon = icon("bar-chart-o"),
                         menuSubItem("Sub-item 1", tabName = "subitem1"),
                         menuSubItem("Sub-item 2", tabName = "subitem2")
                )
    )
  )
  
  dashboardPage(
    dashboardHeader(),
    sidebar = sidebar,
    dashboardBody(
      bookmarkButton()
    )
  )
}

server <- function(input, output, session) {}

shinyApp(ui, server, enableBookmarking = "url")

However, defining the dashboardSidebar outside the of the ui function breaks the bookmarking:

library(shiny)
library(shinydashboard)

sidebar <- dashboardSidebar(
  sidebarMenu(id = "tabs",
              menuItem("Dashboard", tabName = "dashboard", icon = icon("dashboard")),
              menuItem("Widgets", icon = icon("th"), tabName = "widgets"),
              menuItem("Charts", icon = icon("bar-chart-o"),
                       menuSubItem("Sub-item 1", tabName = "subitem1"),
                       menuSubItem("Sub-item 2", tabName = "subitem2")
              )
  )
)

ui <- function(req) {
  dashboardPage(
    dashboardHeader(),
    sidebar = sidebar,
    dashboardBody(
      bookmarkButton()
    )
  )
}

server <- function(input, output, session) {}

shinyApp(ui, server, enableBookmarking = "url")

I'm not sure why this is the case and if this is mentioned in the documentation - if not it should be updated or a GitHub issue needs to be filed.

This is mentioned in the documentation here:

If you’ve used bookmarking in a Shiny app before, you know that the UI must be wrapped in a function. The sidebarMenu() must be called inside the UI function. It does not work to call it outside the UI function, saved the result in a variable, and then use that result in the UI function – if you do this, then the selected tab will save, but it will not restore.

I hope this helps.

Kind regards

Aha! That does help. I had missed that line in the documentation.

Once again, you swoop in to save me from my own mistakes in Shiny! I'm going to go try this now.

Okay, an update.

@ismirsehregal your solution mostly works. It's unfortunate that bookmarking doesn't allow separate definitions of UI elements, because I had found that very convenient for organizing my code, but shrug. It is what it is.

Accordingly, I moved the code in the separate files back into the main file, and now bookmarking works for parameters in the right and left sidebars... mostly.

Weirdly, one thing is still broken. menuItem 1 in the left sidebar pulls up the first tab of a tabset in the right sidebar, while menuItem 2 pulls up the second tab of that tabset. Bookmarking works to restore the settings in the left sidebar for both menu items, and it also works for the first tab of the right sidebar tabset. But it fails for the second tab of the right sidebar tabset.

There is a single bookmark button, not a separate one for each tab. All of the code for the UI in both tabs is defined in the main script, inside the UI function. I can't see any differences that would cause this to fail in one case and not in the other.

I don't know how to even begin making a reproducible example here, and I'm also not sure that getting this to work perfectly is important enough that I'll spend a lot of time on it. So if someone feels like digging into this as a fun project, by all means! But I realize that's asking a lot, so I'm okay letting this problem die. Meanwhile, @ismirsehregal solved like 90% of the problem, which is really excellent, so huge thanks to you :smile:

Which changes were suggested by @nirgrahamuk? Can I see them in your GitHub repo?

As I don't have the time to look deeper into this - just a few comments:

For an app with many inputs you should consider using server side bookmarking due to:

  • With an encoded state, the URL could become very long if there are many values. Some browsers have a limit of about 2,000 characters for the length of a URL, so if the bookmark URL is longer than that, it will not work properly in those browsers.

If there are values which aren't captured by shiny out of the box (like the leaflet checkbox) you can use the onBookmark callback to save additional values (an onRestore to load them again).

  # Save extra values in state$values when we bookmark
  onBookmark(function(state) {
    state$values$myVal <- vals$myVal
  })

If values, which should be captured by shiny aren't restored correctly, make sure you aren't overwriting the restored values on initializing the session.

Cheers!

1 Like

Oh no, that's so funny--I meant to tag you, not nirgrahamuk, but the two of you have helped me so much on this site that I chose the wrong name by accident. No other changes besides the ones you gave.

Thanks for the additional suggestions. I don't think server bookmarking will work because of the problem with having to save more and more bookmarks (and if you delete them, then presumably the bookmarked links won't work, which isn't ideal for my purposes). EDIT: server bookmarking also isn't currently supported on shinyapps.io, which is where I'm hosting my app now.

But thank you very much for the pointer with onBookmark. I think that will be useful for some of the leaflet parameters.