Configuration

Desktop and mobile app

A Récit site becomes a native application with one app: section and one command, recit app build. The application embeds the site exactly as recit-site build produced it (dist/) in a shared Tauri 2 shell, and serves it offline. Nothing is added to the site: the pages do not know they run in a window.

Work in progress

The app: section is in @recit/core; @recit/app and the recit app commands are being built (desktop first, then Android and iOS). This page is their reference.

Install

TerminalBash
pnpm add -D @recit/app

@recit/app carries the shell and @tauri-apps/cli; recit loads it on demand, as it loads @recit/motion-render for recit render. Without it, recit app stops with "install @recit/app". The CLI comes from @recit/cli, as usual.

The app: section

A Récit section of recit.config, next to publish. Only identifier has no safe default when the site has no canonicalUrlOrigin.

TypeScriptrecit.config.ts
import "@recit/site-stories/styles.css"; import { defineConfig } from "recit/config"; export default defineConfig({ // Site options site: { title: "Lorem Ipsum Docs" }, canonicalUrlOrigin: "https://docs.example.com", navigation: [{ type: "doc", file: "index" }], // Récit sections publish: { dir: "./stories", routePrefix: "/stories" }, app: { productName: "Lorem Ipsum Docs", identifier: "com.example.loremipsum", window: { width: 1280, height: 800, minWidth: 720 }, }, });
KeyDefaultMeaning
productNamesite.title, else the name of the site foldername of the application and of its window
identifiercanonicalUrlOrigin reversed: https://docs.example.com → com.example.docsreversed domain, at least two segments of letters, digits and underscores (no hyphen: Android rule). Final once published: the stores and the updater key on it
versionversion of the site's package.json, else 0.0.0 with a warningthe version the system shows
iconmetadata.favicon of the sitesquare source, PNG of 1024 px or more or SVG, relative to the site; every platform size is derived from it
window.width, window.height1280, 800size of the window at first launch
window.minWidth, window.minHeightnoneminimum size
window.resizabletrue—
allowedOrigins[]origins the window may load besides the bundle; every other link opens in the system browser
deepLinknoneURL scheme that opens the application (lorem-docs://…)
android.minSdkVersion24 (Android 7.0)see Android
ios.developmentTeamnoneApple team of the signature; see iOS
updater.endpoints, updater.pubkeynoneautomatic updates on desktop, opt-in; see Updates

Names

The window shows productName exactly as written. The files on disk follow two rules of the systems:

  • the Linux packages (.deb, .rpm) are named after the ASCII form of productName, because Debian and RPM refuse a package name outside ASCII: Récit Docs gives recit-docs, and the diagnostic app-product-name-ascii says so. A name with nothing left in ASCII takes the name below;
  • the binary is named after the identifier without its first segment, in lowercase with hyphens (mainBinaryName of Tauri): com.example.loremipsum gives example-loremipsum, so two applications installed on the same machine never share /usr/bin/recit-app.

Diagnostics

recit app resolves the section before anything else and prints the problems like recit build. An error stops the build; a warning does not.

CodeSeverityWhen
app-missingerrorno app: section; the message gives the minimal section to add, with the computed defaults
app-identifier-missingerrorneither app.identifier nor canonicalUrlOrigin
app-identifier-invaliderroran identifier that breaks the rule above (a hyphen, one segment, a digit first)
app-base-patherrorthe site sets basePath: the application serves the site at its root, the router and the search index expect the prefix
app-cdn-urlerrorthe site sets cdnUrl: the assets would come from the network
app-version-missingwarningno app.version and no version in the site's package.json; 0.0.0 is used (Android refuses it)
app-product-name-asciiwarningproductName has characters outside ASCII: the packages take its ASCII form, the binary the identifier (see Names)
app-search-remotewarningsearch.type: "inkeep": that search calls a remote service; Pagefind works offline
config-invaliderrorthe recit.config itself is invalid, one diagnostic per problem

Commands

CommandOptionsEffect
recit app build [dir]--target desktop|android|ios, --skip-site-buildrecit-site build in the site, then the native build; the installers are copied to .recit/app/bundle/
recit app dev [dir]—reuses a recit-site dev already listening on the site's port, otherwise starts one, then opens the window on it, with HMR
recit app preview [dir]--port <port>serves dist/ in the browser exactly as the application resolves it (deep reload, 404 fallback), without Rust
recit app android <init|dev|build> [-- …]arguments after -- go to tauri android …see Android
recit app ios <init|dev|build> [-- …]arguments after -- go to tauri ios …see iOS
recit app icon [source]—regenerates the icons of every platform from app.icon, or from the given file

dir defaults to the current folder. Everything generated lives in <site>/.recit/app/: the shell, its tauri.conf.json (generated at every call, never edited by hand), the icons, the Cargo target/ and, after a build, the installers in bundle/. The folder is ignored by git. RECIT_APP_TARGET_DIR moves the Cargo cache out of the site, to share it between the sites of a monorepo: the second site does not compile Tauri again, and its installers are still copied to its own .recit/app/bundle/ (Tauri writes them under the shared target directory, where the next build of another site overwrites them).

A site of a pnpm workspace adds the command to its package.json:

JSONpackage.json
{ "scripts": { "build": "recit-site build", "app:build": "recit app build" } }

Prerequisites (desktop)

Rust stable, installed at user level with rustup, and the libraries of the system's webview:

  • Linux (Debian, Ubuntu): sudo apt install libwebkit2gtk-4.1-dev libgtk-3-dev libayatana-appindicator3-dev librsvg2-dev libssl-dev libxdo-dev pkg-config file curl wget. A machine without a display links the application all the same; to start it there (a smoke test), add xvfb, xauth and dbus, and run it under xvfb-run -a dbus-run-session -- <binary>: without a session bus, GTK waits 25 seconds before the window appears.
  • macOS: Xcode Command Line Tools (xcode-select --install).
  • Windows: Visual Studio Build Tools with the C++ workload; WebView2 is part of Windows 10 and 11.

The first build compiles the shell and its dependencies, a few minutes; the next ones reuse the Cargo cache.

What works offline

The bundle of recit-site build has everything the site needs, so the application does too:

  • the pages, one HTML file per route, and the client router after the first one;
  • the Pagefind search, index and WebAssembly included;
  • the fonts, the code highlighting, the math and the diagrams, all in the bundle;
  • the stories the site publishes: decks and films play in the window.

A reload on a deep page finds its HTML file; an unknown address falls back on index.html, so the site's 404 page shows. recit app preview reproduces that resolution in the browser, to check a site before installing Rust.

Sizes

Measured on the example site examples/site-embed (a few pages, one deck, Pagefind), built on Linux x86_64 with Tauri 2.12:

ArtefactSizeNote
binary25 MBthe shell and the whole dist/, embedded
.deb, .rpm13.4 MiBcompressed; the system provides WebKitGTK
.AppImage105 MiBcarries WebKitGTK and GTK, so it runs on any distribution
Android .apk21.7 MBuniversal, signed; see Android
Android .aab15.2 MBthe store builds a smaller .apk per device from it

A site grows these numbers by the size of its dist/: pages, images, fonts and the search index.

Limits

  • basePath and cdnUrl are refused (see the diagnostics): the application serves the site at its root, from its own files.
  • No server in the application: no SSR, no API. A site built with --experimental-ssr is a hosting matter.
  • Links to other origins open in the system browser, the window stays on the site. To load another origin inside the window, list it in allowedOrigins.
  • The smartphone remote of a deck needs the development server: there is none in the application. The presenter console opens in a second window.
  • recit render (MP4) stays a Node and Chromium pipeline, outside the application.

Updates

On desktop, updates are opt-in: the updater section names the endpoints that serve latest.json and the public key that signs the binaries (tauri signer generate; the private key stays a secret of the release workflow). Without the section, the shell ships without the updater. On Android and iOS, the stores update the application; updater is ignored there.