Skip to content

Publish an app

The App Store is driven by one file: registry.json at the repo root. It lists every app, and the @react-ui-os/cli bundles their source from it. Adding an app is two steps: write the app, then add a manifest entry.

An app is one App object plus whatever component tree it needs. Keep each app in its own folder so its files copy together and the relative imports survive the move.

packages/example-apps/src/weather/index.tsx
"use client";
import type { App } from "@react-ui-os/core";
import { useTheme } from "@react-ui-os/desktop";
function WeatherContent() {
const theme = useTheme();
return <div style={{ color: theme.palette.textPrimary, padding: 8 }}>Sunny, 21°</div>;
}
export const weatherApp: App = {
id: "weather",
name: "Weather",
tagline: "Today at a glance",
accent: "#38bdf8",
defaultBounds: { w: 360, h: 420 },
content: WeatherContent,
};

Read colors, radii, and motion from useTheme() rather than hardcoding them, guard any window/document access for SSR, and persist state through the storage adapter instead of localStorage directly. The apps under packages/example-apps/src are working references for the two-pane layout, persistence, and keyboard handling.

Append an entry to the apps array in registry.json:

registry.json
{
"id": "weather",
"name": "Weather",
"description": "Today at a glance.",
"category": "utilities",
"accent": "#38bdf8",
"export": "weatherApp",
"dependencies": ["@react-ui-os/core", "@react-ui-os/desktop"],
"dir": "packages/example-apps/src/weather",
"files": ["index.tsx"]
}
FieldMeaning
idInstall id and the folder name the CLI writes to
exportThe named export the consumer registers with <Desktop>
categoryOne word, used by the gallery filter
accentHex color for the gallery card
dependenciesnpm packages the app imports (always core and desktop)
dirSource folder, relative to the repo root
filesThe files in dir to copy, by name

The CLI copies these files into the consumer’s os-apps/<id>/ folder, so the ./icon and ./store relative imports keep working after the copy.

The CLI installs from any registry, not just the built-in one. Point --registry at your local registry.json; it reads the app sources from each entry’s dir, so there is no build step for local testing:

Terminal window
npx @react-ui-os/cli add weather --registry ./registry.json --dir /tmp/try

You do not need to add your app to this repository. Build your registry into a single self-contained file (every app’s source inlined) and host it anywhere that serves files over HTTP: GitHub Pages, a CDN, an object store.

Terminal window
npx @react-ui-os/cli build registry.json --out public/registry.json

Publish public/registry.json, then anyone installs from it:

Terminal window
npx @react-ui-os/cli add weather --registry https://you.example/registry.json
npx @react-ui-os/cli list --registry https://you.example/registry.json

A remote registry must be the built (inlined) form, because the CLI cannot read your repository’s files over HTTP. The build command produces it.

The built-in registry is curated and intentionally small. If an app is broadly useful and matches the design discipline in DESIGN.md, open a pull request that adds its source and the registry.json entry. A test guards the manifest against drift (every listed file must exist, and every file in the app folder must be listed); CI runs it and the build on every PR.

Terminal window
pnpm --filter @react-ui-os/example-apps test # the drift guard