A _shinyelectron.yml file keeps your build settings next to your app. export() reads it automatically, so the function call stays short and the details live in version control where the rest of the project already does.
_shinyelectron.yml section at a glance: nine knobs that shape a different part of the build, from app metadata and target platforms down to the runtime and the optional multi-app launcher.Why use one
Compare a fully specified call:
With the same build driven by config:
export(appdir = "my-app", destdir = "output")A config file pulls double duty. It is a set of arguments to export(), but it is also a short document telling anyone who clones the repo how the app is meant to be built.
Getting a config file in place
The config file must be named _shinyelectron.yml and live at the root of your app directory:
my-shiny-app/
├── _shinyelectron.yml # Configuration file
├── app.R # Your Shiny app
└── ...
Generate a starter template with init_config():
init_config("path/to/my-app")✔ Created configuration file: path/to/my-app/_shinyelectron.yml
ℹ Edit this file to customize your Electron app settings
The generated file carries the documented defaults you can edit in place. For most projects, the only settings you need to write yourself are the app name and whether to auto-install Node.js:
app:
name: "My App"
nodejs:
auto_install: trueEverything else is filled in by defaults described below.
Complete reference
Every available option, annotated. Each section is covered in more detail further down.
# shinyelectron configuration file
# Documentation: https://r-pkg.thecoatlessprofessor.com/shinyelectron/
app:
name: "My Application" # Application display name
version: "1.0.0" # Application version (used in package metadata)
build:
type: "r-shiny" # Application language (autodetected if omitted)
runtime_strategy: "shinylive" # shinylive, auto-download, bundled, system, container
platforms: # Target platforms
- mac
- win
- linux
architectures: # Target architectures
- x64
- arm64
r:
version: null # R version for bundled/auto-download (null = latest release)
python:
version: null # Python version for bundled/auto-download (null = "3.12.10")
window:
width: 1200 # Default window width (pixels)
height: 800 # Default window height (pixels)
server:
port: 3838 # Development server port
icon: "branding/icon.png" # Single high-res source; electron-builder fans out to each platform
# Optional per-platform icon overrides (rarely needed):
# icons:
# mac: "branding/icon.icns"
# win: "branding/icon.ico"
# linux: "branding/icon.png"
nodejs:
version: null # Node.js version (null = latest LTS)
auto_install: false # Auto-install Node.js if not found
container: # Used when runtime_strategy is "container"
engine: "docker" # "docker" or "podman"
image: null # Container image (null = auto-select)
tag: "latest"
pull_on_start: true # Pull latest image on app start
volumes: [] # Additional volume mounts
env: [] # Additional environment variables
# Multi-app suite (2+ apps packaged in one Electron shell)
# apps:
# - id: "dashboard"
# name: "Dashboard"
# path: "./apps/dashboard"
# type: "r-shiny" # Optional per-app override (default: build.type)
# runtime_strategy: "shinylive" # Optional per-app override (default: build.runtime_strategy)
# description: "Main dashboard"
# icon: "icons/dash.png"
# - id: "admin"
# name: "Admin Panel"
# path: "./apps/admin"How values get resolved
When
export()runs, it merges three sources in priority order:
- Function arguments passed directly to
export()- Config file values from
_shinyelectron.yml- Built-in defaults
A function argument always wins. So
export(appdir = "app", destdir = "out", app_name = "Override")uses"Override"even if the config file says something else, and everything else falls through to the config or the defaults.
Section reference
app
Application metadata.
| Key | Type | Default | Description |
|---|---|---|---|
name |
string | Directory name | Display name shown in window title and system |
version |
string | "1.0.0" |
Version number for the built application |
build
The build process and its targets.
| Key | Type | Default | Description |
|---|---|---|---|
type |
string | autodetect | Application language (see below). Autodetected from files in appdir if omitted. |
runtime_strategy |
string | "shinylive" |
How the app’s runtime reaches the user (see below) |
platforms |
list | Current platform | Target operating systems: mac, win, linux
|
architectures |
list | Current arch | Target CPU architectures: x64, arm64
|
Valid type values: r-shiny (an app.R or ui.R/server.R Shiny app) or py-shiny (an app.py Shiny for Python app).
Valid runtime_strategy values:
| Strategy | Description |
|---|---|
shinylive |
Compiles to WebAssembly and runs in-browser via WebR or Pyodide (default) |
auto-download |
Downloads R or Python on first launch, caches for reuse |
bundled |
Embeds a portable R or Python runtime inside the app at build time |
system |
Uses R or Python already installed on the end user’s machine |
container |
Runs the app inside a Docker or Podman container |
All five strategies work with both r-shiny and py-shiny. See the Runtime Strategies vignette for the full discussion.
Cross-platform caveats
macOS apps build only on macOS. The
bundledstrategy ships a platform-specific runtime binary, so exporting for Windows from macOS is not supported for bundled builds.auto-download,system, andcontainersidestep that constraint. See the Runtime Strategies vignette for the full story.
icon and icons
A single top-level icon entry is the simplest and recommended shape. Point it at a high-resolution PNG (1024×1024 or larger) and electron-builder will generate the per-platform variants it needs (.icns for macOS, .ico for Windows, and the PNG itself for Linux).
icon: "branding/icon.png"If you genuinely need different artwork on different platforms (for example, a monochrome Windows icon alongside a full-color macOS icon), use the per-platform icons map as an override:
| Key | Type | Format | Description |
|---|---|---|---|
mac |
string | .icns |
macOS icon (typically 512×512 or larger) |
win |
string | .ico |
Windows icon (multi-resolution recommended) |
linux |
string | .png |
Linux icon (512×512 recommended) |
Paths are relative to the app directory. When no icon is set at all, the build uses the default Electron icon.
Creating icons
Start from one 1024×1024 PNG. That single file is enough for the default
icon:path; electron-builder handles the conversions. If you need hand-tuned.icnsor.icoartwork, convert with iconutil on macOS, ImageMagick, or any online converter.
window
Electron window dimensions.
| Key | Type | Default | Description |
|---|---|---|---|
width |
integer | 1200 |
Window width in pixels (minimum 100) |
height |
integer | 800 |
Window height in pixels (minimum 100) |
server
Development server settings.
| Key | Type | Default | Description |
|---|---|---|---|
port |
integer | 3838 |
Port for the local Shiny server (1 to 65535) |
nodejs
Node.js installation behavior.
| Key | Type | Default | Description |
|---|---|---|---|
version |
string | null |
Node.js version to install (null = latest LTS) |
auto_install |
boolean | false |
Auto-install Node.js if not found |
Auto-install is off by default. Set auto_install: true explicitly to let export() install Node.js for you when it is missing.
r
Pins R for the bundled and auto-download runtime strategies. Ignored otherwise.
| Key | Type | Default | Description |
|---|---|---|---|
version |
string | null |
R version (e.g. "4.4.1"); null uses the latest release |
python
Pins Python for the bundled and auto-download runtime strategies. Ignored otherwise.
| Key | Type | Default | Description |
|---|---|---|---|
version |
string | null |
Python version (e.g. "3.12.10"); null resolves to "3.12.10"
|
container
Used when runtime_strategy is "container". Ignored otherwise.
| Key | Type | Default | Description |
|---|---|---|---|
engine |
string | "docker" |
Container engine: "docker" or "podman"
|
image |
string | null |
Container image name (null = auto-select based on app.type) |
tag |
string | "latest" |
Image tag |
pull_on_start |
boolean | true |
Pull the latest image when the app starts |
volumes |
list | [] |
Additional volume mounts |
env |
list | [] |
Additional environment variables |
Example:
container:
engine: "docker"
image: "rocker/shiny"
tag: "4.4.1"
pull_on_start: true
volumes:
- "/data:/app/data:ro"
env:
- "SHINY_LOG_LEVEL=DEBUG"
apps
Defines a multi-app suite: two or more Shiny apps packaged in one Electron shell with a launcher screen. At least two entries are required. Any entry can override build.type or build.runtime_strategy, which is how mixed-strategy suites work.
| Key | Type | Required | Description |
|---|---|---|---|
id |
string | yes | Unique identifier (URL-safe) |
name |
string | yes | Display name in the launcher |
path |
string | yes | Relative path to the app directory |
type |
string | no | App type override (default: build.type) |
runtime_strategy |
string | no | Runtime strategy override (default: build.runtime_strategy) |
description |
string | no | Short description shown in the launcher |
icon |
string | no | Per-app icon path |
Example:
apps:
- id: "dashboard"
name: "Dashboard"
path: "./apps/dashboard"
description: "Sales analytics dashboard"
- id: "admin"
name: "Admin Panel"
path: "./apps/admin"
type: "py-shiny"
description: "User management"See the Multi-App Suites vignette for mixed-strategy examples and launcher customization.
Common recipes
Development setup
Fast iteration while you work:
app:
name: "My App (Dev)"
version: "0.0.1"
window:
width: 1000
height: 700
nodejs:
auto_install: trueProduction multi-platform build
For distribution across platforms:
app:
name: "Production App"
version: "1.0.0"
build:
type: "r-shiny"
runtime_strategy: "shinylive"
platforms:
- mac
- win
- linux
architectures:
- x64
- arm64
window:
width: 1200
height: 800
icon: "branding/icon.png"
nodejs:
version: "22.11.0"
auto_install: falseNative R app with bundled runtime
Ship R inside the app:
app:
name: "Analytics Tool"
version: "1.0.0"
build:
type: "r-shiny"
runtime_strategy: "bundled"
platforms:
- mac
- win
r:
version: "4.4.1"
nodejs:
auto_install: trueMulti-app suite
Several apps behind one launcher:
app:
name: "My App Suite"
version: "1.0.0"
build:
type: "r-shiny"
runtime_strategy: "auto-download"
apps:
- id: "dashboard"
name: "Dashboard"
path: "./apps/dashboard"
description: "Sales analytics"
- id: "admin"
name: "Admin Panel"
path: "./apps/admin"
type: "py-shiny"
description: "User management"
nodejs:
auto_install: trueWhat happens when a value is invalid
shinyelectron validates values on read and degrades gracefully rather than aborting the build:
- Invalid
typevalues warn and fall back to autodetect. - Invalid
runtime_strategyvalues warn and fall back toshinylive. - Invalid platforms and architectures are dropped with a warning.
- Window dimensions under 100 pixels warn and use defaults.
- Invalid port numbers warn and use
3838.
If the YAML itself fails to parse, shinyelectron warns and uses all defaults. This is deliberate: a broken config file should never block you from producing a build during development.
Next steps
- Getting Started: first-time user walkthrough.
-
Runtime Strategies: deep dive on
build.runtime_strategy. - Node.js Management: managing a local Node.js install.
- Troubleshooting: diagnosing build issues.