Introduction to reactRouter

Install

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

install.packages("reactRouter")

Minimal example

library(reactRouter)

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

library(reactRouter)

HashRouter(
  NavLink(to = "/", "Main"),
  NavLink(to = "/analysis", "Analysis"),
  Routes(
    Route(path = "/", element = "Main content"),
    Route(path = "/analysis", element = "Analysis content")
  )
)

Usage with shiny

A minimal example using shiny.

library(shiny)
library(reactRouter)

ui <- HashRouter(
  NavLink(to = "/", "Main"), 
  shiny::br(),
  NavLink(to = "/other", "Other"),
  Routes(
    Route(
      path = "/", 
      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)
library(htmltools)

reactRouter::HashRouter(
  bslib::page_navbar(
    title = "reactRouter with bslib",
    nav_item(
      reactRouter::NavLink(
        "Home", 
        to = "/"
      )
    ),
    nav_item(
      reactRouter::NavLink(
        "Analysis", 
        to = "/analysis"
      )
    ),
    reactRouter::Routes(
      reactRouter::Route(
        path = "/",
        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")
    )
  )
)

Usage with shinyMaterialUI

A minimal example using shinyMaterialUI.

# remotes::install_github("lgnbhl/shinyMaterialUI")
library(shinyMaterialUI)

HashRouter(
  Box(
    sx = list(flexGrow = 1),
    AppBar(
      position = "static",
      Toolbar(
        Typography(
          variant = "h6",
          component = "div",
          sx = list(mr = 1),
          "shinyMaterialUI"
        ),
        NavLink(
          to = "/",
          Button(
            color = "inherit",
            "Home"
          )
        ),
        NavLink(
          to = "analysis",
          Button(
            color = "inherit",
            "Analysis"
          )
        )
      )
    ),
      Box(
        reactRouter::Routes(
          reactRouter::Route(
            path = "/",
            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 with shinyMaterialUI 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 <- reactRouter::HashRouter(
  NavLink(to = "/", "Main"), br(),
  NavLink(to = "/other", "Other"),
  actionButton("clicks", "Click me!"),
  Routes(
    Route(
      path = "/", 
      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(
  # 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()
)

reactRouter::HashRouter(
  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.'
      )
    ),
    reactRouter::Routes(
      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(
              Link(to = "/", "Go to the home page")
            )
          )
        )
      )
    )
  )
)

Dynamic segments

A minimal example using dynamic segments, i.e. using Route(to = ":id/*").

library(shiny)
library(reactRouter)
library(bslib)

ui <- HashRouter(
  bslib::page(
    Link(
      to = "/", 
      h3("reactRouter with dynamic routes", class = "m-3"),
      style = "text-decoration: none; color: black"
    ),
    Routes(
      Route(
        path = "/",
        element = div(
          # tags$a() necessary to observe `url_hash` in session
          NavLink(
            to = "project/1/overview",
            "Project 1"
          ),
          tags$br(),
          NavLink(
            to = "project/2/overview",
            "Project 2"
          )
        )
      ),
      Route(
        path = "project/:id/*", 
        element = div(
          NavLink(
            to = "overview",
            "Overview"
          ),
          tags$br(),
          NavLink(
            to = "analysis",
            "Analysis"
          ),
          Outlet()
        ),
        children = list(
          reactRouter::Route(
            path = "overview",
            element = uiOutput("uiOverview")
          ),
          reactRouter::Route(
            path = "analysis",
            element = uiOutput("uiAnalysis")
          )
        )
      )
    )
  )
)

server <- function(input, output, session) {
  
  url_hash <- shiny::reactiveVal(value = NA)

  # update reactive values based on url hash
  observeEvent(session$clientData$url_hash, {
    current_url_hash <- session$clientData$url_hash
    print(current_url_hash)
    url_hash(current_url_hash)
  })
  
  output$uiOverview <- renderUI({
    url_hash()
  })
  output$uiAnalysis <- renderUI({
    url_hash()
  })
}

shinyApp(ui, server)

Run a more advanced example of dynamic routes with:

reactRouterExample("dynamic-segments")

Alternatives

More information

reactRouter implements React Router v.6.30.0.

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

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.