This chapter explains how to deploy shinyplanr for a new region using the Deployment Project model introduced in shinyplanr v2.0. The key design principle is that deployers never need to fork or modify the shinyplanr package source — they create a small, self-contained R project that holds only their region-specific data and configuration.
Note
This chapter is for deployers — practitioners who want to run shinyplanr for a new region. If you are looking for user instructions (how to use a running app), see the Using shinyplanr vignette. If you are setting up data for the first time, see the Setting Up vignette.
Overview
The deployment workflow has three phases:
| Phase | Where | What happens |
|---|---|---|
| 1. Setup | Your computer | Create deployment project, prepare data, generate config |
| 2. Test | Your computer | Run the app locally in RStudio or VSCode |
| 3. Deploy | Posit Connect | Push the app to Posit Connect Cloud |
The deployment project you create is a separate R project — not a fork of shinyplanr. It contains:
MyRegion/ ← Your deployment project root
├── app.R ← Entry point (auto-generated, do not edit)
├── deploy.R ← Deployment script (auto-generated)
├── MyRegion.Rproj ← RStudio project file
├── config/
│ └── shinyplanr_config.rds ← Generated by setup/3_setup_app.R
├── www/
│ ├── logo_navbar.png ← Navbar logo (auto-copied from setup/logos/)
│ ├── logo_welcome.png ← Welcome page logo (auto-copied)
│ ├── logo_funder.png ← Primary funder logo (auto-copied)
│ └── logo_funder2.png ← Second funder logo, e.g. UQ (auto-copied if set)
├── setup/
│ ├── 1_setup_enviro.R ← Install packages + renv
│ ├── 2_setup_data.R ← Spatial data preparation
│ ├── 3_setup_app.R ← App configuration
│ ├── Dict_Feature.csv ← Feature dictionary
│ ├── data/ ← Your raw spatial data files
│ ├── logos/ ← Your logo files
│ └── content/ ← Your help text (markdown)
├── renv.lock ← Package version lock (if using renv)
└── .Rprofile ← renv activation (if using renv)
Steps 1–7: Set Up the Deployment Project
Before deploying, you need to create and configure your deployment project. This is covered in full in the Setting Up vignette. In summary (install shinyplanr first — see the Prerequisites in that vignette):
-
Create the project:
create_shinyplanr_template(country = "MyRegion", crs = "...") -
Install packages: open the new project and run
setup/1_setup_enviro.R -
Prepare data: edit and run
setup/2_setup_data.R -
Configure the app: edit and run
setup/3_setup_app.R— this generatesconfig/shinyplanr_config.rds -
Define features: edit
Dict_Feature.csv -
Customise content: edit markdown files in
setup/content/ -
Test locally:
shiny::runApp()from inside the deployment project
Important
All setup scripts must be run from inside the deployment project (i.e. with
MyRegion.Rprojopen in RStudio), not from the shinyplanr package directory.
Once the app runs correctly locally, proceed with the deployment steps below.
Step 8: Lock Package Versions with renv
Before deploying, lock the exact versions of all packages used (including shinyplanr) to ensure the deployed app is identical to your local test:
renv::snapshot()This writes renv.lock. Commit this file to version control if you are using git.
When to run renv::snapshot()
setup/1_setup_enviro.Ralready callsrenv::snapshot()at the end of its run. You only need to re-run it manually if you have added or updated packages since then. Always runrenv::snapshot()from inside the deployment project, not from the shinyplanr package directory.
Important
Why this matters: When you deploy to Posit Connect, it installs packages fresh from GitHub. Without
renv.lock, it could install a newer version of shinyplanr that is incompatible with your config file. The lock file pins the exact GitHub commit SHA used during local testing.
Upgrading shinyplanr
When a new version of shinyplanr is released and you want to upgrade:
renv::update("shinyplanr") # Install new version
source("setup/3_setup_app.R") # Regenerate config
shiny::runApp() # Test locally
renv::snapshot() # Update lock file
source("deploy.R") # DeployIf the new version has an incompatible config schema, load_config() will print a clear error message explaining what changed.
Step 9: Set Up Posit Connect
You only need to do this once per Posit Connect account:
- Go to Posit Connect Cloud and sign in (or create an account)
- Create an API key: Account → API Keys → New API Key
- In R, run:
rsconnect::setAccountInfo(
name = "your-account-name",
token = "your-token",
secret = "your-secret"
)Step 10: Deploy to Posit Connect
Run the deployment script:
source("deploy.R")This calls rsconnect::deployApp() with a curated list of files:
-
app.Randdeploy.R - The entire
config/directory (containingshinyplanr_config.rds) - The entire
www/directory (containing your logos) -
renv.lock,.Rprofile, andrenv/activate.R(if present)
Data files are not uploaded — the spatial data is embedded inside shinyplanr_config.rds which is already uploaded via config/.
Note
The first deployment will take a few minutes as Posit Connect installs all required packages. Subsequent deployments are faster.
After deployment, Posit Connect will provide a URL for your app. Share this with your stakeholders.
Version Compatibility
shinyplanr uses a config schema version (separate from the package version) to detect incompatible config files. The schema version only increments when the structure of the config changes (keys added, removed, or renamed).
If you try to run an app with a mismatched config, load_config() will stop with a clear error:
Error: Config schema version mismatch.
Config file schema version : 1
Package schema version : 2
Config path: config/shinyplanr_config.rds
Re-run setup/3_setup_app.R to regenerate the config with the current schema.
The fix is always the same: re-run setup/3_setup_app.R to regenerate the config, then re-deploy.
Troubleshooting
App fails to start on Posit Connect
Check that:
-
config/shinyplanr_config.rdswas included in the deployment (checkdeploy.R) -
renv.lockis included and matches your local environment - The shinyplanr package version installed by Posit Connect matches your local version
“Config schema version mismatch” error
Re-run setup/3_setup_app.R in your deployment project to regenerate the config for the current package version.
Missing logos
Ensure setup/3_setup_app.R ran successfully (logo copy step). Check that logo files exist in setup/logos/ and that www/ was populated.
Local app works but deployed app fails
This almost always means a package version mismatch between local and Posit Connect. Run renv::snapshot() locally and re-deploy.
“value for ‘Dict’ not found” or similar
load_config() was not called, or failed silently. Check that app.R in the project root calls shinyplanr::load_config("config/shinyplanr_config.rds") before shinyplanr::run_app().
Interactive maps (Layer Information / Explore / Coverage) are much slower on Posit Connect Cloud than on localhost
This is a known characteristic of the leafgl WebGL rendering package, not a bug specific to your deployment, and it gets worse as planning-unit count grows.
Root cause: leafgl::addGlPolygons() has no incremental-update API. Every call — whether you’re changing the fill colour, switching layers, or clicking a planning unit — re-serialises the entire polygon geometry to GeoJSON and pushes it fresh over the Shiny websocket. There is no way to “just recolour” a layer already on the map. For a grid of ~30,000 hexagonal planning units this payload is several megabytes of JSON per render.
On your local machine, the Shiny websocket runs over loopback (effectively instant, unlimited bandwidth), so this cost is invisible. On Posit Connect Cloud, that same payload travels over the real internet to the browser, so the cost is paid in full on every layer change — this is a network latency/bandwidth problem, not primarily a CPU or RAM sizing problem. Increasing your Connect Cloud instance size (RAM/CPU) will not fix this, because the bottleneck is data transfer, not computation.
What shinyplanr does to mitigate this:
- Coordinate precision sent to the browser is truncated to 5 decimal degrees (~1.1 m at the equator) via
leafgl::addGlPolygons(..., digits = 5), roughly halving the JSON payload size with no visible effect on the map (planning units are always much larger than 1 m). See the code comments inmod_4afeatures.R,mod_2scenario.R, andmod_5coverage.Rfor details. - Planning-unit polygons should already be as simple as possible:
spatialgridr::get_grid(output = "sf_hex")produces hexagons with the minimum 7 coordinate rows (6 vertices + closing point). There is no further simplification available without changing the grid shape itself. - A previous attempt to cache a static “base” WebGL layer across dropdown changes (to avoid resending the full grid) was tried and then reverted, because it only reduced payload on binary-to-binary layer switches (Feature → LockIn → LockOut) and added overhead for the more common case of users browsing freely across layer types (Feature, Cost, Climate, Bioregion, etc.) — a net loss for typical usage patterns.
What you can additionally check:
- Confirm your Posit Connect Cloud instance’s paid tier actually increases usable network/CPU throughput and not just RAM headroom — check Posit’s current documentation, as this is not something shinyplanr can control or verify from the app code.
- Verify (via your browser’s DevTools → Network tab) that HTTP/websocket responses are compressed (
content-encoding: gziporbr) — highly repetitive JSON payloads like GeoJSON compress very well, and if compression is disabled at the proxy layer this cost is paid in full. - If interactive map performance remains a persistent problem for a large deployment, consider raising this with the shinyplanr maintainers: a longer-term architectural fix under consideration is migrating these maps from
leaflet/leafglto themapglpackage (MapLibre GL JS bindings), which supports true client-side re-styling (setPaintProperty()) without re-uploading geometry on every change.
