9  Resources

Published

2024-12-20


  • The inst/ folder is used to store files/resources that are available when your package is installed (i.e., this folder ‘will be copied recursively to the installation directory.’)

  • system.file() is the ‘file path accessor’ function we can use to locate files in the installed version of our app-package

  • Use addResourcePath() with system.file() and inst/ to include external files in your application.

Workflow: for a file located in inst/www/file.png:

# add path to app
addResourcePath('www', system.file('www', package = 'pkg'))
# use path without 'inst/' prefix in UI
img(src = 'www/shiny.png')

In this chapter, we’ll cover how to add external resources (i.e., the files previously stored and served from the www/ folder) to an app-package and the many uses of the inst/ folder.

When we launch our app, we see the Shiny logo (shiny.png) in the www/ folder is not being loaded into the UI:

launch_app(run = 'p')

Launch app with the shinypak package:

launch('09.1_inst-wwww')

www/shiny.png is not visible when we launch the app

www/shiny.png is not visible when we launch the app

Shiny’s internal functions previously automatically handled serving the contents of www/.1 Now that we’ve converted our application into a package, we’ll need to tell the application where to find these resources explicitly.2

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')
pak::pak('mjfrigaard/shinypak')
library(shinypak)

List the apps in this chapter:

list_apps(regex = '09')

Launch apps with launch()

launch(app = '09.1_inst-www')

Download apps with get_app()

get_app(app = '09.1_inst-www')

9.1 Package files

While developing, we get used to accessing and interacting with our package files from the Files pane or Explorer window:

‘Source’ files for sap in Files pane

‘Source’ files for sap in Files pane

‘Source’ files for sap in Explorer window

‘Source’ files for sap in Explorer window

However, these aren’t the files that get bundled into an R package. But we can use fs::path_package() and fs::dir_tree() to print a folder tree of our installed package files:

Installed package files

Below are folder trees for our source package and our installed package:

sap/
├── DESCRIPTION 
├── NAMESPACE 
├── R 
│   ├── data.R
│   ├── display_type.R
│   ├── mod_scatter_display.R
│   ├── mod_var_input.R
│   ├── launch_app.R
│   ├── movies_server.R
│   ├── movies_ui.R
│   └── scatter_plot.R
├── README.md
├── app.R
├── data 
│   ├── movies.RData
│   └── movies.rda
├── inst
│   └── extdata 
│       └── movies.fst
├── man
│   ├── display_type.Rd
│   ├── mod_scatter_display_server.Rd
│   ├── mod_scatter_display_ui.Rd
│   ├── mod_var_input_server.Rd
│   ├── mod_var_input_ui.Rd
│   ├── movies.Rd
│   ├── launch_app.Rd
│   ├── movies_server.Rd
│   ├── movies_ui.Rd
│   └── scatter_plot.Rd
├── sap.Rproj
└── www
    └── shiny.png
sap/
├── DESCRIPTION
├── INDEX
├── Meta
│   ├── Rd.rds
│   ├── data.rds
│   ├── features.rds
│   ├── hsearch.rds
│   ├── links.rds
│   ├── nsInfo.rds
│   └── package.rds
├── NAMESPACE
├── R
│   ├── sap
│   ├── sap.rdb
│   └── sap.rdx
├── data
│   ├── Rdata.rdb
│   ├── Rdata.rds
│   └── Rdata.rdx
├── extdata
│   └── movies.fst
├── help
│   ├── AnIndex
│   ├── aliases.rds
│   ├── sap.rdb
│   ├── sap.rdx
│   └── paths.rds
└── html
    ├── 00Index.html
    └── R.css
1
DESCRIPTION is in both source and installed versions, and format remains unchanged.
2
NAMESPACE is in both source and installed versions, and format remains unchanged.
3
The R/ folder is in both source and installed versions, but these files have been converted to sap, sap.rdb, sap.rdx files.
4
The data/ folder is in both source and installed versions, but these files have been converted to Rdata.rdb, Rdata.rds, Rdata.rdx files.
5
The extdata/ folder is in both source and installed versions, and the movies.fst file remains unchanged.

Some of the source folders and files we’ve been working with remain in the installed version of our package (i.e., the DESCRIPTION, NAMESPACE, and extdata/movies.fst). Still, the majority have been converted to the installed package version format.

