# 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

- 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 implements React Router v.6.30.0.

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