23  golem

Published

2025-03-25

Warning

The contents for this section are being updated. Thank you for your patience.


  • The golem framework has excellent documentation in the Engineering Production-Grade Shiny Apps book and the package website.

  • New golem app-packages come ‘pre-packaged’ with UI, server, and standalone app functions

  • dev/ scripts help guide app setup, development, and deployment

  • Development is assisted by variety of add_ and use_ functions for creating modules, utility functions, js, css, etc

    • add_* functions include a with_test argument for creating tests along with new modules and utility functions
  • All golem package functions and files have consistent naming conventions (mod_, fct_, utils_, etc.)

  • golem ‘gives away’ UI and server utility functions with boilerplate tests (for free!)


This chapter walks through building a version of the sap with the golem framework. Install the golem package from CRAN (or the development version):

install.packages("golem")
# or the dev version
remotes::install_github("Thinkr-open/golem")
library(golem)

The version/description of golem used in this Shiny app-package is:

Package Version Title Description
golem 0.5.1 A Framework for Robust Shiny Applications An opinionated framework for building a production-ready 'Shiny' application. This package contains a series of tools for building a robust 'Shiny' application from start to finish.

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')

Review the chapters in each section:

library(shinypak)
list_apps(regex = 'golem')
## # A tibble: 1 × 2
##   branch   last_updated       
##   <chr>    <dttm>             
## 1 23_golem 2025-03-25 07:41:17

Launch an app:

launch(app = "23_golem")

This chapter is slightly different than the others. I’ll cover the golem method for creating app-packages, not necessarily the code itself. The application I’ll use is identical to the one we’ve built in previous chapters (using the movies data, with two input modules, one display module, and a utility function for building the scatter plot).

%%{init: {'theme': 'neutral', 'themeVariables': { 'fontFamily': 'monospace', "fontSize":"14px"}}}%%
flowchart TD
    User(["<strong>User</strong>"])
    mod_var_input_ui["<strong>mod_var_input_ui()</strong>"]
    mod_aes_input_ui["<strong>mod_aes_input_ui()</strong>"]
    mod_scatter_display_ui["<strong>mod_scatter_display_ui</strong>"] 
    
    subgraph mod_scatter_display_server["<strong>mod_scatter_display_server()</strong>"]
        var_inputs[\"var_inputs"\]
        aes_inputs[\"aes_inputs"\]
        inputs[/"inputs()"/]
        scatter_plot("scatter_plot()")
    end
    
    subgraph mod_var_input_server["<strong>mod_var_input_server()</strong>"]
        VarReactives[/"input$x<br>input$y<br>input$z"/]
    end
    subgraph mod_aes_input_server["<strong>mod_aes_input_server()</strong>"]
        AesReactives[/"input$alpha<br>input$size<br>input$plot_title"/]
    end

    selected_vars[/"selected variables"/]
    selected_aes[/"selected aesthetics"/]
    
    User --> |"<em>Selects X, Y, and Color...</em>"|mod_var_input_ui
    User --> |"<em>Selects Size, Alpha and optional Title...</em>"|mod_aes_input_ui
    mod_var_input_ui --> |"<em>Collects<br>variables...</em>"|mod_var_input_server
    mod_aes_input_ui --> |"<em>Collects aesthetics...</em>"|mod_aes_input_server
    VarReactives ==> selected_vars
    AesReactives ==> selected_aes
    selected_vars ==>|"<em>Input argument for...</em>"|var_inputs
    selected_aes ==>|"<em>Input argument for...</em>"|aes_inputs
    var_inputs & aes_inputs --> inputs --> scatter_plot
    scatter_plot -->|"<em>Renders plot...</em>"|mod_scatter_display_ui
    mod_scatter_display_ui -->|"<em>Displays output...</em>"|Display(["<strong>Graph</strong>"])
    
    style User font-size:18px
    style Display font-size:18px

    style mod_scatter_display_ui fill:#eee,font-size:13px,stroke-width:1px,rx:3,ry:3
    style mod_scatter_display_server fill:#eee,font-size:13px,stroke-width:1px,rx:3,ry:3
    style mod_var_input_server fill:#eee,font-size:14px,stroke-width:1px,rx:3,ry:3
    style mod_var_input_ui fill:#eee,font-size:14px,stroke-width:1px,rx:3,ry:3
    style mod_aes_input_server fill:#eee,font-size:14px,stroke-width:1px,rx:3,ry:3
    style mod_aes_input_ui fill:#eee,font-size:14px,stroke-width:1px,rx:3,ry:3
    
    style AesReactives fill:#fff,stroke:#bbb,stroke-width:2px
    style VarReactives fill:#fff,stroke:#bbb,stroke-width:2px
    style selected_vars fill:#fff,stroke:#000,stroke-width:2px
    style selected_aes fill:#fff,stroke:#000,stroke-width:2px
    style var_inputs fill:#fff,stroke:#bbb,stroke-width:2px
    style aes_inputs fill:#fff,stroke:#bbb,stroke-width:2px
    style inputs fill:#fff,stroke:#bbb,stroke-width:2px

    style scatter_plot fill:#444,color:#FFF,stroke:#000,stroke-width:2px,rx:10,ry:10
    
    style VarReactives font-size: 14px
    style AesReactives font-size: 14px
    

