Skip to contents

For an overview of which router to choose (createHashRouter() vs. createMemoryRouter() vs. createBrowserRouter()) and the role of the reloadDocument prop, see vignette("routers", package = "reactRouter").

Install

#remotes::install_github("lgnbhl/reactRouter") # development version

install.packages("reactRouter")

Minimal example

You can add URL pages in Quarto document or R shiny like so:

library(reactRouter)
library(htmltools)

RouterProvider(
  router = createHashRouter(
    Route(
      path = "/",
      element = div(
        NavLink(to = "/", "Main"), " | ",
        NavLink(to = "/analysis", "Analysis"), hr(),
        Outlet()
      ),
      Route(index = TRUE, element = "Main content"),
      Route(path = "analysis", element = "Analysis content")
    )
  )
)

Usage with shiny

A minimal example using shiny.

library(shiny)
library(reactRouter)

ui <- RouterProvider(
  router = createHashRouter(
    Route(
      path = "/",
      element = div(
        NavLink(to = "/", "Main"),
        shiny::br(),
        NavLink(to = "/other", "Other"),
        Outlet()
      ),
      Route(index = TRUE, element = uiOutput(outputId = "uiMain")),
      Route(path = "other", element = uiOutput(outputId = "uiOther"))
    )
  )
)

server <- function(input, output, session) {
  output$uiMain <- renderUI( { p("Content home") } )
  output$uiOther <- renderUI( { p("Other content") })
}

shinyApp(ui = ui, server = server)

Usage with bslib

A minimal example using bslib.

library(reactRouter)
library(bslib)
#> 
#> Attaching package: 'bslib'
#> The following object is masked from 'package:utils':
#> 
#>     page
library(htmltools)

RouterProvider(
  router = createHashRouter(
    reactRouter::Route(
      path = "/",
      element = bslib::page_navbar(
        title = "reactRouter with bslib",
        nav_item(
          reactRouter::NavLink(
            "Home",
            to = "/"
          )
        ),
        nav_item(
          reactRouter::NavLink(
            "Analysis",
            to = "/analysis"
          )
        ),
        reactRouter::Outlet()
      ),
      reactRouter::Route(
        index = TRUE,
        element = div(
          tags$h3("Home page"),
          p("A basic example of reactRouter with bslib.")
        )
      ),
      reactRouter::Route(
        path = "analysis",
        element = "Content analysis"
      ),
      reactRouter::Route(path = "*", element = "Custom error 404")
    )
  )
)
#> Warning: Navigation containers expect a collection of
#> `bslib::nav_panel()`/`shiny::tabPanel()`s and/or
#> `bslib::nav_menu()`/`shiny::navbarMenu()`s. Consider using `header` or `footer`
#> if you wish to place content above (or below) every panel's contents.

Usage with muiMaterial

A minimal example using muiMaterial.

library(muiMaterial)
#> 
#> Attaching package: 'muiMaterial'
#> The following object is masked from 'package:reactRouter':
#> 
#>     Link

muiMaterialPage(
  CssBaseline(),
  RouterProvider(
    router = createHashRouter(
      Route(
        path = "/",
        element = Box(
          sx = list(flexGrow = 1),
          AppBar(
            position = "static",
            Toolbar(
              Typography(
                variant = "h6",
                component = "div",
                sx = list(mr = 1),
                "muiMaterial"
              ),
              NavLink(
                to = "/",
                Button(
                  color = "inherit",
                  "Home"
                )
              ),
              NavLink(
                to = "analysis",
                Button(
                  color = "inherit",
                  "Analysis"
                )
              )
            )
          ),
          Box(Outlet())
        ),
        reactRouter::Route(
          index = TRUE,
          element = Box("Home page", sx = list(p = 1))
        ),
        reactRouter::Route(
          path = "analysis",
          element = Box("Content analysis", sx = list(p = 1))
        ),
        reactRouter::Route(path = "*", element = "Error 404")
      )
    )
  )
)

Find more examples using muiMaterial here.

Usage with Shiny modules

# adapted from example of shiny.router
# https://github.com/Appsilon/shiny.router/tree/main/examples/shiny_modules
library(shiny)
library(reactRouter)

# This creates UI for each page.
page <- function(title, content, id) {
  ns <- NS(id)
  div(
    titlePanel(title),
    p(content),
    textOutput(ns("click_me"))
  )
}

