
The Long Road to Native R Desktop Apps — and Why RDesk Changes Everything
Source:vignettes/rdesk-article.Rmd
rdesk-article.RmdThere is a question that every R developer eventually asks.
They have built something genuinely useful — a model, a dashboard, an analysis tool that saves their team hours every week. Their colleagues want to use it. But their colleagues do not have R installed. Their IT department will not install R. The server request has been sitting in a queue for three months.
The question is simple: how do I get this R tool into the hands of someone who does not use R?
The R community has been trying to answer this question for over a decade. The story of those attempts is worth telling, because understanding why they all fell short explains exactly why RDesk exists — and why it is the first answer that actually works.
2014: The First Serious Attempt
In April 2014, a researcher named Lee Pang published a blog post titled “Deploying Self-Contained R Apps to the Desktop.” He had a practical problem: he built R applications for colleagues who could not install R on their machines. His solution was ingenious for its time — bundle a portable R executable alongside the Shiny app, launch Shiny in the background, and open the result in the user’s default web browser.
He called the framework DesktopDeployR.
It was genuinely clever. It worked. People used it. The GitHub
repository accumulated forks and adaptations. But it had a fundamental
limitation that no amount of maintenance could fix: the user was still
looking at a browser tab. The app lived at localhost:1234.
It opened ports. It could be killed by closing the wrong browser window.
It required the user to wait for a local server to start before anything
appeared on screen.
It was not a desktop app. It was a Shiny app with extra steps.
2017: RInno and the Electron Experiment
Three years later, a package called RInno arrived on CRAN. The idea was more ambitious — wrap the Shiny app in Electron, the framework that powers VS Code, Slack, and Discord, and produce a real Windows installer.
For a brief window it seemed promising. But the problems multiplied quickly. Electron bundles a complete copy of Chromium — adding 150MB to every application before you write a single line of R code. The Shiny server still ran inside the Electron shell, still opened a port, still required httpuv. Zombie R processes accumulated silently when apps crashed or were closed unexpectedly.
RInno was archived on 2025-06-12 as issues were not corrected despite reminders. By the time it was archived, its GitHub issues were full of developers reporting crashes on modern R versions — the same version mismatch problem that DesktopDeployR had never solved either.
The R community had tried twice. Both times the answer was the same: wrap Shiny in something, open a port, hope nothing crashes.
The Problem Nobody Named
Looking at these failed attempts, a pattern emerges. Every approach shared the same unexamined assumption: the GUI must be served over HTTP.
DesktopDeployR served Shiny over localhost. RInno served Shiny inside Electron over localhost. Both required a TCP port. Both exposed that port to the local network. Both inherited every limitation of browser-based delivery — no native menus, no system tray, no file dialogs, no offline-first guarantee, no way to pass enterprise security audits.
Nobody asked whether the HTTP assumption was necessary. It was simply inherited from Shiny, which inherited it from the web.
Meanwhile, in the broader software world, desktop frameworks were undergoing a quiet revolution. Microsoft released WebView2 as a stable production API in 2021. WebView2 is the rendering engine of Microsoft Edge — the same Chromium-based engine — but exposed as a native Windows control that can be embedded in any application. And critically, WebView2 has a feature that changes everything: virtual hostname file serving.
Instead of starting an HTTP server to serve local files, WebView2
maps a folder directly to an HTTPS hostname internally. No port. No TCP
stack. No network. The files load directly from disk through the OS, as
if they came from https://app.rdesk/.
This is the primitive that makes zero-port architecture possible. It did not exist in stable form until 2021. That is precisely why nobody built RDesk before 2024.
What RInside Got Right — and Why It Was Not Enough
There is one more chapter in this story that deserves mention.
RInside, maintained by Dirk Eddelbuettel, takes a
completely different approach. Instead of serving R over a network, it
embeds the R interpreter directly inside a C++ application. No ports. No
HTTP. The C++ code calls R functions directly via
parseEvalQ().
This is architecturally clean. But for an R developer — which is most of the people asking “how do I distribute this?” — RInside is the wrong answer. You have to write C++. You have to manage the embedded R interpreter lifecycle. The tool that solves the distribution problem must be writable by an R developer using only R.
2024: The Architecture That Should Have Existed
RDesk starts from a different premise than everything that came before it.
Instead of asking “how do we put Shiny in a desktop wrapper?”, it asks: “what would a desktop framework look like if we designed it for R developers from scratch, using 2024 technology?”
The answer has five parts.
First: zero ports by design. RDesk uses WebView2’s
virtual hostname API to serve the www/ folder. Static
assets load directly from disk with no HTTP server involved. Dynamic
communication between R and the UI uses stdin/stdout pipes and
PostWebMessageAsString — a direct Win32 message, not a
network call. Running netstat on a RDesk app shows
nothing.
Second: R is in charge. In RDesk, R is the main process. The C++ launcher is a thin shell that provides the native window. Developers write only R and HTML/CSS/JS.
Third: async that actually works. RDesk’s three-tier
async engine is built on mirai with callr as a
fallback. Pre-warmed daemon pools mean background tasks start in
milliseconds. The async() wrapper handles loading overlays,
cancellation, and result routing automatically.
Fourth: genuine distribution.
build_app() produces a self-contained distributable. As of
v1.0.5, it copies the developer’s own installed R as the runtime —
guaranteeing the package versions and the runtime always match. No more
version-mismatch crashes.
Fifth: one command to start.
rdesk_create_app("MyApp") generates a working dashboard
with real data, async processing, a native menu, and a loading overlay.
A new developer has a running native app in under five minutes.
The Comparison That Matters
| Shiny | RInno (archived) | Electron + R | RDesk | |
|---|---|---|---|---|
| Network ports | Yes | Yes | Yes | Zero |
| Offline use | No | Partial | Partial | Yes |
| Native menus | No | No | Via JS | Yes |
| Distribution | Server deploy | Installer | Installer | ZIP or installer |
| App size | Server-side | 350MB+ | 350-500MB | ~200MB |
| Feels like | A website | A website | A website | Excel / Tableau |
| R skill required | Full R | Full R + Electron | Full R + Node.js | Full R only |
Who This Is For
RDesk was built for a specific kind of developer and a specific kind of situation.
You are an R developer. You have built something genuinely useful — a model scoring tool, a data quality dashboard, a report generator, a clinical data viewer. Your colleagues want to use it. But they are not R users. They are clinicians, analysts, finance teams, operations managers. They have Windows laptops.
Shiny is the wrong answer. A Shiny app requires a server, a URL, and a browser — and every one of those requirements is a conversation with IT that might not go anywhere.
RDesk gives you a .exe installer. IT understands
installers. Silent install via /SILENT flag works with SCCM
and Intune. The app runs offline. No ports to open in the firewall. No
server to maintain.
Getting Started
RDesk is on CRAN.
install.packages("RDesk")Create a working app in one line:
RDesk::rdesk_create_app("MyFirstApp")When you are ready to distribute:
RDesk::build_app(
app_dir = "MyFirstApp",
app_name = "MyFirstApp",
build_installer = TRUE
)
# Output: dist/MyFirstApp-1.0.0-setup.exeSend that file to anyone on Windows. They double-click it. Done.
Full documentation, a Shiny migration guide, an async guide, and a practical cookbook are at:
https://janakiraman-311.github.io/RDesk/
A Note on What Comes Next
RDesk v1.0 is Windows-only. This is an honest architectural constraint — WebView2 is a Microsoft technology and the native launcher uses Win32 APIs.
Cross-platform support is the primary focus of v2.0. The webview library vendored in RDesk already supports macOS (WKWebView) and Linux (WebKitGTK) at the abstraction level.
For developers who need cross-platform today, Shiny remains the right answer. For developers who need native Windows distribution with zero ports and a pure R developer experience, RDesk is the first tool that delivers it.
The long road to native R desktop apps has reached its first real destination. The next leg of the journey starts from here.
RDesk is MIT licensed and actively maintained.
GitHub: https://github.com/Janakiraman-311/RDesk
CRAN: https://cran.r-project.org/package=RDesk
Docs: https://janakiraman-311.github.io/RDesk/