flowchart TD A[Golem Philosophy] --> B[Package-Based Architecture] A --> C[Structured Development] A --> D[Production Focus] A --> E[Team Collaboration] B --> B1[Dependency Management] B --> B2[Testing Infrastructure] B --> B3[Documentation Standards] C --> C1[Guided Workflow] C --> C2[Best Practice Enforcement] C --> C3[Standardized Structure] D --> D1[Deployment Ready] D --> D2[Configuration Management] D --> D3[Monitoring Integration] E --> E1[Modular Development] E --> E2[Version Control Integration] E --> E3[Code Review Processes] style A fill:#e1f5fe style B fill:#f3e5f5 style C fill:#e8f5e8 style D fill:#fff3e0 style E fill:#fce4ec
Key Takeaways
- Production-Grade Architecture: Transform Shiny applications into robust, enterprise-ready packages with automated testing, documentation, and deployment capabilities
- Structured Development Workflow: Master the complete Golem development lifecycle from project initialization through production deployment with guided best practices
- Package-Based Benefits: Leverage R package infrastructure for dependency management, testing frameworks, and professional distribution methods
- Team Collaboration Framework: Implement standardized project structures and workflows that support multiple developers and complex application requirements
- Enterprise Integration: Build applications that integrate seamlessly with corporate infrastructure, security requirements, and deployment pipelines
Introduction
The transition from prototype to production represents the most challenging phase in Shiny application development. While Shiny makes it remarkably easy to create functional applications quickly, scaling these applications to enterprise-grade systems requires architectural decisions and development practices that most data scientists haven’t encountered. The Golem framework bridges this gap, providing an opinionated yet flexible structure for building production-ready Shiny applications.
The motivation behind Golem recognizes a fundamental truth: building a proof-of-concept application is easy, but things change dramatically when applications become larger and more complex, especially when you need to send that app to production. Until recently, there was no real framework for building and deploying production-grade Shiny apps. This is where Golem comes into play, offering Shiny developers a toolkit for making stable, easy-to-maintain, and robust production web applications with R.
Golem represents more than just another R package—it’s a complete development philosophy that transforms how you think about Shiny applications. By treating your Shiny app as an R package from the beginning, Golem unlocks professional development practices including automated testing, comprehensive documentation, systematic dependency management, and standardized deployment procedures.
Understanding the Golem Framework
What Makes Golem Different
Golem is an opinionated framework for building production-ready Shiny applications. This means it provides structured approaches to common development challenges while maintaining flexibility for specific requirements. The framework emphasizes several key principles:
Package-First Approach: A Golem application is contained inside a package. This isn’t just a technical detail—it’s a fundamental architectural decision that enables professional development practices. Everything you know about package development can be applied to Golem, making it easier to manage dependencies, write tests, create documentation, and distribute your application.
Modular by Design: Golem works better if you are working with Shiny modules. The framework encourages modular development from the beginning, making applications easier to maintain, test, and extend. Knowing how modules work is recommended but not necessary to get started.
Production-Oriented: Unlike frameworks designed for rapid prototyping, Golem focuses on building applications that will be sent to production. This means considering factors like configuration management, deployment automation, monitoring integration, and long-term maintenance from the project’s inception.
The Golemverse Ecosystem
Golem is part of the golemverse, a series of tools for building production Shiny apps. This ecosystem includes complementary packages that extend Golem’s capabilities:
- attachment: Automatic dependency management and DESCRIPTION file maintenance
- fakir: Fake data generation for prototyping and testing
- shinipsum: Lorem ipsum for Shiny, enabling UI-first development
- dockerfiler: Docker integration for containerized deployment
- covrpage: Coverage reporting and documentation
Getting Started with Golem
Installation and Setup
Install the stable version from CRAN:
install.packages("golem")
For the latest development features:
# install.packages("remotes")
::install_github("Thinkr-open/golem") remotes
Creating Your First Golem Project
Using RStudio (Recommended):
- File → New Project → New Directory
- Choose “Package for Shiny App Using golem”
- Enter your project name (e.g., “myAwesomeApp”)
- Choose project location and create
Command Line Creation:
::create_golem("path/to/myAwesomeApp") golem
This creates a complete project structure with all necessary files and directories pre-configured for production development.
Understanding the Golem Project Structure
A new Golem project includes a comprehensive structure designed for professional development:
myAwesomeApp/
├── DESCRIPTION # Package metadata and dependencies
├── NAMESPACE # Package namespace (auto-generated)
├── NEWS.md # Change log
├── README.Rmd # Project documentation
├── dev/ # Development scripts
│ ├── 01_start.R # Initial project setup
│ ├── 02_dev.R # Development workflow
│ ├── 03_deploy.R # Deployment configuration
│ └── run_dev.R # Development server
├── R/ # Application code
│ ├── app_config.R # Configuration management
│ ├── app_server.R # Main server logic
│ ├── app_ui.R # Main UI definition
│ └── run_app.R # Application launcher
├── inst/ # Package resources
│ ├── app/www/ # Web assets (CSS, JS, images)
│ └── golem-config.yml # Environment configuration
├── man/ # Documentation (auto-generated)
└── tests/ # Testing infrastructure
└── testthat/ # Unit tests
The Golem Development Workflow
Phase 1: Project Initialization (dev/01_start.R)
The first development script guides you through essential project setup tasks:
## Fill the DESCRIPTION ----
::fill_desc(
golempkg_name = "myAwesomeApp",
pkg_title = "My Awesome Shiny Application",
pkg_description = "A production-ready dashboard for business analytics.",
author_first_name = "John",
author_last_name = "Smith",
author_email = "john.smith@company.com",
repo_url = "https://github.com/company/myAwesomeApp"
)
## Set golem options ----
::set_golem_options()
golem
## Create Common Files ----
::use_mit_license(name = "John Smith")
usethis::use_readme_rmd(open = FALSE)
usethis::use_code_of_conduct(contact = "john.smith@company.com")
usethis::use_lifecycle_badge("Experimental")
usethis::use_news_md(open = FALSE)
usethis
## Use git ----
::use_git()
usethis
## Init Testing Infrastructure ----
::use_recommended_tests()
golem
## Use Recommended Packages ----
::use_recommended_deps()
golem
## Favicon ----
::use_favicon()
golem
## Add helper functions ----
::use_utils_ui()
golem::use_utils_server() golem
## Configure Package Dependencies ----
# Add specific packages your app will use
::use_package("DT")
usethis::use_package("plotly")
usethis::use_package("dplyr")
usethis
## Add Internal Datasets ----
# For built-in datasets your app needs
::use_data_raw("sample_data")
usethis
## Set Up Environment Configuration ----
# Configure different environments (dev, test, prod)
::use_recommended_tests()
golem
## Documentation Setup ----
# Prepare for comprehensive documentation
::use_vignette("getting-started")
usethis::use_pkgdown() usethis
This initialization phase establishes the foundation for professional development by configuring package metadata, setting up testing infrastructure, establishing documentation standards, and preparing for version control integration.
Phase 2: Development Workflow (dev/02_dev.R)
The development script provides structure for building your application:
## Add modules ----
::add_module(name = "data_input")
golem::add_module(name = "visualization")
golem::add_module(name = "analysis")
golem
## Add utility functions ----
::add_utils("data_processing")
golem::add_fct("calculations")
golem
## Add external resources ----
::add_css_file("custom")
golem::add_js_file("interactions")
golem
## Dependencies ----
# Automatically detected and added to DESCRIPTION
::att_amend_desc()
attachment
## Documentation ----
::document() devtools
Creating Modules with Golem:
# Generate a new module
::add_module(name = "dashboard_summary", with_test = TRUE) golem
This creates two files: - R/mod_dashboard_summary.R
- Module implementation - tests/testthat/test-mod_dashboard_summary.R
- Module tests
Module Template Structure:
# R/mod_dashboard_summary.R
#' dashboard_summary UI Function
#'
#' @description A shiny Module.
#'
#' @param id,input,output,session Internal parameters for {shiny}.
#'
#' @noRd
#'
#' @importFrom shiny NS tagList
<- function(id){
mod_dashboard_summary_ui <- NS(id)
ns tagList(
h3("Dashboard Summary"),
plotOutput(ns("summary_plot")),
::dataTableOutput(ns("summary_table"))
DT
)
}
#' dashboard_summary Server Functions
#'
#' @noRd
<- function(id, data){
mod_dashboard_summary_server moduleServer(id, function(input, output, session){
<- session$ns
ns
$summary_plot <- renderPlot({
output# Plotting logic using data()
})
$summary_table <- DT::renderDataTable({
output# Table generation using data()
})
}) }
Phase 3: Deployment Configuration (dev/03_deploy.R)
The deployment script prepares your application for production:
## Docker ----
::add_dockerfile()
golem::add_dockerfile_with_renv()
golem
## RStudio Connect ----
::add_rstudioconnect_file()
golem
## Shinyapps.io ----
::add_shinyappsio_file()
golem
## Posit Connect ----
::add_positconnect_file()
golem
## Other platforms ----
::add_dockerfile_heroku()
golem::add_dockerfile_shinyproxy() golem
Each deployment function creates platform-specific configuration files and deployment scripts optimized for your application’s requirements.
Advanced Golem Features
Configuration Management
Golem provides sophisticated configuration management through the golem-config.yml
file:
# inst/golem-config.yml
default:
golem_name: myAwesomeApp
golem_version: 0.0.0.9000
app_prod: false
database_url: "sqlite://./dev.db"
api_endpoint: "https://api-dev.company.com"
production:
app_prod: true
database_url: !expr Sys.getenv("DATABASE_URL")
api_endpoint: "https://api.company.com"
testing:
database_url: "sqlite://./test.db"
api_endpoint: "https://api-test.company.com"
Accessing Configuration in Your App:
# In your application code
<- golem::get_golem_config()
config <- config$database_url
database_url <- config$api_endpoint
api_endpoint
# Environment-specific behavior
if (golem::app_prod()) {
# Production-specific logic
else {
} # Development-specific logic
}
Business Logic Separation
Golem encourages separating business logic from Shiny-specific code:
# R/fct_calculations.R - Pure business logic
<- function(investment, return_value) {
calculate_roi if (investment <= 0) {
stop("Investment must be positive")
}- investment) / investment) * 100
((return_value
}
<- function(data) {
calculate_portfolio_metrics %>%
data group_by(asset_class) %>%
summarise(
total_investment = sum(investment),
total_return = sum(return_value),
roi = calculate_roi(total_investment, total_return),
.groups = "drop"
)
}
# R/mod_portfolio_analysis.R - Shiny module using business logic
<- function(id, portfolio_data) {
mod_portfolio_analysis_server moduleServer(id, function(input, output, session) {
<- reactive({
metrics req(portfolio_data())
calculate_portfolio_metrics(portfolio_data())
})
$metrics_table <- DT::renderDataTable({
outputmetrics()
})
}) }
Testing Infrastructure
Golem automatically sets up comprehensive testing infrastructure:
# tests/testthat/test-fct_calculations.R
test_that("ROI calculation works correctly", {
expect_equal(calculate_roi(100, 150), 50)
expect_equal(calculate_roi(1000, 1200), 20)
expect_error(calculate_roi(-100, 150))
})
# tests/testthat/test-mod_portfolio_analysis.R
test_that("Portfolio module loads without error", {
testServer(mod_portfolio_analysis_server, {
# Module testing logic
})
})
# tests/testthat/test-golem_utils_server.R
test_that("App launches", {
expect_running(sleep = 5)
})
Common Issues and Solutions
Issue 1: Package Loading and Dependencies
Problem: Modules or functions not found when running the app, especially after adding new dependencies.
Solution: Use Golem’s dependency management workflow:
# 1. Add package dependencies explicitly
::use_package("ggplot2")
usethis::use_package("dplyr")
usethis
# 2. Update DESCRIPTION automatically
::att_amend_desc()
attachment
# 3. Document and reload
::document()
devtools::load_all()
devtools
# 4. Check that everything works
::run_dev() golem
Issue 2: Configuration Not Loading
Problem: Configuration values from golem-config.yml
not accessible in the application.
Solution: Ensure proper configuration setup and access:
# In app_config.R, verify config is loaded
<- golem::get_golem_config()
config
# In your modules, access config properly
<- function(id) {
mod_my_module_server moduleServer(id, function(input, output, session) {
# Get config at module level
<- golem::get_golem_config()
config
$debug_info <- renderText({
outputpaste("App version:", config$golem_version)
})
})
}
# Set environment variable for different configs
Sys.setenv("GOLEM_CONFIG_ACTIVE" = "production")
Issue 3: Module Communication Complexity
Problem: Difficulty passing data between modules in a Golem application.
Solution: Use reactive values and proper module communication patterns:
# In app_server.R
<- function(input, output, session) {
app_server # Shared reactive values
<- reactiveValues(
shared_data current_dataset = NULL,
filters = list(),
analysis_results = NULL
)
# Module communication through shared state
mod_data_input_server("data_input", shared_data)
mod_analysis_server("analysis", shared_data)
mod_visualization_server("viz", shared_data)
}
# In modules
<- function(id, shared_data) {
mod_data_input_server moduleServer(id, function(input, output, session) {
observeEvent(input$load_data, {
# Update shared data
$current_dataset <- load_dataset()
shared_data
})
})
}
<- function(id, shared_data) {
mod_analysis_server moduleServer(id, function(input, output, session) {
# React to shared data changes
observe({
req(shared_data$current_dataset)
$analysis_results <- analyze_data(shared_data$current_dataset)
shared_data
})
}) }
Common Questions About Golem Framework
Choose Golem when you’re building applications that will be maintained over time, developed by multiple people, or deployed to production environments. If your application will have more than 1000 lines of code, require automated testing, need systematic deployment, or be used by non-technical stakeholders, Golem provides essential infrastructure. However, for simple prototypes, personal projects, or one-off analyses, the overhead of Golem’s package structure may not be justified. The decision point is often whether you need the professional development practices that Golem enables.
While not strictly required, understanding R package development significantly improves your Golem experience. Golem automates many package development tasks, but you’ll be more effective if you understand concepts like DESCRIPTION files, NAMESPACE management, and testing with testthat. If you’re new to packages, start with Golem and learn package concepts as you encounter them. The framework provides good defaults and guided workflows that teach package development practices implicitly.
Golem uses the golem-config.yml
file for environment-specific configuration and the GOLEM_CONFIG_ACTIVE
environment variable to switch between configurations. You can define different settings for development, testing, and production environments, then activate the appropriate configuration during deployment. The framework also generates deployment-specific files for various platforms (Docker, RStudio Connect, Heroku) with appropriate configuration handling built in.
Yes, but the migration effort depends on your application’s current structure. Well-organized applications with modular code migrate more easily. The process typically involves: creating a new Golem project, moving UI and server code into Golem’s structure, converting functions to modules where appropriate, setting up package dependencies, and configuring testing infrastructure. For large applications, plan for a gradual migration where you can test each component as you move it. The investment is usually worthwhile for applications that will continue to be developed and maintained.
Golem is more mature and opinionated, providing a complete package-based development workflow with extensive tooling for professional development. Rhino is newer and focuses more on modern web development practices with TypeScript integration. Golem is better for teams familiar with R package development and those wanting comprehensive documentation and deployment tooling. Rhino might be better for teams with strong front-end development skills or those requiring advanced JavaScript integration. Both frameworks solve similar problems but with different philosophical approaches to Shiny development.
Test Your Understanding
You’re setting up a new Golem project for a financial analytics application that will be developed by a team of 3 developers over 6 months. The application needs to connect to multiple databases, provide different interfaces for analysts and managers, and be deployed to both staging and production environments. Which Golem setup approach is most appropriate?
- Create separate Golem projects for each user type and environment
- Use a single Golem project with modules for different user types and environment configuration
- Start with a simple Golem setup and add complexity later
- Use Golem only for production and develop features in separate standard Shiny apps
- Consider the team collaboration requirements
- Think about configuration management for different environments
- Remember that Golem is designed for complex, multi-developer projects
- Consider the benefits of unified development vs. separated concerns
B) Use a single Golem project with modules for different user types and environment configuration
This approach leverages Golem’s strengths for complex, collaborative development:
Why this is correct:
- Unified Codebase: Single project enables shared business logic, consistent testing, and unified deployment
- Modular Architecture: Different user interfaces can be implemented as separate modules while sharing core functionality
- Environment Management: Golem’s configuration system handles multiple deployment environments elegantly
- Team Collaboration: Single project structure supports multiple developers working on different modules simultaneously
Implementation strategy:
# Module structure
::add_module("analyst_dashboard")
golem::add_module("manager_dashboard")
golem::add_module("data_connection")
golem::add_module("user_management")
golem
# Configuration for environments
# inst/golem-config.yml
:
default: "dev_analyst_db"
database_analyst: "dev_manager_db"
database_manager
:
production: !expr Sys.getenv("PROD_ANALYST_DB")
database_analyst: !expr Sys.getenv("PROD_MANAGER_DB") database_manager
Why other options are less suitable:
- Option A: Creates unnecessary complexity and code duplication
- Option C: Underestimates the complexity requirements and team needs
- Option D: Defeats the purpose of using Golem for collaborative development
You’re in the middle of developing a Golem application and need to add a new feature that requires a complex data processing module and integration with an external API. Complete the proper Golem workflow for adding this functionality:
# Step 1: Add the module
::add_module(name = "api_integration", ________)
golem
# Step 2: Add business logic functions
::add_fct("________")
golem
# Step 3: Add required dependencies
::use_package("________")
usethis::use_package("________")
usethis
# Step 4: Update package documentation
________()
# Step 5: Update dependencies automatically
________()
- Consider what parameters the add_module function needs for testing
- Think about what type of functions you’d need for API integration
- Consider common packages for HTTP requests and data processing
- Remember Golem’s automatic dependency management tools
# Step 1: Add the module with testing
::add_module(name = "api_integration", with_test = TRUE)
golem
# Step 2: Add business logic functions
::add_fct("api_processing")
golem
# Step 3: Add required dependencies
::use_package("httr2") # For HTTP requests
usethis::use_package("jsonlite") # For JSON processing
usethis
# Step 4: Update package documentation
::document()
devtools
# Step 5: Update dependencies automatically
::att_amend_desc() attachment
Workflow explanation:
- with_test = TRUE: Creates both the module and its test file, following TDD practices
- add_fct(): Creates a file for business logic functions separate from Shiny-specific code
- HTTP/JSON packages: Essential for API integration work
- devtools::document(): Updates package documentation based on roxygen2 comments
- att_amend_desc(): Automatically scans code and updates DESCRIPTION file with dependencies
Additional best practices:
# After adding the module, you might also want to:
::add_utils("api_helpers") # For utility functions
golem::use_recommended_tests() # Ensure comprehensive testing setup
golem::load_all() # Reload package for testing
devtools::run_dev() # Test the changes golem
You’re advising three different teams on whether to use Golem or standard Shiny development. Match each scenario with the most appropriate recommendation:
Scenario A: A solo data scientist building a quick exploratory dashboard for personal use to analyze quarterly sales data. Timeline: 1 week, audience: self only.
Scenario B: A 4-person analytics team building a customer insights platform that will be used by 50+ business users across multiple departments. Timeline: 6 months, needs automated testing and deployment.
Scenario C: A research team creating an interactive supplement for a journal publication. Timeline: 2 months, audience: academic peers, needs to be reproducible and publicly available.
Recommendations:
- Standard Shiny - simple and fast for individual use
- Golem - essential for enterprise-grade requirements
- Golem - beneficial for reproducibility and professional presentation
- Consider complexity, timeline, and audience for each scenario
- Think about maintenance and collaboration requirements
- Consider the balance between development overhead and long-term benefits
Scenario A → Recommendation 1 (Standard Shiny)
- Quick timeline and personal use case don’t justify Golem’s overhead
- No collaboration or long-term maintenance requirements
- Standard Shiny provides faster development for simple exploratory work
Scenario B → Recommendation 2 (Golem)
- Multi-developer team requires structured collaboration
- Enterprise deployment needs professional development practices
- 50+ users demand reliability, testing, and proper deployment procedures
- 6-month timeline justifies investment in proper architecture
Scenario C → Recommendation 3 (Golem)
- Academic reproducibility benefits from package structure
- Public availability requires professional presentation
- Documentation and testing improve credibility
- Moderate complexity and timeline make Golem investment worthwhile
Decision factors:
- Team size: Multiple developers → Golem
- Audience: Enterprise/public → Golem, Personal → Standard
- Timeline: Short exploratory → Standard, Long-term → Golem
- Requirements: Testing, deployment, documentation → Golem
- Maintenance: One-time use → Standard, Ongoing → Golem
Conclusion
The Golem framework represents a paradigm shift in Shiny development, transforming applications from simple scripts into robust, enterprise-grade software. By embracing package-based development from the beginning, Golem provides the infrastructure necessary for building applications that not only work today but remain maintainable and extensible as requirements evolve and teams grow.
The structured workflow that Golem provides—from initial project setup through production deployment—eliminates the guesswork from professional Shiny development. The framework’s opinionated approach ensures that teams follow best practices for testing, documentation, configuration management, and deployment, while its flexibility allows for customization based on specific organizational needs.
Most importantly, Golem bridges the gap between data science and software engineering, providing data scientists with professional development tools while maintaining the rapid development cycles that make Shiny attractive. The result is applications that serve not just as analytical tools but as reliable, scalable business systems that can support organizational decision-making at scale.
Next Steps
Based on what you’ve learned about the Golem framework, here are the recommended paths for continuing your production-grade Shiny development journey:
Immediate Next Steps (Complete These First)
- Testing and Debugging Strategies - Master the testing infrastructure that Golem provides and learn advanced debugging techniques
- Production Deployment Overview - Leverage Golem’s deployment capabilities for professional application hosting
- Practice Exercise: Create a new Golem project and implement a multi-module application using the complete development workflow
Building on Your Foundation (Choose Your Path)
For Advanced Architecture:
For Production Excellence:
For Team Leadership:
Long-term Goals (2-4 Weeks)
- Build a complete Golem application with multiple modules, comprehensive testing, and production deployment
- Establish team standards and workflows based on Golem best practices
- Create organizational templates and guidelines for Golem-based development
- Implement continuous integration and deployment pipelines using Golem’s deployment tools
Explore More Articles
Here are more articles from the same category to help you dive deeper into professional Shiny development practices.
Reuse
Citation
@online{kassambara2025,
author = {Kassambara, Alboukadel},
title = {Golem {Framework} and {Workflow:} {Production-Grade} {Shiny}
{Development}},
date = {2025-05-23},
url = {https://www.datanovia.com/learn/tools/shiny-apps/best-practices/golem-framework.html},
langid = {en}
}