Installation
Requirements
Section titled “Requirements”- React 19+
- A bundler that respects
package.jsonexports. Vite, Astro, Next.js (App Router), Remix, and TanStack Start all qualify. Webpack 4 does not. Use Webpack 5+. - TypeScript is optional but recommended. The types are bundled.
Install
Section titled “Install”pnpm add @react-ui-os/core @react-ui-os/desktop @react-ui-os/theme-macosnpm install @react-ui-os/core @react-ui-os/desktop @react-ui-os/theme-macosbun add @react-ui-os/core @react-ui-os/desktop @react-ui-os/theme-macosOptional: pick a branded theme.
pnpm add @react-ui-os/theme-windowsnpm install @react-ui-os/theme-windowsYour first desktop
Section titled “Your first desktop”import type { App } from "@react-ui-os/core";import { Desktop } from "@react-ui-os/desktop";import { macosTheme } from "@react-ui-os/theme-macos";
const apps: App[] = [ { id: "notes", name: "Notes", accent: "#f59e0b", content: () => ( <article> <h1>Hello, desktop.</h1> <p>Drag this window by the title bar. Try Cmd-K.</p> </article> ), },];
export default function App() { return <Desktop apps={apps} theme={macosTheme} />;}That single component renders the full desktop. No other styles to import; the library injects its own keyframes at the root.
The library mounts client-side. In Next.js App Router, mark the consuming module with "use client":
"use client";
import { Desktop } from "@react-ui-os/desktop";// ...In Astro, mount the desktop as a React island with client:load:
---import Desktop from "../components/Desktop";---<Desktop client:load />Server-side, the desktop renders nothing (every JSX surface that touches window returns null), so hydration is safe.
Troubleshooting
Section titled “Troubleshooting”The dock appears but windows don’t drag.
Your bundler may be stripping "use client" directives. Confirm React 19 and that your bundler honors the package.json "exports" field.
Styles leak from a parent layout.
The library uses position: fixed; inset: 0 on the desktop root. Make sure no parent forces overflow: hidden on the entire viewport before the desktop mounts.
Custom event collisions.
The library dispatches events on the namespace react-ui-os:*. If your app uses the same prefix, override the storage adapter prefix:
import { createLocalStorageAdapter } from "@react-ui-os/core";
const storage = createLocalStorageAdapter("acme-os");<Desktop apps={apps} theme={theme} storage={storage} />;