24  leprechaun

Published

2025-04-07

ISSUE

The use_/_build function workflow didn’t create the SASS or JavaScript changes in the latest build of lap. I’ve opened an issue on the leprechaun GitHub repo and will update this section with any changes.


  • leprechaun has many of the same features found in the golem framework, but without the added dependencies (think golem à la carte)

  • Helper functions for creating modules, app.R files, JavaScript, CSS, SCSS, HTML, etc.

  • leprechaun apps also come ‘pre-packaged’ with UI, server, and standalone app functions

  • Additional features and functionality are added with a variety of use_* functions ‘bundle’ resources in the inst/ folder:

    • use_sass(), use_config(), use_packer() (with use_js_utils()), etc.

This chapter walks through building a version of the sap with the leprechaun framework. The resulting app-package (lap) is in the 24_leprechaun branch. For more information on buidling an application with the leprechaun framework, see my supplemental Shiny frameworks website.

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 = 'leprechaun')
## # A tibble: 1 × 2
##   branch        last_updated       
##   <chr>         <dttm>             
## 1 24_leprechaun 2025-04-06 22:46:54

Launch the app:

launch(app = "24_leprechaun")

Download the app:

get_app(app = "24_leprechaun")

Install leprechaun and packer to get started:

install.packages('pak')
pak::pak("devOpifex/leprechaun")
library(leprechaun)
pak::pak("JohnCoene/packer")
library(packer)

I’ve included the version/description of both the leprechaun and packer packages we’ll use for the app-package:

Package Version Title Description
leprechaun 1.0.0.9001 Create Simple 'Shiny' Applications as Packages Code generator for robust dependency-free 'Shiny' applications in the form of packages. It includes numerous convenience functions to create modules, include utility functions to create common 'Bootstrap' elements, setup a project from the ground-up, and much more.
packer 0.1.3.9000 An Opinionated Framework for Using 'JavaScript' Enforces good practice and provides convenience functions to make work with 'JavaScript' not just easier but also scalable. It is a robust wrapper to 'NPM', 'yarn', and 'webpack' that enables to compartmentalize 'JavaScript' code, leverage 'NPM' and 'yarn' packages, include 'TypeScript', 'React', or 'Vue' in web applications, and much more.

24.1 lap (a leprechaun app-package)

After checking out the 24_leprechaun branch, be sure to load, document, and install the application.

Ctrl/Cmd + Shift + L
Ctrl/Cmd + Shift + D
Ctrl/Cmd + Shift + B

lap exports the movies data and the standalone app function, run().

library(lap)
lap::run()

Launch app with the shinypak package:

launch('24_leprechaun')

lap movies app

lap movies app

24.2 Set up

Creating a new leprechaun app-package can be done with usethis::create_package()1 (which we covered in the packages chapter). After your app-package is created, leprechaun::scaffold() builds the core app files:2

lap/
├── DESCRIPTION
├── NAMESPACE
├── R/
   ├── _disable_autoload.R
   ├── assets.R
   ├── input-handlers.R
   ├── leprechaun-utils.R
   ├── run.R
   ├── server.R
   ├── ui.R
   └── zzz.R
├── inst/
   ├── assets
   ├── dev
   ├── img
   └── run
       └── app.R
└── lap.Rproj

7 directories, 12 files

24.3 Development

The sections below outline the steps for creating the sap movies review app from scratch using leprechaun.

24.3.1 The R/ folder

All leprechaun apps come with primary app UI and server functions (R/ui.R and R/server.R) and a standalone app function (R/run.R.).

UI

  • ui() defines the overall UI structure using navbarPage().

    • assets() combines the output of serveAssets() (which gathers JS and CSS from inst/assets)

Server

  • server() contains the server logic for the application.

    • send_message <- make_send_message(session) sets up a utility that lets you easily send messages from R to JavaScript

Modules

show/hide R/module_vars.R file
#' vars UI
#' 
#' @param id Unique id for module instance.
#' 
#' @keywords internal
varsUI <- function(id){
    ns <- NS(id)

    tagList(
        h2("vars")
    )
}

