| Title: | Multipage 'Shiny' Apps |
| Version: | 1.0.0 |
| Description: | Build natively multi-page 'Shiny' applications, serving content on several endpoints instead of a single page. Pages are declared with their own user interface and server function and are dispatched by URL with 'routr', including parameterised routes. Redirections, request and response middleware, and helpers to read and set cookies are also provided. |
| License: | MIT + file LICENSE |
| URL: | https://github.com/colinfay/brochure, https://github.com/ColinFay/brochure |
| BugReports: | https://github.com/ColinFay/brochure/issues |
| Imports: | attempt (≥ 0.3.1), htmltools, purrr, rlang (≥ 0.4.12), routr (≥ 2.0.0), shiny (≥ 1.6.0) |
| Suggests: | golem (≥ 0.3.0), knitr, rmarkdown, testthat (≥ 3.0.0), usethis, withr |
| Config/testthat/edition: | 3 |
| VignetteBuilder: | knitr |
| Encoding: | UTF-8 |
| Config/roxygen2/version: | 8.0.0 |
| NeedsCompilation: | no |
| Packaged: | 2026-10-02 12:53:55 UTC; colinfay |
| Author: | Colin Fay |
| Maintainer: | Colin Fay <contact@colinfay.me> |
| Repository: | CRAN |
| Date/Publication: | 2026-10-14 11:10:22 UTC |
brochure: Multipage 'Shiny' Apps
Description
Build natively multi-page 'Shiny' applications, serving content on several endpoints instead of a single page. Pages are declared with their own user interface and server function and are dispatched by URL with 'routr', including parameterised routes. Redirections, request and response middleware, and helpers to read and set cookies are also provided.
Author(s)
Maintainer: Colin Fay contact@colinfay.me (ORCID) [copyright holder]
Authors:
Colin Fay contact@colinfay.me (ORCID) [copyright holder]
See Also
brochureApp() to build an app, page() to declare a page, and
vignette("brochure") to get started.
Create a brochureApp
Description
This function is to be used in place of shinyApp(). It takes a series of
page()s, each with its own url, UI and server function, and serves them
from a single Shiny application.
Usage
brochureApp(
...,
onStart = NULL,
options = list(),
enableBookmarking = NULL,
content_404 = "Not found",
basepath = "",
req_handlers = list(),
res_handlers = list(),
wrapped = shiny::tagList
)
Arguments
... |
The |
onStart |
A function that will be called before the app is actually run.
This is only needed for |
options |
Named options that should be passed to the |
enableBookmarking |
Can be one of |
content_404 |
The content served when no |
basepath |
The path your app is served under by a reverse proxy. It is
removed from the incoming url, so that what is left matches the href of your
|
req_handlers |
a list of functions that can manipulate the |
res_handlers |
A list of functions that can manipulate the httpResponse
object before it is send to the browser. Each function must take a |
wrapped |
A function taking the UI of a page and returning the UI
actually served. This is how every page gets the same shell: |
Details
Behind a reverse proxy, basepath tells the app where it is mounted: it is
removed from the incoming path, and prepended to the urls the app emits.
On Posit Connect the mount is picked up on its own, from the
RStudio-Connect-App-Base-URL header, and basepath is not needed. That
header is not part of any published contract, so set basepath if you want
the behaviour pinned. Brochure also injects a small script registering the
handler server_redirect() talks to, which prefixes internal targets with
the mount.
Value
A shiny.appobj
See Also
page() to declare a page, redirect() to answer an url with a
redirection, and vignette("deployment") to serve the app under a prefix.
Examples
library(shiny)
app <- brochureApp(
page(
href = "/",
ui = tagList(h1("Home")),
server = function(input, output, session) {}
),
page(
href = "/contact",
ui = tagList(h1("Contact"))
),
redirect(from = "/index.html", to = "/")
)
# Then run it as you would any Shiny app:
if (interactive()) {
shiny::runApp(app)
}
Get the keys of a parameterised page
Description
A page declared with a parameterised href (e.g. "/who/:id") exposes the
values matched in the URL through get_keys(). Call it with no argument
inside a page server, and pass it the request inside a page ui.
Usage
get_keys(x = shiny::getDefaultReactiveDomain())
Arguments
x |
A shiny session, or the |
Value
A named list of the values matched in the page href, or NULL
outside of a brochure page.
Examples
library(shiny)
page(
href = "/who/:id",
ui = function(request) {
h1(get_keys(request)$id)
},
server = function(input, output, session) {
print(get_keys())
}
)
Golem Hook function
Description
Golem Hook function
Usage
golem_hook(path, package_name, ...)
Arguments
path |
Name of the folder to create the package in. This will also be used as the package name. |
package_name |
Package name to use. By default, |
... |
Arguments passed from |
Value
Used for side effect
Examples
if (requireNamespace("golem") & interactive()) {
golem::create_golem(file.path(tempdir(), "myapp"), project_hook = golem_hook)
}
Add page
Description
Module template for golem
Usage
new_page(name, path, export, ...)
Arguments
name |
The name of the module. |
path |
The path to the R script where the module will be written.
Note that this path will not be set by the user but via
|
export |
Should the module be exported? Default is |
... |
Arguments to be passed to the |
Value
Used for side effect
Examples
if (requireNamespace("golem") & interactive()) {
golem::add_module(name = "home", module_template = brochure::new_page)
}
A Brochure Page
Description
A page is an url, a UI and a server function. Opening it starts a Shiny session of its own, separate from any other page of the app.
Usage
page(
href,
ui = tagList(),
server = function(input, output, session) {
},
method = "GET",
req_handlers = list(),
res_handlers = list()
)
Arguments
href |
The endpoint to serve the UI on. It can carry parameters, as in
|
ui |
The UI definition of the app (for example, a call to
If bookmarking is enabled (see |
server |
A function with three parameters: |
method |
The HTTP method the page answers to. Defaults to |
req_handlers |
a list of functions that can manipulate the |
res_handlers |
A list of functions that can manipulate the httpResponse
object before it is send to the browser. Each function must take a |
Details
Requests are matched against hrefs by the routr package, so an href is written the way routr writes a path:
-
"/contact"matches that path and nothing else. -
":name"matches exactly one segment and captures it, so"/who/:id"matches/who/colinbut neither/whonor/who/colin/edit. Use several of them if you need to:"/pair/:a/:b". -
"*"matches whatever is left, so"/files/*"matches/files/a/b/c. The captured value is named*1.
A trailing slash never matters: /contact and /contact/ are the same page.
Pages are tried in the order you passed them to brochureApp(), and the
first match wins. That holds for the session a page opens as well, which is
resolved on the path alone: the websocket handshake is a GET whatever
method the page answers on, so two pages sharing an href run the server of
the first one declared, whichever of them was served. That matters when two hrefs can match the same url:
declared as page("/who/me") then page("/who/:id"), a request for
/who/me gets the first; declared the other way round, the parameterised
page catches it and the static one is never reached. Put the specific ones
first.
See the routr documentation at https://routr.data-imaginist.com/ for the full path syntax.
Value
A brochure_page object, to be passed to brochureApp().
See Also
get_keys() to read the parameters of an href, and
vignette("handlers") for req_handlers and res_handlers.
Examples
library(shiny)
page(
href = "/page2",
ui = tagList(
h1("This is my second page"),
plotOutput("plotb")
)
)
Read the cookies sent with a request
Description
get_cookies() returns the raw Cookie header of the request the session
was opened with, and parse_cookie_string() turns that header into a named
vector.
Usage
parse_cookie_string(cookie_string)
get_cookies(session = shiny::getDefaultReactiveDomain())
Arguments
cookie_string |
The cookie string to parse, as |
session |
The |
Value
For get_cookies(), the Cookie header as a single string, or
NULL when the request carried none. For parse_cookie_string(), a named
character vector of the cookies it holds, or "" when given NULL. Index
it with single brackets: a visitor arriving without a cookie gives nothing
to index into, and [[ errors where [ returns NA.
See Also
set_cookie() to set one from a response handler, and
vignette("cookies") for carrying a session across pages.
Examples
parse_cookie_string("brochure_session=63422; brochure_cookie=3958")
Redirection
Description
Answers an url with an HTTP redirection, before any Shiny code runs. To
redirect from inside a page server instead, see server_redirect().
Usage
redirect(from, to, code = 301, method = "GET")
Arguments
from |
the url to redirect from |
to |
the url to redirect to |
code |
redirecting http code, one of 301, 302, 303, 307 or 308 |
method |
The HTTP method the redirection answers to.
Defaults to |
Value
A redirect object, to be passed to brochureApp().
See Also
server_redirect() to redirect from inside a page server.
Examples
redirect(
from = "/index.html",
to = "/"
)
# 301 says the move is permanent, 302 that it is temporary
redirect(
from = "/old",
to = "/new",
code = 302
)
Do a server side redirection
Description
Do a server side redirection
Usage
server_redirect(to, session = shiny::getDefaultReactiveDomain())
Arguments
to |
the destination of the redirection: a path ( |
session |
shiny session object, default is |
Value
Used for side effect
See Also
redirect() to answer an url with an HTTP redirection instead.
Examples
library(shiny)
# `server_redirect()` is called from a page server, so it needs a session:
page(
href = "/login",
ui = tagList(actionButton("go", "Take me home")),
server = function(input, output, session) {
observeEvent(input$go, {
server_redirect("/")
})
}
)
Middleware to set cookies
Description
Please read https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Set-Cookie for more information. Description of parameters is taken from this page.
Usage
set_cookie(
res,
name,
value,
expires = NULL,
max_age = NULL,
domain = NULL,
path = NULL,
secure = NULL,
http_only = TRUE,
same_site = "Lax"
)
remove_cookie(res, name, path = NULL, domain = NULL)
Arguments
res |
An httpResponse object |
name |
A cookie-name can be any US-ASCII characters,
except control characters, spaces, or tabs. It also must
not contain a separator character like the following:
[ ]: R:%20 |
value |
A cookie-value can optionally be wrapped in double quotes and include any US-ASCII characters excluding control characters, Whitespace, double quotes, comma, semicolon, and backslash. |
expires |
The maximum lifetime of the cookie as an HTTP-date timestamp. Please enter an ISO 8601 datetime format. |
max_age |
Number of seconds until the cookie expires. A zero or negative number will expire the cookie immediately. If both Expires and Max-Age are set, Max-Age has precedence. |
domain |
Host to which the cookie will be sent. |
path |
A path that must exist in the requested URL,
or the browser won't send the Cookie header. |
secure |
Cookie is only sent to the server when a request is made with the https: scheme (except on localhost), and therefore is more resistent to man-in-the-middle attacks. |
http_only |
Forbids JavaScript from accessing the
cookie, for example, through the Document.cookie property.
Defaults to |
same_site |
Controls whether a cookie is sent with
cross-origin requests, providing some protection against
cross-site request forgery attacks (CSRF). Defaults to |
Value
the httpResponse, with a cookie header
See Also
get_cookies() to read them back, and vignette("cookies") for
carrying a session across pages.
Examples
set_cookie(
shiny::httpResponse(),
"this",
12
)