9.1.1 system.file()

Hopefully, seeing an installed package’s contents demystifies what happens when we run devtools::install(). When we want to add non-R package files to our app (like the shiny.png logo), we store these files in the inst/folder and access them with system.file().3

The contents of the inst/ subdirectory will be copied recursively to the installation directory. Subdirectories of inst/ should not interfere with those used by R (currently, R/, data/, demo/, exec/, libs/, man/, help/, html/ and Meta/, and earlier versions used latex/, R-ex/).” - Writing R extensions, Package subdirectories

system.file() gives us access to the package files on installation (i.e., the files we saw in the folder tree above). We used system.file() in the previous chapter on data to access the movies.fst file in inst/extdata/:

system.file() is accessing movies.fst from the installed location
fst::read_fst(
  path = system.file("extdata/", "movies.fst", 
                     package = "sap")
  )

In essence, movies.fst has the ‘source’ package and the ‘installed’ locations.

Source package files

# What we see
inst/
  └── extdata/
        └── movies.fst

Installed package files

# What R sees
└── extdata/
      └── movies.fst

I found exploring the structure of the inst/ folder in other packages incredibly helpful in understanding how to use this directory for package development. For example, the inst/examples/ folder in the shiny package holds a variety of Shiny apps:

/path/to/install/Library/R/x86_64/4.2/library/shiny/examples/
├── 01_hello
   ├── DESCRIPTION
   ├── Readme.md
   └── app.R
├── 02_text
   ├── DESCRIPTION
   ├── Readme.md
   └── app.R
├── 03_reactivity
   ├── DESCRIPTION
   ├── Readme.md
   └── app.R
├── 04_mpg
   ├── DESCRIPTION
   ├── Readme.md
   └── app.R
├── 05_sliders
   ├── DESCRIPTION
   ├── Readme.md
   └── app.R
├── 06_tabsets
   ├── DESCRIPTION
   ├── Readme.md
   └── app.R
├── 07_widgets
   ├── DESCRIPTION
   ├── Readme.md
   └── app.R
├── 09_upload
   ├── DESCRIPTION
   ├── Readme.md
   └── app.R
├── 10_download
   ├── DESCRIPTION
   ├── Readme.md
   └── app.R
└── 11_timer
    ├── DESCRIPTION
    ├── Readme.md
    └── app.R

These files are used in shiny::runExample()):

shiny::runExample(example = '11_timer')

9.2 Image files

To include the contents of www/ in our app-package, we’ll need to move www/ into inst/, and then access its contents with system.file().4

9.2.1 addResourcePath()

The addResourcePath() function will add a “directory of static resources to Shiny’s web server.5 In sap, we want to add the www directory, which includes the shiny.png file.

Current www location

├── inst
   └── extdata
       └── movies.fst
└── www
    └── shiny.png

 

New www location

inst/
  ├── extdata/
     └── movies.fst
  └── www/
      └── shiny.png

In R/movies_ui.R, we’ll include the addResourcePath() at the top of the tagList() and reference the image in img() using only the subfolder in the path:

show/hide movies_ui()
movies_ui <- function() {
  addResourcePath(
    prefix = 'www',
    directoryPath = system.file('www', package = 'sap'))
  tagList(
    fluidPage(
      theme = shinythemes::shinytheme("spacelab"),
      titlePanel(
        div(
          img(
            src = "www/shiny.png",
            height = 60,
            width = 55,
            style = "margin:10px 10px"
            ), 
         "Movie Reviews"
        )
      ),
      sidebarLayout(
        sidebarPanel(
          mod_var_input_ui("vars")
        ),
        mainPanel(
          mod_scatter_display_ui("plot")
        )
      )
    )
  )
} 
1
Prefix (or folder name) of installed location
2
Path to installed package files
3
Reference to installed package image file

The application includes the image file after loading, documenting, and installing our package:



Ctrl/Cmd + Shift + L / D / B

library(sap)
launch_app(run = 'p')

inst/www accessible with addResourcePath()

inst/www accessible with addResourcePath()

We can also use inst/ to store alternate image files and configure the UI to display a different layout. This method ensures our app has the exact same functionality, but a different UI layout.6