Variable and aesthetic input modules

23.1 gap (a golem app-package)

All golem apps export the standalone app function, run_app().

library(gap)
gap::run_app()

Launch app with the shinypak package:

launch('23_golem')
Figure 23.1: After loading, documenting, and installing gap, launch the movies with run_app()

In the sections below, I’ll note various features or tools that differ from the standard devtools/usethis app-package development we’ve covered so far. For a more comprehensive overview of golem Shiny app development, check out the Engineering Production-Grade Shiny Apps book and my Shiny frameworks website.

23.2 The dev/ scripts

After creating a new golem app with golem::create_golem() or using the New Project Wizard in RStudio, you’ll see a dev/ folder with three scripts for setting up, developing, and launching/deploying your application.

dev
├── 01_start.R
├── 02_dev.R
└── 03_deploy.R

The package website has each script available in vignettes: 01_start, 02_dev, 03_deploy.

23.3 Set up

01_start.R should be filled at start.”

The dev/01_start.R file opens when the new golem app-package launches and provides steps for setting up the following files, folders, and options:

  1. Use golem’s fill_desc() function for entering the necessary DESCRIPTION fields.1

  2. golem has functions that automatically set multiple options in the golem-config.yml2 and package dependencies3

  3. usethis functions are called for:4

    • creating a LICENSE5
    • creating6 and building7 a README
    • creating a CODE_OF_CONDUCT.md8
    • adding a Lifecycle badge9, and
    • and the NEWS.md file.10
  4. If you aren’t using Git, there are functions for initiating and configuring your repo.11

  5. The app testing suite is set up using use_recommended_tests().12

  6. golem apps also have custom functions for creating a series of UI13 and server14 utility functions (which we’ll cover below).

23.4 Development

02_dev.R should be used to keep track of your development during the project.

After setting up your golem app-package, the dev/02_dev.R file contains functions and tools for developing the application.

23.4.1 The R/ folder

The dev/02_dev.R script contains the following functions for developing functions, modules, and other application code.

Primary app files

All golem apps come with primary app UI and server functions (R/app_ui.R and R/app_server.R) and a standalone app function (R/run_app.R.).

Modules

add_module() adds a .R file with a mod_ prefix (an optional test can be included with the with_test = TRUE argument)

Utility functions

  • Utility functions can be added with add_utils() or add_fct() (also includes the with_test option for tests).

  • The golem_utils_* files contain commonly used UI and server functions:

Other R files

  • R/_disable_autoload.R disables shiny::loadSupport() (an option we covered in the Launch chapter)

  • R/app_config.R contains configuration functions:

    • app_sys() is a wrapper for system.file()

    • get_golem_config() reads environment variables (i.e., GOLEM_CONFIG_ACTIVE) and the contents of inst/golem-config.yml:

Below is the R/ folder after creating the modules and utility functions.

show/hide R folder
R
├── _disable_autoload.R
├── app_config.R
├── app_server.R
├── app_ui.R
├── data.R
├── gap_theme.R
├── golem_utils_server.R
├── golem_utils_ui.R
├── mod_aes_inputs.R
├── mod_scatter_display.R
├── mod_var_inputs.R
├── run_app.R
├── utils_scatter_display.R
└── utils_tests.R

