Shiny’s bookmarking (enableBookmarking()) and shinysnap
solve neighbouring problems, and an app can use both. This vignette
compares them, shows how they coexist, and lists what to change when
moving an app’s save-and-restore feature from one to the other.
| bookmarking | shinysnap | |
|---|---|---|
| state lives in | a URL ("url") or a server directory
("server") |
a file the user downloads and uploads |
| restore happens | by loading the URL: a new session, a page reload | into the running session, no reload |
| dynamic UI | restored at construction through restoreInput() |
restored at construction and by the client as inputs appear |
| requires | a UI function, enableBookmarking() |
nothing in the UI |
| readable by people | no (URL-encoded JSON, or .rds files) |
yes (JSON) |
| survives app changes | no built-in help | app name and version in the file, validate and
migrate hooks, a report of what did not apply |
Bookmarking is the right tool for “send a colleague a link to what I am looking at”. shinysnap is the right tool for “save my work to a file, come back next month, possibly on a newer version of the app”.
shinysnap reuses the parts of bookmarking’s machinery that make sense mid-session, so an app that already bookmarks keeps working:
setBookmarkExclude() are excluded
from snapshots too.setSerializer()) are never
captured.restoreInput(), with the snapshot’s
values, and puts the previous context back when the restore
settles.The hooks mirror each other.
onBookmark(function(state) ...) writes into
state$values; so does snap_on_save().
onRestore() reads state$values;
snap_on_restore() reads the file’s values, and
snap_track() writes them back into your
reactiveValues for you.
snap_as_bookmark_url() encodes a snapshot the way URL
bookmarking does, so that a saved state can also be opened as a link,
provided the app has enableBookmarking("url") and a UI
function:
library(shinysnap)
snap <- list(
inputs = list(n = 100L, model = "complex", weights = c(0.5, 0.75)),
values = list(note = "baseline")
)
snap_as_bookmark_url(snap, base_url = "https://example.org/app/")
#> [1] "https://example.org/app/?_inputs_&n=100&model=%22complex%22&weights=%5B0.5%2C0.75%5D&_values_¬e=%22baseline%22"Inside a server function, pass session instead of
base_url and the protocol, host, port, and path the browser
used are filled in. The query string carries the same
_inputs_ and _values_ keys, with the same
encoding, as the URL session$doBookmark() produces for the
same state.
enableBookmarking() and the
bookmarkButton() with snap_download_button()
and snap_file_input() in the UI, and
snap_download_handler() and
snap_file_restore() in the server. The UI no longer needs
to be a function.onBookmark() hooks with
snap_on_save(), and onRestore() hooks with
snap_track() for reactiveValues (they are
written back for you) or snap_on_restore() for anything
else.setBookmarkExclude() calls; add
snap_enable(exclude = ...) patterns for ids that should
never be saved.snap_enable(),
and write a migrate hook the first time a saved value
changes meaning.snap_as_test_inputs() turns a saved file
into the session$setInputs() call of a
shiny::testServer() test.