#' vars Server
#' 
#' @param id Unique id for module instance.
#' 
#' @keywords internal
vars_server <- function(id){
    moduleServer(
        id,
        function(
            input, 
            output, 
            session
            ){
                
            ns <- session$ns
            send_message <- make_send_message(session)

            # your code here
        }
    )
}

# UI
# varsUI('id')

# server
# vars_server('id')

Run

  • R/run.R provides functions to launch your Shiny application in different modes:

    • run() is the main function that users will call to launch your application in normal mode.

    • run_dev() is for development purposes and launches a development version of the app.

Assets

  • The R/assets.R file contains the following functions:

    • serveAssets(): scans your package for JavaScript and CSS files, separates regular JavaScript files from “module” type JavaScript files, and bundles everything as proper HTML dependencies that Shiny can use.

    • remove_modules(): filters out the module files from the list of all JavaScript files.

    • get_modules(): extracts only the module files from the list of all JavaScript files.

    • collapse_files(): creates a regular expression pattern to match specific files.

We’ll use the functions in R/assets.R via the assets() function in the R/ui.R file, which calls serveAssets() to include all any front-end resources in our Shiny app.

Input handlers

R/input-handlers.R sets up custom data handling between your JavaScript front-end and R back-end.

leprechaun utilities

The R/leprechaun-utils.R file provides a utility function (make_send_message()) — a helper to send custom messages from the server to the front-end (JavaScript) in a namespace-aware and modular way.

R/zzz.R

.onLoad() creates a static file server path for images and makes inst/img accessible to the web browser.3

24.3.2 The inst/ folder

leprechaun::scaffold() creates the following folders in inst/:

inst/assets/

Contains any JavaScript and CSS files that are part of the application frontend.

inst/dev/

Optional directory used for custom dev scripts, build tools, or staging.

inst/img/

Holds static image files like logos, icons, or other assets we want to display in the UI.

inst/run

Contains the app.R file used for development mode (i.e., with lap::run_dev()).

24.3.2.1 Adding images

Adding images is simplified with the .onLoad() function, which we can demonstrate by adding an image file to the UI function.

Place leprechaun-logo.png in inst/img/:

inst/
  └── img/
       └── leprechaun-logo.png

Add file path to the UI:

bslib::card_header(
  tags$h4(tags$em("Brought to you by ",
    tags$img(
      src = "img/leprechaun-logo.png",
      height = 100,
      width = 100,
      style = "margin:10px 10px"
      )
    )
  )
)

Load, document, and install the package, then launch the application with run():

Ctrl/Cmd + Shift + L
Ctrl/Cmd + Shift + D
Ctrl/Cmd + Shift + B

lap::run()

24.3.2.2 use and build

The leprechaun workflow involves a combination of use_ functions that are combined with leprechaun::build() to add various functionality to your application. I’ll demonstrate adding JavaScript and SCSS below.

24.3.2.3 Use JavaScript

Using packer

This creates inst/dev/packer.R and inst/assets/index.js.

inst
├── assets
   └── index.js
├── dev
   └── packer.R
├── extdata
   └── movies.RData
├── img
└── run
    └── app.R

Assign the output from make_send_message() to send_message() in R/server.R, then pass the msgId and text of the message:

send_message <- make_send_message(session)
send_message("show-packer",
              text = "a message from R/server.R")
  1. Create send_message()
  2. Use send_message() to send a message the UI.

After loading, documenting, and installing your app-package:

Ctrl/Cmd + Shift + L
Ctrl/Cmd + Shift + D
Ctrl/Cmd + Shift + B

Run the application:

lap::run()

send_message() in R/server.R

send_message() in R/server.R
JS Alerts in Positron

If you’re using Positron , you’ll have to display the application in the browser to view the alert.

Adding images to inst/img/

Adding images to inst/img/

24.3.2.4 Use Sass

To add Sass styling, we can combine leprechaun’s use_sass() and build() functions.

leprechaun::use_sass() adds scss/ folder with _core.scss and main.scss.

scss
├── _core.scss
└── main.scss

1 directory, 2 files

The original _core.scss file:

html{
    .error {
        color: red
    }
}

Change the color to green using $accent: #38B44A;

$accent: #38B44A;

