--- title: "Introduction to reactRouter" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Introduction to reactRouter} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>" ) ``` ### Install ``` r #remotes::install_github("lgnbhl/reactRouter") # development version install.packages("reactRouter") ``` ## Minimal example ```{r setup} library(reactRouter) ``` You can add URL pages in Quarto document or R shiny like so: ```{r basic} 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](https://shiny.posit.co/). ```r 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](https://rstudio.github.io/bslib/). ```r 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](https://felixluginbuhl.com/shinyMaterialUI/). ```r # 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](https://felixluginbuhl.com/shinyMaterialUI/). ### Usage with Shiny modules ```r # 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: ``` {r} # 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 renders whatever child route is currently active, # so you can think about this 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/*")`. ```r 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: ```r reactRouterExample("dynamic-segments") ``` ### Alternatives - [shiny.router](https://appsilon.github.io/shiny.router/) implements a custom hash routing for shiny. - [brochure](https://github.com/ColinFay/brochure) provide a mechanism for creating natively multi-page shiny applications (but is still WIP). ### More information **reactRouter** implements React Router [v.6.30.0](https://reactrouter.com/6.30.0). More info about how to use React Router can be found in the [official website](https://reactrouter.com/6.30.0).