bslib is a dependency of shiny, so we don’t need to import this with usethis::use_package() (see output from pak::pkg_deps_tree('shiny') below)

shiny 1.9.1 ✨                                                             
├─httpuv 1.6.15 ✨🔧
 ├─later 1.3.2 ✨🔧
 │ ├─Rcpp 1.0.13 ✨🔧
 │ └─rlang 1.1.4 ✨🔧
 ├─promises 1.3.0 ✨🔧
 │ ├─fastmap 1.2.0 ✨🔧
 │ ├─later
 │ ├─magrittr 2.0.3 ✨🔧
 │ ├─R6 2.5.1 ✨
 │ ├─Rcpp
 │ └─rlang
 ├─R6
 └─Rcpp
├─mime 0.12 ✨🔧
├─jsonlite 1.8.8 ✨🔧
├─xtable 1.8-4 ✨
├─fontawesome 0.5.2 ✨
 ├─rlang
 └─htmltools 0.5.8.1 ✨🔧
   ├─base64enc 0.1-3 ✨🔧
   ├─digest 0.6.36 ✨🔧
   ├─fastmap
   └─rlang
├─htmltools
├─R6
├─sourcetools 0.1.7-1 ✨🔧
├─later
├─promises
├─crayon 1.5.3 ✨
├─rlang
├─fastmap
├─withr 3.0.1 ✨
├─commonmark 1.9.1 ✨🔧
├─glue 1.7.0 ✨🔧
├─bslib 0.8.0 ✨
 ├─base64enc
 ├─cachem 1.1.0 ✨🔧
 │ ├─rlang
 │ └─fastmap
 ├─fastmap
 ├─htmltools
 ├─jquerylib 0.1.4 ✨
 │ └─htmltools
 ├─jsonlite
 ├─lifecycle 1.0.4 ✨
 │ ├─cli 3.6.3 ✨🔧
 │ ├─glue
 │ └─rlang
 ├─memoise 2.0.1 ✨
 │ ├─rlang
 │ └─cachem
 ├─mime
 ├─rlang
 └─sass 0.4.9 ✨🔧
   ├─fs 1.6.4 ✨🔧
   ├─rlang
   ├─htmltools
   ├─R6
   └─rappdirs 0.3.3 ✨🔧
├─cachem
└─lifecycle

Key:  ✨ new | 🔧 compile

The updated movies_ui() function below has an optional bslib argument that will change the layout an display an alternate image (stored in inst/www/bootstrap.png).

show/hide updated movies_ui()
movies_ui <- function(bslib = FALSE) {
  addResourcePath(
    prefix = 'www', 
    directoryPath = system.file('www', package = 'sap'))
  if (isFALSE(bslib)) {
  tagList(
    bslib::page_fillable(
      h1("Movie Reviews"),
      bslib::layout_sidebar(
        sidebar =
          bslib::sidebar(
            title = tags$h4("Sidebar inputs"),
            img(
              src = "www/shiny.png",
              height = 60,
              width = 55,
              style = "margin:10px 10px"
            ),
            mod_var_input_ui("vars")
          ),
        bslib::card(
          full_screen = TRUE,
          bslib::card_header(
            tags$h4("Scatter Plot")
          ),
          bslib::card_body(fillable = TRUE,
            mod_scatter_display_ui("plot")
          ),
          bslib::card_footer(
            tags$blockquote(
              tags$em(
                tags$p(
                  "The data for this application comes from the ",
                  tags$a("Building web applications with Shiny",
                    href = "https://rstudio-education.github.io/shiny-course/"
                  ),
                  "tutorial"
                )
              )
            )
          )
        )
      )
    )
  )
  } else {
    tagList(
      bslib::page_fillable(
        title = "Movie Reviews (bslib)",
        theme = bslib::bs_theme(
          bg = "#101010",
          fg = "#F6F5F5",
          primary = "#EE6F57",
          secondary = "#32E0C4",
          success = "#FF4B5C",
          base_font = sass::font_google("Ubuntu"),
          heading_font = sass::font_google("Ubuntu")
        ),
        bslib::layout_sidebar(
          sidebar = bslib::sidebar(
            open = TRUE,
            mod_var_input_ui("vars")
          ),
          bslib::card(
            full_screen = TRUE,
                bslib::card_header(
                  img(src = "www/bootstrap.png",
                  height = 80,
                  width = 100,
                  style = "margin:10px 10px")
              ),
             bslib::card_body(fillable = TRUE,
                 mod_scatter_display_ui("plot")
            )
          )
        )
      )
    )
  }
} 
1
Include inst/www resources
2
Original bslib layout
3
New bslib layout
4
Reference to alternate image (in inst/www/bootstrap.png)