# Both sample pages.
root_page <- page("Home page", "Home page clicks", "root")
second_page <- page("Other page", "Other page clicks", "second")

server_module <- function(id, clicks, power = 1) {
  moduleServer(id, function(input, output, session) {
    output$click_me <- renderText({
      as.numeric(clicks())^power
    })
  })
}

# Create output for our router in main UI of Shiny app.
ui <- RouterProvider(
  router = createHashRouter(
    Route(
      path = "/",
      element = div(
        NavLink(to = "/", "Main"), br(),
        NavLink(to = "/other", "Other"),
        actionButton("clicks", "Click me!"),
        Outlet()
      ),
      Route(index = TRUE, element = div(root_page)),
      Route(path = "other", element = div(second_page))
    )
  )
)

# Plug router into Shiny server.
server <- function(input, output, session) {
  clicks <- reactive({
    input$clicks
  })
  server_module("root", clicks = clicks, power = 1)
  server_module("second", clicks = clicks, power = 2)
}

# Run server in a standard way.
shinyApp(ui, server)

Example with Quarto

As React Router provides client routing, you can easily create multiple routes in a Quarto or R markdown documents:

# code to run in a Quarto document
# example adapted from: https://github.com/remix-run/react-router/tree/dev/examples/basic
library(reactRouter)
library(htmltools)

Layout <- div(
  style = "border:1px solid black;", # add border just for the example
  h1("Basic Example"),
  tags$p(
    paste0('This example demonstrates some of the core features of React Router
        including nested reactRouter::Route(), reactRouter::Outlet(),
        reactRouter::Link(), and using a "*" route (aka "splat route")
        to render a "not found" page when someone visits an unrecognized URL.'
    )
  ),
  # A "layout route" is a good place to put markup you want to
  # share across all the pages on your site, like navigation.
  tags$nav(
    tags$ul(
      tags$li(
        reactRouter::Link(to = "/", "Home")
      ),
      tags$li(
        reactRouter::Link(to = "/dashboard", "Dashboard")
      ),
      tags$li(
        reactRouter::Link(to = "/nothing-here", "Nothing Here")
      )
    )
  ),
  tags$hr(),
  # An <Outlet> renders whatever child route is currently active,
  # so you can think about this <Outlet> as a placeholder for
  # the child routes we defined above.
  reactRouter::Outlet()
)

RouterProvider(
  router = createHashRouter(
    Route(
      path = "/",
      element = Layout,
      Route(
        index = TRUE,
        element = div(
          tags$h2("Home"),
          tags$p("Home content")
        )
      ),
      Route(
        path = "dashboard",
        element = div(
          tags$h2("Dashboard"),
          tags$p("Dashboard here")
        )
      ),
      # Using path="*"" means "match anything", so this route
      # acts like a catch-all for URLs that we don't have explicit
      # routes for.
      Route(
        path = "*",
        element = div(
          tags$h2("Nothing to see here!"),
          tags$p(
            # reactRouter::Link() conflicts with muiMaterial::Link()
            reactRouter::Link(to = "/", "Go to the home page")
          )
        )
      )
    )
  )
)

Dynamic segments

A minimal example using dynamic segments (Route(path = ":id")). The route parameter is read with useParams() — no full page reload needed and no dependency on session$clientData$url_hash:

library(reactRouter)
library(htmltools)

RouterProvider(
  router = createHashRouter(
    Route(
      path = "/",
      element = div(
        h3("reactRouter with dynamic routes"),
        NavLink(to = "/project/1", "Project 1"), " | ",
        NavLink(to = "/project/2", "Project 2"),
        tags$hr(),
        Outlet()
      ),
      Route(index = TRUE, element = p("Select a project.")),
      Route(
        path = "project/:id",
        element = div(
          h4("Project: ", useParams(tags$strong(), selector = "id"))
        )
      )
    )
  )
)

Server-rendered Shiny output (uiOutput(), renderUI(), plotOutput(), htmlwidgets) works correctly with the default reloadDocument = FALSE — Shiny output bindings re-attach when React Router mounts the new route’s element. See vignette("routers", package = "reactRouter") for the full reference.

Alternatives

  • shiny.router implements a custom hash routing for shiny.
  • brochure provide a mechanism for creating natively multi-page shiny applications (but is still WIP).

More information

reactRouter wraps React Router v7 and exposes the data router API to R users.

More info about how to use React Router can be found in the official website.