TrademarkTrademark
Ctrl k
Search docs...
Composition

Desktop apps

Ship the same app to macOS, Windows and Linux with the desktop target.
Every composition is a website. The desktop target wraps that website in macOS, Windows and Linux apps built with Electron: It combines with the mobile target: --targets mobile,desktop. The desktop app is a window onto the deployed site, not a second build of it. It loads --url, so:
  • pages, auth and API routes run on the server exactly as they do in a browser, with the same cookies and the same code;
  • every deploy reaches the app at once, with no new installer;
  • only desktop/ is packaged (a few kilobytes on top of Electron), and its offline.html is shown when the site cannot load.
--url is therefore required. --app-id sets the bundle id; it defaults to the URL's host reversed, so https://my-app.com becomes com.myapp.
  • desktop/main.mjs, the main process: one window, links to other sites open in the system browser, a second launch focuses the running app.
  • desktop/offline.html, the offline page, and desktop/package.json, the packaged app's own manifest.
  • electron-builder.yml with the bundle id, the app name and the installer formats: a DMG on macOS, an NSIS installer on Windows, an AppImage on Linux.
  • src/lib/desktop.ts: isDesktopApp(), for the few things that should differ inside the app (hiding a "download the app" link). It reads the user agent, so it also works on the server with the request's user-agent header.
  • electron and electron-builder, plus desktop and desktop:build scripts.
APP_URL only applies to unpacked runs; an installed app always loads --url. Each installer is built on its own OS, so the DMG needs a Mac. Put a 1024x1024 icon.png in desktop/resources/ to replace Electron's icon.
  • Links to other sites open in the system browser. That includes OAuth providers and Stripe Checkout. Email and password auth stays in the app, because it runs on the site's own routes.
  • Signing. Unsigned apps work, but macOS Gatekeeper and Windows SmartScreen warn on first launch. Signing and notarization are set in electron-builder.yml; see electron-builder's code signing guide.
  • Updates. The site updates itself, so the installer only changes when Electron or desktop/ does. There is no update feed to run.
Last updated October 17, 2026
Edit this page on GitHub