The new bslib argument in our updated moves_ui() function toggles between the two UI options. We should also add an app argument to launch_app() to handle the two UI options:

show/hide updated launch_app()
#' Movies app standalone function
#'
#' Wrapper function for `shinyApp()`
#'
#' @param app which app to run. Options are: 
#'  * `"movies"` = the default app  
#'  * `"bslib"` = alternative `bslib` layout
#' @param options arguments to pass to `options()`
#' @param run where to launch app:
#'  * `p` = launch in viewer pane
#'  * `b` = launch in external browser
#'  * `w` = launch in window
#' @param ... arguments passed to UI
#'
#' @return shiny app
#'
#' @seealso [mod_var_input_ui()], [mod_var_input_server()], [mod_scatter_display_ui()], [mod_scatter_display_server()]
#'
#' @import shiny
#'
#' @export
#'
launch_app <- function(app = "movies", options = list(), run = "p", ...) {
  if (interactive()) {
    display_type(run = run)
  }
  if (app == "bslib") {
    shinyApp(
      ui = movies_ui(bslib = TRUE),
      server = movies_server,
      options = options
    )
  } else {
    shinyApp(
      ui = movies_ui(...),
      server = movies_server,
      options = options
    )
  }
}

Now we can load, document, and build the package and confirm app = "bslib" works:



Ctrl/Cmd + Shift + L / D / B

launch_app(app = 'bslib')

This alternate version of launch_app() uses the same modules and utility functions as the previous versions but when app = "bslib", the app displays the alternate UI layout:

inst/www/bootstrap.png image from movies_ui()

inst/www/bootstrap.png image from movies_ui()

The inst/ folder is a versatile tool for storing various files needed in our application (logos or images, CSS styling, JavaScript functions, HTML, etc.). The example above was simple, but using inst/ to hold resources for alternate UIs that can be displayed with a single argument is handy for demoing versions for stakeholders.

9.3 Data files

It is not uncommon to develop an application that can handle data from multiple sources. In these situations, we can sometimes store the alternative data files in the inst/ folder.7

9.3.1 A tidy-movies app

We’ll create an alternative application in sap that uses a tidy version of the ggplot2movies data, which we create using a function stored in the data-raw/tidy_movies.R file.8

We can place the application modules, UI, and server functions in inst/tidy-movies/R:

inst
├── extdata
   └── movies.fst
├── tidy-movies
   ├── R
   │   ├── devServer.R
   │   ├── devUI.R
   │   ├── dev_mod_scatter.R
   │   └── dev_mod_vars.R
   ├── app.R
   ├── imdb.png
   └── tidy_movies.fst
└── www
    ├── bootstrap.png
    └── shiny.png

5 directories, 10 files
1
Tidy movies app folder
2
App ui, server, and modules
3
Launch tidy-movies app
4
Alternate image/logo
5
Alternate data

All of the functions from sap are available in the tidy-movies/ app with explicit namespacing (i.e., sap::fun()):

  1. dev_mod_vars_ui() contains choices for the columns in the tidy_movies data, but there’s no need to rewrite the mod_var_input_server() function.9

  2. The dev_mode_scatter module functions have been rewritten to add functionality for importing the tidy_movies.fst data file and an option to removing missing values from the graph.

  3. inst/tidy-data/app.R contains a call to shinyApp() and any other packages we’ll need to launch the application. The data and alternative image file can be placed in the root folder (with the app.R file):

9.3.2 Launching tidy-movies

Finally, we’ll launch the tidy_movies data app with the app argument in our standalone function. This conditional argument is similar to the app = "bslib" option, but we use shinyAppDir() to launch the app stored in inst/tidy-movies (which we locate with system.file()).

