# Tutorial: Using reactRouter with rhino and shiny.fluent

## Introduction

This tutorial demonstrates how to build a dynamic Shiny application using `reactRouter` for routing and `shiny.fluent` for modern UI components, all within the `rhino` framework. We’ll use Dota 2 API data as an example of routing multiple pages.

Initially, ensure you have the necessary rhino package installed. You can do this by running the following command in your R console:

    # Install rhino if not yet installed
    install.packages("rhino")

Next, you will need to create a new rhino project. If you haven’t already set up a rhino project, you can do so by running the following command in your R console:

    # Initialize a new rhino project (will create project scaffolding)
    rhino::init()

This will create a basic structure for your application. Add the following libraries to your `dependencies.R` file:

    # dependencies.R

    library(rhino) # App structure
    library(httr) # API requests
    library(shiny.fluent) # UI components
    library(reactRouter) # Client-side routing
    library(echarts4r) # Charting
    library(stringdist) # String matching
    library(treesitter) # Optional: Syntax parsing
    library(treesitter.r) # Optional: R syntax support

and then

    renv::snapshot()

Now we are ready to go.

## Building the Application

In this example, we will create a simple application that displays information about Dota 2 heroes. The application will have multiple routes, allowing users to navigate between different pages.

The components of the application will be structured as follows:

- home: the main page of the application, which will display a list of heroes
- menu: a navigation menu for the application, allowing users to navigate between different pages
- header: a header component that will be displayed on every page
- details: a page containing detailed information about a specific hero
- benchmark: a page that will display benchmark statistics for heroes
- ranks: a page that will display the ranks of heroes based on their performance

The final strucutre of the `app` will look like this:

    ├── app
    │   ├── js
    │   │   └── index.js
    │   ├── logic
    │   │   ├── data.R
    │   │   └── utils.R
    │   ├── main.R
    │   ├── static
    │   │   ├── css
    │   │   │   └── app.min.css
    │   │   ├── favicon.ico
    │   │   └── js
    │   │       └── app.min.js
    │   ├── styles
    │   │   └── main.scss
    │   └── view
    │       ├── benchmark.R
    │       ├── details.R
    │       ├── header.R
    │       ├── home.R
    │       ├── menu.R
    │       └── rank.R
    ├── app.R
    ├── config.yml
    ├── dependencies.R
    ├── renv.lock
    ├── rhino.yml
    └── run_dev.R

The main part of the application to address the routing and the UI components is in the `app.R` file.

    # app / main.R

    box::use(
      app / view / home,
      app / view / menu,
      app / view / details,
      app / view / benchmark,
      app / view / rank
    )

    # Define UI with namespaced modules
    ui <- function(id) {
      ns <- shiny::NS(id) # Namespace for module isolation
      shiny.fluent::fluentPage(
        reactRouter::HashRouter(
          reactRouter::Routes(
            # Home page route
            reactRouter::Route(path = "/", element = home$ui(ns("home"))),

            # Project-based nested routes
            reactRouter::Route(
              path = "/:projectId/*",
              element = menu$ui(ns("menu")), # Common layout/menu
              children = list(
                reactRouter::Route(
                  path = "details",
                  element = details$ui(ns("details"))
                ),
                reactRouter::Route(
                  path = "benchmark",
                  element = benchmark$ui(ns("benchmark"))
                ),
                reactRouter::Route(
                  path = "rank",
                  element = rank$ui(ns("rank"))
                )
              )
            ),

            # Fallback for undefined routes
            reactRouter::Route(path = "*", element = "Custom error 404")
          )
        )
      )
    }

    #' @export
    server <- function(id) {
      shiny::moduleServer(id, function(input, output, session) {
        hero_selected <- home$server("home")

        shiny::observe({
          shiny::req(hero_selected())

          print(paste0("hero_id selected: ", hero_selected()))
        })

        menu$server("menu", hero_selected = hero_selected)
        details$server("details", hero_selected = hero_selected)
        benchmark$server("benchmark", hero_selected = hero_selected)
        rank$server("rank", hero_selected = hero_selected)
      })
    }

This function defines the overall layout and routing of the application using reactRouter. It contains four key parts:

1. Top-level routing via `HashRouter()` and `Routes()`.

2. Root Route /: Displays the home page.

3. Nested Route /:projectId/*:

4. Displays a layout (menu) and child routes (details, benchmark, rank).

5. Each sub-route renders a different module UI (e.g., details\(ui, benchmark\)ui).

6. Fallback * Route: Catches any undefined paths and shows a custom 404 message.

## Running the Application

You can now run your app locally with the following script:

    # run_dev.R

    rhino::build_js()
    rhino::build_sass()
    shiny::runApp(port = 4929, launch.browser = FALSE)

## Recap

In this tutorial, dynamic Shiny application using:

- `rhino` for structured project setup.
- `shiny.fluent` for modern and interactive UI.
- `reactRouter` for dynamic and nested client-side routing.

This setup provides flexibility, scalability, and maintainability for more complex Shiny applications.