1 directory, 14 files
1
Turn off loadSupport()
2
Configure app functions
3
App UI and server functions
4
Data documentation
5
thematic theme
6
golem utility functions
7
Aesthetics input module
8
Scatter plot module
9
Variable input module
10
Standalone app function
11
Scatter plot utility function (scatter_plot())
12
Test utility function (test_logger())


Including the mod_/utils_ prefixes in the names of the .R scripts makes it easier to separate them from other code in your package namespace when using tab-completion or searching for a particular file using Ctrl + .:


Go to File/Function in RStudio

Go to File/Function in RStudio

23.4.2 The inst/ folder

The inst/ folder has a special role in golem apps. Custom functions handle external resources to ensure these files are loaded when the application launches.

The inst/ folder

  • The app/ folder contains the application files.15

    • www/ contains an initial favicon.ico icon.
  • golem-config.yml is used to set various configuration options. 16

External files

Non-R files

  • dev/02_dev.R includes helpers for adding JavaScript, CSS, and other files to the inst/app/www/ folder:

    • golem::add_js_file()

    • golem::add_js_handler()

    • golem::add_css_file()

    • golem::add_sass_file()

    • golem::add_any_file()

inst
├── WORDLIST
├── app
   └── www
       ├── favicon.ico
       └── golem.png
├── extdata
   └── movies.RData
└── golem-config.yml

4 directories, 5 files

23.4.3 Data

Data in golem app-packages function like the data files and folders in a standard R package (we covered these in Chapter 7).

External data

inst/extdata/ contains the external data files.18

inst
└── extdata
    └── movies.RData

2 directories, 1 file

Raw data

data-raw/ contains movies.R, which is used to load extdata/movies.RData and create the data/movies.rda file.

data-raw
├── movies.R
└── movies.RData

1 directory, 2 files

Package data

data/ contains the movies.rda file used in the application.

data
└── movies.rda

1 directory, 1 file

23.4.4 Documentation

The roxygen2 documentation in golem app-package files comes with boilerplate tags and descriptions similar to those covered in Chapter 5.

The man/ folder

By default, modules created with add_module() or golem’s other file creation functions will have the @noRd tag, so the man/ folder will only contain functions we’ve manually exported:19

man
├── movies.Rd
├── run_app.Rd
├── scatter_plot.Rd
└── test_logger.Rd

1 directory, 12 files
1
Data documentation
2
The ‘pre-packaged’ standalone app function (only export from golem apps)
3
Plot utility function (exported from gap)
4
Test utility function (exported from gap)

Vignettes

Package vignettes in golem app-packages operate like vignettes in standard R packages.

vignettes/
├── shinytest2.Rmd
└── tests_and_coverage.Rmd

1 directory, 2 files

23.4.5 Dependencies

Development dependencies

  • The install_dev_deps() function from the dev/01_start.R script installs the packages needed to develop a golem app.

  • Include packages to the Imports field in the DESCRIPTION with attachment::att_amend_desc(), which parses the code under R/ to make sure the DESCRIPTION file is up-to-date.

Package dependencies

  • Include add-on packages with usethis::use_package() and use pkg::fun() in the roxygen2 documentation (see Section 6.3).
    • For example, we’ve included a thematic theme to match the golem package colors.

    • After creating our theme in R/gap_theme.R, the golem_add_external_resources() function in R/app_ui.R can include this dependency with bslib::bs_theme_dependencies().

  • Modules and functions created with golem’s file creation helpers include @importFrom in their roxygen2 documentation.

23.4.6 Tests

The test suite for golem app-packages is set up in the dev/01_start.R script.20 golem’s helper functions can potentially provide a lot of boilerplate tests, because any code files created with add_module(), add_utils(), and add_fct() will also include a test file if with_test is set to TRUE.

The tests/ folder

  • A tests/README.md file is created by the covrpage package.

  • tests/spelling.R adds functionality from the spelling package.21

Unit tests

  • test-golem-recommended.R contains tests for the functions included in your new golem app.22

  • The two golem utility function files (golem_utils_server.R, and golem_utils_ui.R) have accompanying tests files.

    • test-golem_utils_server.R contains utility functions that might be useful in the server.

    • test-golem_utils_ui.R contains utility functions that might be useful in the UI.

  • test-utils_scatter_display.R tests our scatter_plot() utility function.