Launch app with the shinypak package:

launch('09.2_inst-tidy-movies')
show/hide R/launch_app.R
launch_app <- function(app = "movies", options = list(), run = "p", ...) {
  if (interactive()) {
    display_type(run = run)
  }
  if (app == "bslib") {
    shinyApp(
      ui = movies_ui(bslib = TRUE),
      server = movies_server,
      options = options
    )
  } else if (app == "ggp2") {
      shinyAppDir(
        appDir = system.file("tidy-movies",
          package = "sap"
      ),
      options = options
      )
  } else {
    shinyApp(
      ui = movies_ui(...),
      server = movies_server,
      options = options
    )
  }
}


After loading, documenting, and installing, we’ll run the tidy ggplot2movies::movies data app:



Ctrl/Cmd + Shift + L / D / B

launch_app(app = 'ggp2')

inst/tidy-movies/ app with dev_movies_ui()

inst/tidy-movies/ app with dev_movies_ui()
Using colors and themes

Using different colors and themes for alternative applications can be a quick and easy way to differentiate the versions of your application.

9.4 Quarto apps

Shiny apps can also be built inside Quarto documents, in which case we could also use the inst/ folder to store and load resources (images, CSS, etc.).

Launch app with the shinypak package:

launch('09.3_inst-quarto')

The inst/quarto/ folder contains a Quarto version of our movies app:

inst/quarto
├── _quarto.yml
├── index.qmd
└── www
    ├── quarto.png
    └── styles.scss

1 directory, 4 files

The _quarto.yml file contains project metadata for the app10, and the www/ folder contains an image file and a CSS file.

9.4.1 Shiny documents

In index.qmd, we can specify server: shiny in the YAML header to let Quarto know we want the document to be interactive:

---
title: Movies
format:
  html:
    page-layout: full
    embed-resources: true
    theme:
      - united
      - www/styles.scss
    
server: shiny
---

The other options we’ve included in our YAML header are page-layout (for customizing the layout of each element), embed-resources (to create a standalone HTML document), and theme (we’re using a Bootstrap 5 theme with custom CSS).

9.4.2 Setting up

To set up our Quarto/Shiny app, we should include a code chunk with context: setup near the top of the document. We can use the image files in inst/quarto/www by adding a call to addResourcePath() in the setup chunk:

```{r}
#| context: setup
library(sap)
library(thematic)
library(ragg)
addResourcePath(
    prefix = 'quarto',
    directoryPath = system.file('www', package = 'sap'))
options(shiny.useragg = TRUE)
thematic::thematic_set_theme(
  theme = thematic::thematic_theme(
    bg = "#070d35", 
    fg = "#FFFFFF", 
    accent = "#2ee3a4"))
```
1
setup for HTML document
2
packages (including our app-package)
3
resources (including CSS)
4
options (for images)
5
theme from thematic

Note the context: setup chunk includes options and themes (I’m using the thematic package to set a theme for the ggplot2 graph in our app).

9.4.3 Page layout

Using page-layout: full in the YAML header lets us use a variety of panel options for each code chunk. For example, we’ll place the variable input module in a code chunk with the input option, the display module with the center option, and the data/Quarto attribution in the fill option.11

```{r}
#| panel: input
mod_var_input_ui("vars")
```

```{r}
#| panel: center
mod_scatter_display_ui("plot")
```

```{r}
#| panel: fill
tags$br()
tags$em(
  "Built using ",
  tags$a(
    img(
      src = "www/quarto.png",
      height = 25,
      width = 90,
      style = "margin:10px 10px"
    ),
    href = "https://quarto.org/docs/interactive/shiny/"
  ),
  "and data from the ",
  tags$a("Building web applications with Shiny", 
    href = "https://rstudio-education.github.io/shiny-course/"),
  "tutorial."
)
```
1
Horizontal input panel
2
Center the graph display
3
Data/Quarto attribute with hyperlinks across bottom of app

9.4.4 Quarto app server

The context: setup and panel option code chunks will execute when the document is rendered. However, any code chunk with context: server will execute when the document is served (not when it is rendered).

```{r}
#| context: server

selected_vars <- mod_var_input_server("vars")

mod_scatter_display_server("plot", var_inputs = selected_vars)

```

