# install.packages('pak')
::pak('mjfrigaard/shinypak') pak
26 Shiny Assistant
The Shiny Assistant is an AI-powered chat bot anyone can use to help them build a Shiny application.
“you can ask it questions about Shiny, or to create a Shiny application from scratch, or ask it to make changes to an existing application.” - Shiny Assistant, Winston Chang
26.1 Chat + IDE
Launch app with the shinypak
package:
launch('23_llm-shiny-assist')
The UI for Shiny Assistant looks like a standard chat, but if you ask it to “Open the editor”, we see an interface with a script editor, console, and viewer pane:
Shiny Assistant is great for trying different UI components, layouts, and styling (color palettes, themes, fonts, etc.). We will walk through using the Assistant for building an Shiny app-package, but we’ll limit the number of prompts to keep this chapter brief. If you’d like to read a more comprehensive example of using Shiny Assistant to build an application, I recommend reading this post on Appsilon’s blog.
I think you’ll be convinced that the Shiny Assistant is an impressively helpful tool to have in your Shiny toolbox, even with a limited number of prompts. Let’s get started!
26.1.1 Prompt 1: Build an app
Let’s start with trying to reproduce some of the applications we have in sap
. I typically have data before I have an idea for a dashboard or app, so we’ll pass the structure of simplified version our movies
data to the Assistant with some instructions on what we’d like to see:2
Prompt: Create a shiny application that visualizes IMDB movie data using a scatter plot. Include inputs for x, y, color, size, and transparency.
The column names are below:
``` Rows: 651 Columns: 18 $ title… $ genre … $ runtime … $ year … $ top200_box … $ director … $ mpaa_rating … $ imdb_rating … $ critics_rating … $ audience_rating … $ imdb_num_votes … $ critics_score … $ audience_score … $ best_pic_nom … $ best_pic_win … $ best_actor_win … $ best_actress_win … $ best_dir_win … ```
Use the data stored in this URL:
https://raw.githubusercontent.com/mjfrigaard/sap/refs/heads/23_llm-shiny-assist/data-raw/movies_sample.csv
Below we can see the initial app:
The initial application has nearly identical input, outputs, and layout. However, we can see an additional checkbox for Show Movie Titles and the Size by input has been mapped to a variable (not a numerical value).
In our next prompt, we’ll refine the app to more closely match what we have in our sap
app-package.
26.1.2 Prompt 2: Refine
Prompt: Change the size to a numeric slider and include a text input for a plot title.
The application is updated with the changes and we can view the changes to app.R
in the Shiny Assistant UI:
26.1.3 Prompt 3: Functionalize
Now that we have a UI with the correct inputs and outputs, we’ll convert these into modules and separate the application into it’s respective components.
Prompt: Convert the inputs and outputs into modules and create a standalone app function that calls separate UI and server functions.
The response to this prompt won’t change the app, but we’re given a summary of the changes in the chat:
Response:
I’ve modified the code to use a modular structure following these changes:
Created separate modules for:
Controls (inputs) -
controlsUI
andcontrolsServer
Plot (output) -
plotOutputUI
andplotOutputServer
About data section -
aboutDataUI
(UI only as it’s static)Created standalone UI and server functions:
ui_function()
- assembles all UI components
server_function()
- coordinates between modulesCreated an application function:
movie_visualization_app()
- bundles everything togetherThe communication between modules:
The controls module returns all inputs as a reactive list
The plot module takes two parameters: the data and controls reactives
This modular approach makes the code more maintainable and easier to extend. Each component has a well-defined responsibility, and they communicate through clear interfaces.
I’ve left the Shiny Assistant chat setting to Verbose, but this can be changed to return less descriptive details on the code being produced:
26.1.4 Prompt 4: Structure
Now we’re reading to start structure the application as an R package. We’ll attempt to create the files and documentation with a single prompt.
Prompt: Store the modules, UI, server, and standalone app functions in separate files and include roxygen2 documentation.
This results in an error when the Shiny Assistant tries to run our updates:
The summary tells us what changed, but doesn’t give us advice on (or even acknowledge) the error.
Response:
I’ve reorganized the code into separate files with
roxygen2
documentation:
controls_module.R
: Contains the UI and server functions for the controls module with full roxygen2 documentation including parameters, return values, and imported functions.
plot_module.R
: Contains the UI and server functions for the plot output module with documentation for both components.
about_module.R
: Contains the UI function for the about data section.**
ui.R
*: Contains the main UI function that assembles all UI components.
server.R
: Contains the main server function that coordinates between modules.
app.R
: Contains the standalone app function that sources all other files and runs the application.Each function includes proper roxygen2 documentation with:
Function descriptions
Parameter descriptions
Return value descriptions
@importFrom
tags for explicit dependencies
@export
tags for functions that should be exportedThe
app.R
file also includes logic to run the app only if the file is being executed directly in an interactive session.
26.1.5 Prompt 5: Error
The error above tells us the bslib::page_sidebar()
function isn’t being loaded before runApp()
is being called, but the call to library(bslib)
is in the app.R
file. We’ll see if providing some direction on function/file names and where to call shinyApp()
will resolve this error:
Prompt: An error occurred because the
page_sidebar
function could not be found. The app.R
file needs to contain the library()
and source()
calls before the call to shinyApp()
. Also, rename the ui.R
and server.R
to app_ui.R
and app_server.R
.
This results in the following changes to the app code structure:
Response:
I’ve made the following changes to fix the issues:
Renamed the files:
Changed
ui.R
toapp_ui.R
Changed
server.R
toapp_server.R
Modified the
app.R
file to:
Load all required libraries at the beginning of the file
Source all module and function files
Define the
movie_visualization_app()
functionCall the function directly (removed the
if (interactive())
check to ensure it runs)Maintained the same structure for all modules and functions
These changes ensure that:
All necessary files are sourced before using their functions
The application will properly find all functions when running
The organization follows a more standard Shiny module structure
The application should now run without the “page_sidebar function could not be found” error since we’re properly sourcing all the module files before they’re used in the UI and server functions.
In the app.R
file, if we explicitly run the call to library(bslib)
and then source("app_ui.R")
(which contains the call to page_sidebar()
), we see these are executed without errors in the Console:
However, when we try to source and call the standalone app function, we see the following error:
Error in runApp(x) :
Can't call `runApp()` from within `runApp()`. If your application code contains `runApp()`, please remove it.
This error is a little tricky, because we’re calling shinyApp()
, but this is happening because we’re trying to explicitly call shinyApp()
(and the UI works by call runApp()
).
We can fix this by clicking on the icon in the UI to run the app:
26.1.6 Bonus: Theme
Now that we have our modules, app functions and roxygen2
documentation, we’ll finish by adding a dark thematic
theme:
Prompt: Add a dark theme using the thematic package: https://rstudio.github.io/thematic/
The updated app includes a dark theme:
And the summary of changes is below:
Response:
I’ve integrated the thematic package to add a dark theme to the application. Here are the changes I made:
Added
library(thematic)
to the app.R file to load the thematic package.Added
thematic_shiny(font = "auto")
to automatically adjust plot themes to match the app theme. This automatically styles the ggplot2 visualizations to match the Shiny app’s theme.Modified the UI function in app_ui.R to use a dark theme:
Created a dark theme using
bs_theme()
from bslib with dark background colors, light text, and appropriate accent colorsApplied the theme to the
page_sidebar()
using thetheme
parameterUpdated the plot_module.R to work with thematic:
Removed the explicit
theme_minimal()
call since thematic will handle the base themingKept specific theme adjustments for title styling and legend positioning
The thematic package now automatically coordinates the ggplot2 theme with the application’s dark theme, creating a cohesive dark mode experience throughout the entire application. The plots will automatically inherit the dark background and appropriate colors for text and elements.
After confirming the new theme works, we’ll download the app files by clicking on the cloud icon in the upper right corner:
26.2 Finishing touches
We’ll place the downloaded files in the inst/shiny-assist/movies
folder in sap
, and after some minor changes to the files/folder organization (we’ll move the standalone app function into it’s own file in the R/
folder):
inst/shiny-assist/
└── movies
├── R
│ ├── about_module.R
│ ├── app_server.R
│ ├── app_ui.R
│ ├── controls_module.R
│ ├── movie_visualization_app.R
│ └── plot_module.R
└── app.R
3 directories, 7 files
- 1
-
R/
folder for modules and functions - 2
- Standalone app function
The app-package structure starting to take shape!
26.2.1 The app.R
file
The app.R
file no longer needs to source()
the modules and app functions if they are placed in an R/
folder.3
# Load required libraries
library(shiny)
library(ggplot2)
library(dplyr)
library(readr)
library(bslib)
library(thematic)
# Setup thematic to automatically adjust plot theme to match app theme
thematic_shiny(font = "auto")
# Run the application
movie_visualization_app()
Now app.R
only loads the packages, theme, and launches the app.
In Positron, we can see the Run App icon is present at the top of app.R
, and it’s fully functional:
26.2.2 ggplot2
warnings
We have a persistent warning printed to the console about the use of aes_string()
in our plot_module.R
.
The solution is to convert the aes_string()
to use rlang::sym()
and !!
.
aes_string()
# Create base plot
<- ggplot2::ggplot(data(),
p ::aes_string(
ggplot2x = ctrl$x_var,
y = ctrl$y_var)
)
rlang::sym()
& !!
# convert to symbols
<- rlang::sym(ctrl$x_var)
x_var <- rlang::sym(ctrl$y_var)
y_var
# Create base plot
<- ggplot2::ggplot(
p data(),
::aes(
ggplot2# use double bang!!
x = !!x_var,
y = !!y_var
) )
aes_string()
# Add color if selected
if (ctrl$color_var != "none") {
<- p + aes_string(
p color = ctrl$color_var
)
}
rlang::sym()
& !!
# Add color if selected
if (ctrl$color_var != "none") {
# convert to symbols
<- rlang::sym(ctrl$color_var)
color_var
<- p + ggplot2::aes(
p # use double bang!!
color = !!color_var
) }
Finally, we’ll add this app to our launch_app()
function:
show/hide updated launch_app() function
<- function(app = NULL, options = list(), run = "p", ...) {
launch_app if (interactive()) {
display_type(run = run)
}
if (is.null(app)) {
<- "movies"
app
}
logr_msg(glue::glue("Launching app: {app}"),
level = "INFO"
)
tryCatch({
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_preview(
quartosystem.file("quarto", "index.qmd", package = "sap"),
render = "all"
)else if (app == "assist") {
} shinyAppDir(
appDir = system.file("shiny-assist", package = "sap"),
options = options
)else {
} shinyApp(
ui = movies_ui(...),
server = movies_server,
options = options
)
}error = function(e) {
}, logr_msg(glue::glue("FATAL: Application failed to launch. Reason: {e$message}"),
level = "FATAL"
)
stop("Application launch failed. Check logs for details.")
}
) }
- 1
- New Shiny Assistant application
Recap
This chapter covered building a Shiny app ‘fit for a package’ using the Shiny Assistant. As you can see, in just a few prompts, we can have a Shiny application that ~80% ready to be loaded, installed, and launched. The callout box below includes a few items to watch out for when using Shiny Assistant to build apps:
Read more in the Shiny Assistant post on the Shiny blog.↩︎
I’ve included a .csv version of these data in the
data-raw/
folder of this branch.↩︎loadSupport()
was covered back in Section 2.3.↩︎