25  rhino

WARNING: rhino isn’t like the previous two frameworks we’ve covered in this section, because rhino doesn’t create an app-package:

  • rhino apps rely on renv and box for managing imported dependencies (instead of the DESCRIPTION and NAMESPACE files in an R package).

  • rhino requires node.js, an open-source JavaScript runtime environment.

This chapter briefly describes a version of moviesApp built using rhino. The resulting app (rap) is in the 24_rhino branch.

The branch in this chapter is slightly different than the previous golem and leprechaun branches, because instead of loading, documenting, and installing rap, we’re going to re-initialize the IDE by selecting Session > Terminate R…

(a) Re-initialize the IDE
Figure 25.1: On the 24_rhino branch, re-initialize the IDE (instead of loading, documenting, and installing)

When the IDE re-opens, we see the rap files and notice the Build pane has been removed:

(a) rhino app IDE
Figure 25.2: Notice the Build pane has been removed from the 24_rhino branch

The Build pane is deactivated because rhino applications aren’t R packages.1

Launch the application in rap by opening the app.R file and clicking Run App (or by passing rhino::app() into the Console).

Launch app with the shinypak package:

(a) Calling rhino::app()
Figure 25.3: Running the application in rap

I’ve created the shinypak R package In an effort to make each section accessible and easy to follow:

Install shinypak using pak (or remotes):

# install.packages('pak')

Review the chapters in each section:

list_apps(regex = 'rhino')
## # A tibble: 1 × 2
##   branch   last_updated       
##   <chr>    <dttm>             
## 1 24_rhino 2024-04-11 12:10:16

Launch the app:

launch(app = "24_rhino")

Download the app:

get_app(app = "24_rhino")

25.1 rap (a rhino app)

The files in rap are below:

├── .Rprofile
├── .github/
   └── workflows
├── .gitignore
├── .lintr
├── .renvignore
├── .rscignore        
├── README.md
├── app
   ├── js
   ├── logic
   ├── main.R
   ├── static
   ├── styles
   └── view
├── app.R
├── config.yml
├── dependencies.R.
├── moviesApp.Rproj
├── renv
   ├── .gitignore.   
   ├── activate.R
   ├── library
   ├── settings.json
   └── staging
├── renv.lock
├── rhino.yml
└── tests
    ├── cypress
    ├── cypress.json
    └── testthat

24 directories, 31 files
Activates the renv package
CI/CD via GitHub actions
Lintr (from lintr package)
renv ignore (works like .gitignore)
rhino app dependencies
renv library of packages in app project

As we can see, most of the standard R package folders and files are missing from rap, because rhino applications use the box package for importing dependencies and organizing code.2

25.2 rhino features

The rhino website explains the philosophy behind the application structure above, so I won’t repeat that information here. However, I highly recommend reading the available documentation on rhino and box before deciding to adopt this framework.3

25.3 box modules

A box module (not to be confused with a Shiny module) is a collection of .R scripts in a specified folder. The modules in a new rhino app are stored in the app/logic/ and app/view/ folders:4

├── js/
├── logic/
├── main.R
├── static/
├── styles/
└── view/

6 directories, 1 file
JavaScript code
Non-shiny code
Primary app file
Static .js or .css
App CSS files
Shiny modules and app code

25.3.1 Utility functions

In rap, I’ve placed the non-Shiny utility functions (i.e., the business logic) in app/logic:

├── __init__.R
├── data.R
└── plot.R

1 directory, 4 files
Load movies data
scatter_plot() utility function

25.3.2 Shiny modules

Our Shiny box modules are placed in app/view, and separated into inputs and display:

├── __init__.R
├── display.R
└── inputs.R

1 directory, 3 files
similar to the code from R/mod_var_input.R
similar to the code from R/mod_scatter_display.R

app/view/inputs collects and returns the reactive values from the UI. The app/view/display module includes the app/logic/data and app/logic/plot modules.

# app/view/display.R

# import data and plot modules
  app / logic / data,
  app / logic / plot

25.4 app/main.R

The app/main.R file contains the primary UI and Server functions for the application. This file adds the shiny functions and the inputs and display modules from app/view:

# app/main.R

# shiny functions
    NS, fluidPage, sidebarLayout, sidebarPanel,
    mainPanel, fluidRow, column, tags, icon,
    textOutput, moduleServer, renderText

# import modules
  # load inputs module ----
  app / view / inputs,
  # load display module ----
  app / view / display

Note that we don’t need to import app/logic modules in app/main.R, because they’re imported in their respective app/view modules.

25.5 Tests

rhino apps have support for testing with testthat, shiny::testServer(), shinytest2, and Cypress.

├── cypress
   └── integration
       └── app.spec.js
├── cypress.json
└── testthat
    └── test-main.R

4 directories, 3 files
Cypress test infrastructure
testthat test infrastructure

Below is the boilerplate test code in the tests/testthat/test-main.R file:



test_that("main server works", {
  testServer(server, {
    expect_equal(output$message, "Hello!")
box module importing test package functions
Using shiny::testServer() and testthat::test_that() functions in test.

I’ve included tests for the utility functions and modules in the 24_rhino branch, but I’ll cover testing with rhino elsewhere.5

25.6 rhino dependencies

In rhino apps, dependencies are managed by renv and the dependencies.R file. The renv package is designed to,

“create[s] and manage[s] project-local R libraries, save[s] the state of these libraries to a ‘lockfile’, and later restore[s] the library as required.” 6

The rhino::pkg_install() helper function updates both the dependencies.R file and renv library. Using dependencies.R, renv, and box modules removes the need to manage dependencies in a DESCRIPTION or NAMESPACE file.7



rhino takes a novel and innovative approach to developing Shiny applications (and covering all the ways they differ from app-packages is beyond the scope of this book). Feel free to review the code in the 24_rhino branch for a better understanding of how the box modules are structured and used within the ui and server.

The rhino framework isn’t used as wildly golem,8 but it’s been gaining popularity (and has been used in a recent pilot FDA submission).

rhino CRAN downloads

Please open an issue on GitHub

  1. I re-initialize the session on the 24_rhino branch so I’m not tempted to load, document, install, or test the code using the IDE.↩︎

  2. Imported dependencies in rhino apps use box modules instead of the DESCRIPTION and NAMESPACE.↩︎

  3. Be sure to read up on testing box modules and rhino applications with cypress and shinytest2.↩︎

  4. rhino recommends placing non-Shiny code in the app/logic folder and keeping all Shiny modules and reactive code in app/view.↩︎

  5. See the Shiny frameworks supplemental website for more information on testing your rhino app.↩︎

  6. As described in renv’s DESCRIPTION file↩︎

  7. Be sure to read the renv configuration article for a better understanding on how it works with rhino apps.↩︎

  8. Check for yourself on cran-downloads↩︎