These chunks are run in separate R sessions, meaning we cannot access variables created in the first chunk within the second and vice versa (similar to to the movies_ui(), and movies_server.R()).

To render our Quarto Shiny app, we can use the Run Document/Preview button or running the following commands in the Terminal:

quarto serve /<path>/<to>/sap/inst/quarto/index.qmd

Movies Quarto Shiny App

Movies Quarto Shiny App

We can also launch the quarto app using the following R commands:

quarto::quarto_preview(
  file = system.file("quarto", "index.qmd", 
                     package = "sap"),
  render = "all",
  browse = utils::browseURL,
  watch = TRUE
)

9.4.5 Launching inst/quarto

To launch our Quarto application from inst/quarto, we’ll change the app argument again in launch_app():

show/hide launch_app()
launch_app <- function(app = "movies", options = list(), run = "p", ...) {
  if (interactive()) {
    display_type(run = run)
  }
  if (app == "bslib") {
    shinyApp(
      ui = movies_ui(bslib = TRUE),
      server = movies_server,
      options = options
    )
  } else if (app == "ggp2") {
      shinyAppDir(
        appDir = system.file("tidy-movies",
          package = "sap"
      ),
      options = options
      )
  } else if (app == "quarto") {
      quarto::quarto_preview(
        system.file("quarto", "index.qmd",
            package = "sap" ), 
        render = "all")
  } else {
    shinyApp(
      ui = movies_ui(...),
      server = movies_server,
      options = options
    )
  }
}

Now we can load, document, and build the package:



Ctrl/Cmd + Shift + L / D / B

Launching the application now just requires a single argument:

launch_app(app = "quarto")

9.5 Production

Finally, it’s also possible to have a folder dedicated for deploying a production version of our application from our app-package. I recommend naming this folder something like inst/prod/ or inst/deploy, and it can contain a version of your application that’s ‘fit for public consumption.’ In inst/prod/app I’ve created an app.R file:

Launch app with the shinypak package:

launch('09.4_inst-prod')
inst/
  └── prod/
      └── app
          └── app.R
          
2 directories, 1 file

9.5.1 prod/app/app.R

app.R includes a call to launch_app() with the app and options arguments:

show/hide prod/app/app.R
library(sap)
launch_app(app = "bslib", options = list(test.mode = FALSE))

I’ll use the "bslib" version from above to differentiate it from the other applications in sap.

9.5.2 Deploying inst/prod/

Back in the our app.R file, we’ll use shinyAppDir() and system.file() to return the app object from prod/app/app.R:

show/hide app.R
withr::with_options(new = list(shiny.autoload.r = FALSE), code = {
  if (!interactive()) {
    sink(stderr(), type = "output")
    tryCatch(
      expr = {
        library(sap)
      },
      error = function(e) {
        pkgload::load_all()
      }
    )
    shinyAppDir(appDir = system.file("prod/app", package = "sap"))
  } else {
    pkgload::load_all()
  }
  launch_app(options = list(test.mode = FALSE), run = 'p')
})
1
Set option to turn off loadSupport()
2
Create shiny object from prod/app



Ctrl/Cmd + Shift + L / D / B

To deploy the app, call rsconnect::deployApp() in the console and supply an appName:

rsconnect::deployApp(appName = 'movie-reviews-prod')

The deployment log will look something like this:

── Preparing for deployment ─────────────────────────────────────────────────
✔ Deploying "movie-reviews-prod" using "server: shinyapps.io / username: <username>"
ℹ Creating application on server...
✔ Created application with id 12711883
ℹ Bundling 39 files: .Rbuildignore, app.R, data/movies.rda, data/movies.RData,
data-raw/tidy_movies.R, DESCRIPTION, inst/tidy-data/app.R, inst/tidy-data/imdb.png,
inst/tidy-data/R/dev_mod_scatter.R, inst/tidy-data/R/dev_mod_vars.R, 
inst/tidy-data/R/devServer.R, inst/tidy-data/R/devUI.R, 
inst/tidy-data/tidy_movies.fst, inst/extdata/movies.fst, inst/prod/app/app.R, 
inst/www/bootstrap.png, inst/www/shiny.png, man/display_type.Rd, …, 
R/scatter_plot.R, and README.md
ℹ Capturing R dependencies with renv
✔ Found 69 dependencies
✔ Created 1,568,327b bundle
ℹ Uploading bundle...
✔ Uploaded bundle with id 9101312
── Deploying to server ────────────────────────────────────────────────────────
Waiting for task: 1457289827
  building: Processing bundle: 9101312
  building: Building image: 11074678
  building: Fetching packages
  building: Installing packages
  building: Installing files
  building: Pushing image: 11074678
  deploying: Starting instances
  success: Stopping old instances