Module tests

  • test-mod_var_inputs.R and test-mod_aes_inputs.R test our input modules (see Section 17.2).

  • The communication between our modules is tested in test-mod_scatter_display.R (see Section 17.3).

System tests

  • I’ve moved the two system tests to the shinytest2 vignette:

    • test-shinytest2.R is the initial resulting test from shinytest2::record_test() covered in Section 18.3.

    • test-app-feature-01.R tests the data visualization dropdown feature (see Section 18.4).

tests/
├── README.md
├── spelling.R
├── testthat
   ├── helper.R
   ├── test-golem-recommended.R
   ├── test-golem_utils_server.R
   ├── test-golem_utils_ui.R
   ├── test-mod_aes_inputs.R
   ├── test-mod_scatter_display.R
   ├── test-mod_var_inputs.R
   └── test-utils_scatter_display.R
└── testthat.R

3 directories, 11 files
1
Created from covrpage package
2
Created from spelling package
3
Test helpers
4
Created with: golem::use_recommended_tests()
5
Created with: golem::use_utils_ui(with_test = TRUE)
6
Created with: golem::use_utils_server(with_test = TRUE)
7
Created with: golem::add_module(name = 'aes_inputs', with_test = TRUE)
8
Created with: golem::add_module(name = 'scatter_display', with_test = TRUE)
9
Created with: golem::add_module(name = 'var_inputs', with_test = TRUE)
10
Utility function test (scatter_plot())

Running tests in your golem app may produce a familiar warning:

Warning message:
In shiny::loadSupport(app_dir, renv = renv, globalrenv = globalrenv) :
  Loading R/ subdirectory for Shiny application, but this directory appears
  to contain an R package. Sourcing files in R/ may cause unexpected behavior.

We covered this message in the Launch chapter, and it’s being addressed in a future release of shinytest2

23.4.7 Code coverage

Code test coverage is handled by usethis::use_coverage() and covrpage::covrpage().23 I’ve found the code coverage functions don’t play well with long descriptions in testthat’s BDD functions or shinytest2 tests (which is why I’ve placed these functions/tests in the vignettes).

Test coverage

devtools::check(quiet = TRUE)
#> ℹ Loading gap
#> ── R CMD check results ──────────────────────────── gap 0.0.0.9000 ────
#> Duration: 42.5s
#> 
#> ❯ checking for future file timestamps ... NOTE
#>   unable to verify current time
#> 
#> ❯ checking top-level files ... NOTE
#>   Non-standard file/directory found at top level:
#>     ‘app.R’
#> 
#> ❯ checking package subdirectories ... NOTE
#>   Problems with news in ‘NEWS.md’:
#>   No news entries found.
#> 
#> ❯ checking R code for possible problems ... NOTE
#>   mod_scatter_display_server : <anonymous>: no visible binding for global
#>     variable ‘movies’
#>   Undefined global functions or variables:
#>     movies
#> 
#> 0 errors ✔ | 0 warnings ✔ | 4 notes ✖
covr::package_coverage()
#> gap Coverage: 85.27%
#> R/run_app.R: 0.00%
#> R/utils_tests.R: 53.85%
#> R/mod_scatter_display.R: 56.76%
#> R/golem_utils_server.R: 77.78%
#> R/golem_utils_ui.R: 87.94%
#> R/app_config.R: 100.00%
#> R/app_server.R: 100.00%
#> R/app_ui.R: 100.00%
#> R/mod_aes_inputs.R: 100.00%
#> R/mod_var_inputs.R: 100.00%
#> R/utils_scatter_display.R: 100.00%

23.4.8 Continuous Integration (CI)

All the continuous integration (CI) functions in dev/02_dev.R are from the usethis package (see Chapter 21). Many of these functions have been deprecated and/or should be used cautiously.

GitHub Actions

Other CI Options

23.5 Deployment

03_deploy.R should be used once you need to deploy your app.

When you’re ready to deploy your golem app, the dev/03_deploy.R file contains a ‘pre deployment checklist’ with multiple options for deploying your application.

