reactRouter brings React Router, the world’s most popular React routing library, to R and Shiny.
Why reactRouter?
reactRouter works with Shiny apps and Quarto documents — and also on its own. It extends whatever you are already building with new features:
- URL-based navigation — your application behaves like a real website, with a home page and multiple sub-pages (e.g. one per report or analysis), each with its own shareable URL.
- Reactivity without a server — data loaders fetch and display data as users navigate, entirely in the browser, without requiring a running R server.
- Static hosting — the output is plain HTML/JS, deployable to GitHub Pages, Netlify, Posit Connect, or any static file host (if you don’t use it with Shiny).
- AI-friendly — React Router is one of the most widely used JavaScript libraries, so AI tools like Claude, ChatGPT, or Copilot know it well. Ask an AI to generate React Router code, adapt it to R, and you’re done — no React knowledge needed.
Used alone, reactRouter brings the best of both worlds: the reactivity of Shiny (data that updates as users interact) and the simplicity of Quarto (multiple pages as static files, hostable anywhere, no server to maintain or pay for).
Usage
Create URL pages easily:
library(reactRouter)
library(htmltools)
ui <- 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")
)
)
)
# htmltools::save_html(ui, "index.html")
htmltools::browsable(ui)Or use data loaders to fetch and display data client-side — no R server needed:
library(reactRouter)
library(htmltools)
# local or fetch an API
people_json <- jsonlite::toJSON(dplyr::starwars, dataframe = "rows", auto_unbox = TRUE)
ui <- RouterProvider(
router = createHashRouter(
Route(
path = "/",
element = div(
NavLink(to = "/", "Home"), " | ",
NavLink(to = "/people/1", "Luke"),
hr(), Outlet()
),
Route(index = TRUE, element = p("Try #/people/4 for Darth Vader.")),
Route(
path = "people/:id",
loader = JS(sprintf("({ params }) => (%s)[params.id - 1]", people_json)),
element = div(
useLoaderData(tags$h3(), selector = "name"),
useLoaderData(tags$p(), selector = "homeworld")
)
)
)
)
)
# htmltools::save_html(ui, "index.html")
htmltools::browsable(ui)Install
install.packages("reactRouter")
# remotes::install_github("lgnbhl/reactRouter") # development versionAcknowledgements
reactRouter is built on top of shiny.react, the R package by Appsilon that makes it possible to use React components in Shiny and Quarto.
Data loaders work seamlessly with any R package that uses shiny.react under the hood. This includes the MUI component ecosystem for R:
- muiMaterial — Material UI components (buttons, dialogs, tabs, and more)
- muiDataGrid — interactive data grids
- muiCharts — charts and visualisations
- muiTreeView — tree view components
Learn more about how to use data loaders with these R packages here.
Contribute
Found a bug or have a feature request? Open an issue. Pull requests are welcome.
Follow Felix Luginbuhl on LinkedIn for updates.