html{
    h4 {
        color: $accent;
    }
}
leprechaun::build()
✔ Running packer.R
✔ Bundled       
✔ Running sass.R

Once again, load, document(), and install lap, then launch the application with run():

Ctrl/Cmd + Shift + L
Ctrl/Cmd + Shift + D
Ctrl/Cmd + Shift + B

lap::run()

Running lap with new Sass

Running lap with new Sass

It’s important to note that adding features to your leprechaun app-package will add non-standard directories to the root folder:

├── node_modules/ <- too many sub-folders to list here!
├── package-lock.json
├── package.json
├── scss/
   ├── _core.scss
   └── main.scss
├── srcjs/
   ├── config
   ├── index.js
   └── modules
├── webpack.common.js
├── webpack.dev.js
└── webpack.prod.js

These folders are necessary for adding JavaScript/SCSS functionality, so don’t remove them (just make sure they’re added to the .Rbuildignore/.gitignore)

24.3.3 Data

We can add data to lap the same way we’d add data to any R package.

inst/extdata/

Add the movies.RData file to inst/extdata/

data-raw/

Create the movies.R file in data-raw/ with usethis::use_data_raw("movies")

data/

Load the movies.RData in data-raw/movies.R and export the file to data/movies.rda using usethis::use_data().

R/

Document the dataset in R/data.R.

24.3.4 Documentation

leprechaun modules and functions include @keywords internal, which can be used in combination with a {pkgname}-package.R file. We can create this file using usethis::use_package_doc():

Add the following to lap-package.R.

#' @keywords internal 
"_PACKAGE"

The documentation for lap is intentionally sparce:

lap index

lap index

By default, run() is the only leprechaun funtion available in the package index (I’ve exported the theme, data, and utility functions).

24.3.5 Dependencies

leprechaun lists bslib, htmltools, and shiny when the app-package is initially buit. If using packer, this application is also added to the DESCRIPTION file with leprechaun::use_packer().

To keep track of the add-on packages, I recommend using the attachment package.

24.3.6 Tests

Unit tests in leprechaun apps should be written using the same process/tools as a standard R package/Shiny app.

Unit tests

Use testthat to set up the test suite and use_test() to create and open test files.

Integration tests

Use testServer to create tests for module server functions.

System tests

Use shinytest2 for system tests.

24.3.7 Deployment

To launch our app locally (or using rsconnect::deployApp()), leprechaun has a handy leprechaun::add_app_file() function that creates an app.R file in the root folder with the following contents:

# Launch the ShinyApp 
# do not remove to keep push deploy button
# from RStudio
pkgload::load_all(
    reset = TRUE,
    helpers = FALSE
)

run()

This file shouldn’t be confused with inst/run/app.R:

# do not deploy from this file
# see leprechaun::add_app_file()
leprechaun::build()

pkgload::load_all(
    path = "../../",
    reset = TRUE,
    helpers = FALSE
)

run()

The inst/run/app.R file is specifically used by the run_dev() function.

Recap

leprechaun app-packages have an unopinionated structure and extensive JavaScript frontend support.

  • Building modules is made easier with the add_module() function (although the naming convention could be more consistent).

  • The application is split into separate UI, server, and standalone app functions/files.

  • Testing support is not included (but testthat and shinytest2 can be easily implemented).

The leprechaun framework is ideal for any developer who is comfortable with JavaScript frameworks (e.g., Webpack, Babel, etc.) and want to build custom UIs with Shiny as a backend.


  1. Remember to provide a list of fields to usethis::create_package() for the DESCRIPTION file (or edit this manually).↩︎

  2. leprechaun::scaffold() has arguments for ui (fluidPage or navbarPage) bs_version (bootstrap version) and overwrite (if you need to start over).↩︎

  3. R/zzz.R files are traditionally used for setup functions that run during package loading. The name ensures it’s loaded last alphabetically (though modern R doesn’t strictly require this naming convention anymore).↩︎

  4. srcjs/, package-lock.json, package.json, webpack.common.js, webpack.dev.js and webpack.prod.js are added to the .Rbuildignore (and node_modules/ is added to the .gitignore).↩︎

  5. The scss folder in the root directory is used by leprechaun::build() to create inst/tidy-data/sass.R.↩︎