Running checks

golem has functions for preparing to deploy your app to Posit products.

app.R

  • All of these functions create an app.R file to the root of your app-package.

    • pkgload::load_all() is called in the new app.R file (so this package is added to the Imports field in the DESCRIPTION).
# Launch the ShinyApp (Do not remove this comment)
# To deploy, run: rsconnect::deployApp()
# Or use the blue button on top of this file

pkgload::load_all(export_all = FALSE,
  helpers = FALSE,
  attach_testthat = FALSE)
options( "golem.app.prod" = TRUE)
gap::run_app() # add parameters here (if any)

R/_disable_autoload.R

These functions also add an empty R/_disable_autoload.R file to handle the loadSupport() warning.

Hidden files

Various hidden files are added (.rscignore) and included in the .Rbuildignore ("^rsconnect$" to ignore rsconnect folder, "^app\\.R$" to ignore the app.R file, etc.).

Deploy code

dev/03_deploy.R includes boilerplate code for deploying your application using rsconnect::deployApp().

rsconnect::deployApp(
  appName = desc::desc_get_field("Package"),
  appTitle = desc::desc_get_field("Package"),
  appFiles = c(
    # Add any additional files unique to your app here.
    "R/",
    "inst/",
    "data/",
    "NAMESPACE",
    "DESCRIPTION",
    "app.R"
  ),
  appId = rsconnect::deployments(".")$appID,
  lint = FALSE,
  forceUpdate = TRUE
)

golem also has multiple options for creating Docker files. I’ve included the golem::add_dockerfile_with_renv() below because we’re using renv.

Docker

  • add_dockerfile_with_renv() creates a tmp/deploy folder and adds Dockerfile, Dockerfile_base, README, gap_0.0.0.9000.tar.gz, and renv.lock.prod (see below)

    • gap_0.0.0.9000.tar.gz is a compressed version of our app-package to deploy in the Docker container.

    • renv.lock.prod is a JSON file with a list of packages used in our app-package.

The tmp/deploy folder from add_dockerfile_with_renv() is below:

deploy/
  ├── Dockerfile
  ├── Dockerfile_base
  ├── README
  ├── gap_0.0.0.9000.tar.gz
  └── renv.lock.prod