── Deployment complete ───────────────────────────────────────────────────────
✔ Successfully deployed to <https://<username>.shinyapps.io/movie-reviews-prod/>

You can see a deployed version of this application here

Recap

This chapter had covered how to include external files and resources (i.e., what was previously stored in the www/ folder of a regular Shiny app project) in your app-package with addResourcePath() and system.file().

We’ve also covered how to use the inst/ folder to include alternative files, development and production/deployment versions of your app. You can now launch the following applications from sap:

Standard application with/without test mode

library(sap)
launch_app(options = list(test.mode = TRUE))
# or 
launch_app(options = list(test.mode = FALSE))

inst/bslib: an application with an alternative layout (with/without test mode)

library(sap)
launch_app(app = "bslib", options = list(test.mode = TRUE))
# or 
launch_app(app = "bslib", options = list(test.mode = FALSE)) 

inst/tidy-data: an application using a ‘development’ dataset (with/without test mode)

library(sap)
launch_app(app = "ggp2", options = list(test.mode = TRUE))
# or 
launch_app(app = "ggp2", options = list(test.mode = FALSE))

inst/prod: An app.R file for launching a ‘production’ version of our app.

library(sap)
rsconnect::deployApp(appName = 'movie-reviews-prod')

In the next section, we’re going to cover testing the code in a shiny app-package.

Recap: inst & www folders
  • inst/: the inst/ folder is installed with your app-package and will be accessible to users, so it’s a great location for files you want contained in your app, but don’t fit into the standard R package structure.

    • inst/ is also a great location for alternative versions of applications (i.e., inst/app/dev or inst/app/prod/).
  • system.file(): constructs a path to files or folders within installed packages and is especially useful when working with external datasets (i.e., inst/extdata/) or other external resources included with your app-package (i.e., inst/www/).

  • www: used for external static resources in shiny apps. shiny will automatically serve files under the www/ directory, but in app-packages we need to explicitly set this location with shiny::addResourcePath()

  • addResourcePath(): create a prefix (i.e., path) for a directoryPath of static files to accessible in shiny’s web server:

    # file location
    inst/
      └── www/
            └── shiny.png
    # add path to app 
    addResourcePath(prefix = 'www', 
                    directoryPath = system.file('www', 
                                    package = 'sap'))
    # use path without 'inst/' prefix
    shiny::img(src = 'www/shiny.png')

Please open an issue on GitHub


  1. The www/ folder is a special one for Shiny. Resources your app may link to, such as images—or in this case, scripts—are placed in the www/ folder. Shiny then knows to make these files available for access from the web browser.” - Shiny documentation↩︎

  2. This is a common problem developers encounter when converting shiny app into app-packages. See this popular thread on Posit Community.↩︎

  3. Read more about sub-directories to avoid in inst/ in R Packages, 2ed.↩︎

  4. The key takeaway here is that the inst/ subfolders and files are available unchanged in the installed version (with the inst/ folder omitted.).↩︎

  5. You can read more about adding external resources in the documentation for addResourcePath().↩︎

  6. As the development of your application progresses, you can (and should) keep different versions of your application in separate Git branches. But I’ve also found using the inst/ folder for those early stages of developing is helpful.↩︎

  7. We covered external data in Section 7.4.↩︎

  8. We covered the data-raw/ folder in the Data chapter, and you can read more about it here in R packages, 2ed↩︎

  9. This requires exporting mod_var_input_server() with @export in the R/ folder.↩︎

  10. Read more about the _quarto.yml configuration file in the Quarto documentation.↩︎

  11. Read more about page-layout in the Quarto documentation↩︎