show/hide golem::add_dockerfile_with_renv() README
docker build -f Dockerfile_base --progress=plain -t gap_base .
docker build -f Dockerfile --progress=plain -t gap:latest .
docker run -p 80:80 gap:latest
# then go to 127.0.0.1:80
show/hide golem::add_dockerfile_with_renv() Dockerfile
FROM gap_base
COPY renv.lock.prod renv.lock
RUN R -e 'options(renv.config.pak.enabled = FALSE);renv::restore()'
COPY gap_*.tar.gz /app.tar.gz
RUN R -e 'remotes::install_local("/app.tar.gz",upgrade="never")'
RUN rm /app.tar.gz
EXPOSE 80
USER rstudio
CMD R -e "options('shiny.port'=80,shiny.host='0.0.0.0');library(gap);gap::run_app()"
show/hide golem::add_dockerfile_with_renv() Dockerfile_base
FROM rocker/verse:4.4.2
RUN apt-get update -y && apt-get install -y  make zlib1g-dev git libicu-dev && rm -rf /var/lib/apt/lists/*
RUN mkdir -p /usr/local/lib/R/etc/ /usr/lib/R/etc/
RUN echo "options(renv.config.pak.enabled = FALSE, repos = c(CRAN = 'https://cran.rstudio.com/'), download.file.method = 'libcurl', Ncpus = 4)" | tee /usr/local/lib/R/etc/Rprofile.site | tee /usr/lib/R/etc/Rprofile.site
RUN R -e 'install.packages("remotes")'
RUN R -e 'remotes::install_version("renv", version = "1.0.3")'
COPY renv.lock.prod renv.lock
RUN R -e 'renv::restore()'

You can read more details about deploying with Docker on the Shiny Frameworks supplemental website.

23.6 Summary of golem features

golems helper functions and dev scripts make application development fast–I was able to create gap quickly, and all of the supporting packages (covrpage, attachment, spelling) make the development process faster/easier. Below is a brief summary of the steps it took to create gap:

  1. Modules and utility function were easily created with add_module() and add_utils()
  2. The bslib layout functions and the thematic theme were added to app_ui() and golem_add_external_resources()
  3. The module server logic was added to app_server()
  4. The golem hex image was moved added inst/app/www/
  5. I downloaded the movies.RData file into inst/extdata/, then created data-raw/movies.R and created data/movies.rda.
  6. Called usethis::use_vignette() to create shinytest2.Rmd and tests_and_coverage.Rmd.
  7. Added the test_logger() function to R/utils_tests.R.
  8. Revised the unit tests and moved the system tests to vignettes/shinytest2.Rmd.
  9. Included code coverage in README.Rmd with usethis::use_coverage('codecov') and created the tests/README.md file with covrpage::covrpage().
  10. Called attachment::att_amend_desc() to capture any dependencies
  11. Finally, I loaded, documented, and installed the gap package and ran the application with gap::run_app()

If you’ve followed along with the preceding chapters, the golem framework will be familiar. In essence, golem takes many of the package development steps we’ve covered and bundles them into wrapper functions (i.e., add_module() is similar to running usethis::use_r() and usethis::use_test(), then adding an roxygen2 skeleton).

23.7 gap dependencies

It’s also worth noting that using the golem framework adds golem as a dependency:

# in the 23_golem branch of sap
pak::local_deps_explain(deps = 'golem', root = ".")
gap -> golem 

For comparison, if we look at the app-package from the previous branch (22_pkgdown) the dependency tree shows us that using devtools/usethis doesn’t make our app-package depend on these packages:

See the 22_pkgdown branch of sap.

# in the 09d_inst-prod branch of sap
pak::local_deps_explain(deps = 'devtools', root = ".")
x devtools
# in the 09d_inst-prod branch of sap
pak::local_deps_explain(deps = 'usethis', root = ".")
x usethis

Recap

RECAP  


The golem framework is a huge time saver if you’re familiar with R package development (and you’d prefer if many of these steps were bundled and optimized for ‘production grade’ Shiny apps). However, if you’re not familiar with package development, you might end up with app-packages that have bugs you can’t diagnose or fix.

Please open an issue on GitHub


  1. We covered in the Packages chapter.↩︎

  2. Options are set with with the options argument of golem::fill_desc() (which calls golem::set_golem_options()).↩︎

  3. Dependencies are installed with golem::install_dev_deps()↩︎

  4. Many of these functions and files are covered in the Whole App Game chapter.↩︎

  5. Created using usethis::use_mit_license().↩︎

  6. Created using usethis::use_readme_rmd().↩︎

  7. Built using devtools::build_readme().↩︎

  8. Created using usethis::use_code_of_conduct().↩︎

  9. Created using usethis::use_lifecycle_badge().↩︎

  10. Created using usethis::use_news_md().↩︎

  11. Initialize Git using usethis::use_git() and set the remote with usethis::use_git_remote().↩︎

  12. The tests/ folder and testthat files included with golem::use_recommended_tests() are covered in the Test suite chapter.↩︎

  13. Create UI utility functions using golem::use_utils_ui()↩︎

  14. Create server utility functions using golem::use_utils_server()↩︎

  15. the app/ folder is used to add external resources to the application (similar to the previous versions of sap).↩︎

  16. golem apps use a golem-config.yml file for setting various options. These are initially set with set_golem_options() (and based on the config package).↩︎

  17. The use_recommended_tests() is run in the dev/01_start.R file and if spellcheck is TRUE, creates the tests/spelling.R file and the inst/WORDLIST file.↩︎

  18. This contains the movies.RData file for the original Shiny application.↩︎

  19. The noRd tag is added to module files created with add_module(), but you can export these functions by setting the export argument to TRUE.↩︎

  20. The testthat test suite is set up with golem::use_recommended_tests() function.↩︎

  21. The spelling package will spell check vignettes, packages, etc.↩︎

  22. test-golem-recommended.R contains the recommended tests for app_ui(), app_server(), app_sys(), and golem-config.yml.↩︎

  23. The covrpage package is not on CRAN, but the development version always seems to work.↩︎

  24. rhub::check_for_cran() is “deprecated and defunct”, use rhub::rhubv2() instead.↩︎