Project Overview
Set up a MōBrowser project from scratch and explore its structure and configuration.
To create a cross-platform desktop application, you need to create a MōBrowser project. It contains all the files and dependencies required to build and run your desktop application.
Creating a Project
Make sure your current working directory is the one where you intend to create a project. Run the following command in your command line:
npm create mobrowser-app@latest
This command will install and execute create-mobrowser-app, the official project scaffolding tool. It asks for:
- The project name.
- Whether to add a native module to access platform-specific APIs. The native module can be written in C++ or Rust.
- Whether to initialize a Git repository in the project directory.
✔ Project name: App
✔ Add a native module? No
✔ Initialize a Git repository? Yes
The tool then creates the project in a new directory, installs its dependencies, and runs code generation, so the project is ready to run with npm run dev.
Project template
Every project is created from the same template: a React and TypeScript app with shadcn/ui components. It’s a small working desktop app that you can build on:
- A “Hello, world!” main window with a hidden title bar.
- An application menu with File, Edit, View, and Window menus (Help on Windows).
- A Settings window with a theme picker, launch at login, automatic update checks, and an About page.
- Links that open external URLs in the default browser.
- The shadcn app kit: Base UI components and ready-made patterns such as pages, a router, a search field, faceted filters, a data table, a confirmation dialog, and a save bar.
- End-to-end tests for the main window and the Settings window, and GitHub Actions workflows that run them and release the app.
The project is ready for development with an AI coding agent. AGENTS.md, mirrored by CLAUDE.md, explains to the agent how to run, inspect, and test the app. The .agents/skills/ directory contains skills with rules the agent follows:
| Skill | Description |
|---|---|
shadcn | Builds the renderer UI from the app kit components. Includes the component docs, so the agent doesn’t rely on its training data. |
desktop-shell | Shapes the native side of the app in the main process: the window, the application menu, the tray, the Dock, and the app lifecycle. Makes the agent decide them from the purpose of the app instead of keeping the defaults. |
mobrowser-testing | Makes the agent keep the checks it makes with the automation commands as end-to-end tests. |
Git repository
When you run the tool outside a Git repository, it asks whether to initialize a Git repository in the new project directory, and runs git init by default. The question is skipped if Git isn’t installed. If git init fails, the tool warns you and keeps the project.
You can also create a repository first, for example on GitHub, clone it, and create the project inside the clone:
gh repo create my-app --private --clone --add-readme
cd my-app
npm create mobrowser-app@latest
When the current directory is the root of a Git repository that has only a README, the tool asks where to create the project:
? This directory is a Git repository root. Generate the project here? (Use arrow keys)
❯ Yes
No, in a subdirectory
With Yes, the project is created at the repository root, next to the README, and the project name defaults to the name of the directory. The generated GitHub Actions workflows are then at the root of the repository, where GitHub runs them. If the repository has other files, the project is created in a subdirectory without asking.
Opening the project in a coding agent or IDE
When the project is ready, the tool lists the coding agents and IDEs installed on your machine and offers to open the project in one of them:
? Open it in a coding agent/IDE: (Use arrow keys)
❯ ChatGPT Desktop (Codex)
Claude Code
Claude Desktop
Codex
Cursor
VS Code
No
Only the tools found on your machine are listed:
| Tool | What happens |
|---|---|
| Cursor, VS Code | Opens the project directory in the IDE. |
| Claude Desktop | Starts a new Claude Code session in the project directory. |
| ChatGPT Desktop (Codex) | Starts a new Codex thread in the project directory. |
| Claude Code, Codex | Prints the commands that start the agent in the project directory. The agent is not listed when you run the tool inside it. |
For example, for Claude Code:
To open the project in Claude Code, run:
cd App
claude
Choose No to see how to run the app instead. The tool doesn’t ask this question when no coding agent or IDE is found, or when it runs without an interactive terminal.
Creating a project without prompts
To create a project without prompts, for example in a script, pass the options on the command line:
npm create mobrowser-app@latest -- --name App --native rust --git no
| Option | Description |
|---|---|
--name <name> | The project name. Required. |
--native <no|cpp|rust> | Adds a native C++ or Rust module. The default is no. |
--setup <yes|no> | Installs dependencies and runs code generation. The default is yes. |
--git <yes|no> | Initializes a Git repository. The default is yes, or no inside an existing repository. |
--dir . | Creates the project in the current directory, which must be empty or the root of a Git repository with only a README. |
--help, -h | Shows the available options. |
Without prompts, the tool doesn’t offer to open the project in a coding agent or IDE.
Project structure
If you add a native C++ module, you will see the following files and folders in the generated project:
.agents/
├── skills/
.github/
├── workflows/
.vscode/
assets/
resources/
src/
├── main/
├── native/
├── renderer/
test/
├── e2e/
.gitignore
AGENTS.md
CLAUDE.md
CMakeLists.txt
components.json
mobrowser.conf.json
package.json
playwright.config.ts
postcss.config.js
tsconfig.json
tsconfig.node.json
vite.config.tsHere is the description of the files and folders in the project structure:
| File/Folder | Description |
|---|---|
.agents/skills/ | Skills for coding agents. |
.github/workflows/ | GitHub Actions workflows that test and release the app. |
.vscode/ | Visual Studio Code launch and task configuration. |
assets/ | Application assets such as icons, images, and other resources. |
resources/ | Application resources that shouldn’t be packed into app.bin. |
src/main/ | Main process source code. |
src/renderer/ | Renderer process source code. |
src/native/ | Native C++ module source code. |
test/e2e/ | End-to-end tests. |
.gitignore | Files and folders Git ignores, such as node_modules/ and build/. |
AGENTS.md | Instructions for coding agents. |
CLAUDE.md | Instructions for Claude Code. It refers to AGENTS.md. |
CMakeLists.txt | CMake build configuration of the native C++ module. |
components.json | shadcn/ui configuration file. |
mobrowser.conf.json | Application configuration file. |
package.json | npm package configuration file with the project dependencies and scripts. |
playwright.config.ts | Playwright configuration file for the end-to-end tests. |
postcss.config.js | PostCSS configuration file for Tailwind CSS. |
tsconfig.json | TypeScript configuration file. |
tsconfig.node.json | TypeScript configuration file for Node.js. |
vite.config.ts | Vite configuration file. |
A project without a native module has no src/native/ and CMakeLists.txt.
A project with a native Rust module has Cargo.toml, Cargo.lock, build.rs, and rust-toolchain.toml instead of CMakeLists.txt. See Native Rust Module.
Project configuration
The mobrowser.conf.json file is the main project and application configuration file. It contains the application name, version, author, copyright, description, and other properties.
Here’s an example of the mobrowser.conf.json file:
{
"app": {
"name": "Stats",
"version": {
"major": "1",
"minor": "0",
"patch": "0"
},
"author": "TeamDev",
"copyright": "Copyright © 2026 TeamDev",
"description": "System resources monitor and process explorer.",
"locales": ["en-US"],
"trustedOrigins": [],
"schemes": [],
"updateServerUrl": "",
"bundle": {
"macOS": {
"icon": "assets/app.icns",
"executableName": "Stats",
"bundleID": "com.teamdev.Stats",
"codesignIdentity": "${MACOS_CODESIGN_IDENTITY}",
"codesignKeychain": "${KEYCHAIN_PATH}",
"codesignEntitlements": "assets/entitlements.plist",
"teamID": "${MACOS_TEAM_ID}",
"appleID": "${MACOS_APPLE_ID}",
"password": "${MACOS_APPLE_PASSWORD}",
"extraInfo": "",
"installer": {
"dmg": {
"name": "",
"volumeName": "",
"volumeIcon": "assets/app.icns",
"eula": "",
"deltaStrategy": "BestSize",
"window": {
"textSize": 14,
"skipPrettifying": false,
"backgroundImage": "",
"position": {
"x": 500,
"y": 400
},
"size": {
"width": 600,
"height": 400
},
"icon": {
"size": 150,
"position": {
"x": 160,
"y": 160
}
},
"appDropLink": {
"x": 430,
"y": 160
}
}
}
}
},
"Windows": {
"icon": "assets/app.ico",
"executableName": "Stats",
"signCommand": "",
"installer": {
"exe": {
"name": "",
"icon": "assets/app.ico",
"installationGif": "",
"packageId": "Stats",
"installationFolderName": "Stats",
"displayName": "Stats",
"shortcuts": "Desktop,StartMenuRoot",
"deltaStrategy": "BestSpeed"
}
}
},
"Linux": {
"icon": "assets/app.png",
"executableName": "Stats"
}
}
}
}
Here’s the description of the properties in the mobrowser.conf.json file. Paths may be absolute or relative to the project root. You can reference environment variables in string values using the ${ENV_NAME} syntax, which is especially useful for signing credentials.
Application properties
| Property | Description |
|---|---|
app.name | The user-visible application name. It appears in places such as window titles, the About dialog, and the system menu. It is also used for executable and installer names unless you set a platform-specific executableName. The display name may contain spaces and non-ASCII characters. |
app.version | The application version, represented by the major, minor, and patch string properties. MōBrowser combines them into a version such as 1.0.0; this value is available at runtime as app.version and is used in packaged artifact names and update metadata. |
app.version.major | The major version component. Increment it for incompatible or otherwise major releases; for example, "2" in version 2.0.0. |
app.version.minor | The minor version component. Increment it for backward-compatible feature releases; for example, "3" in version 2.3.0. |
app.version.patch | The patch version component. Increment it for backward-compatible fixes; for example, "4" in version 2.3.4. |
app.author | The person, team, or organization that publishes the application, for example, "TeamDev". This value is included in the packaged application’s metadata and is displayed in Windows file properties. |
app.copyright | The application’s copyright notice, for example, "Copyright © 2026 TeamDev". This value is included in package metadata, is displayed in Windows file properties, and is available at runtime as app.copyright. |
app.description | A short, user-facing summary of the application’s purpose. This value is included in package metadata, is displayed in Windows file properties, and is available at runtime as app.description. |
app.locales | An array of locale identifiers to include in the application, for example, ["en-US", "de", "fr"]. Limiting this list reduces the bundle size. Use ["all"] to include every supported locale. If this property is omitted or no configured locale matches the system locale, the application uses en-US. See Localization for the supported identifiers. |
app.trustedOrigins | An array of remote origins that receive privileges normally reserved for the bundled application origin: access to the renderer-to-main IPC bridge, automatic grants for framework permission requests, and permission to be used as top-level in-app navigation targets. Entries may be exact origins ("https://app.example.com", "http://localhost:8000"), hostname patterns ("*.example.com"), or scheme-qualified hostname patterns ("https://*.example.com"). URL paths and broad public-suffix patterns such as "*.com" are not allowed. Keep this list as narrow as possible because trusted content can invoke exposed main-process services and receive sensitive permissions. |
app.schemes | An array of custom URL scheme names, without ://, for example, ["stats"] for stats://open. The packaged application is registered as a handler for these schemes so links can launch or activate it. Handle incoming URLs with the openUrl app handler; on Windows and Linux, also inspect the url command-line switch when a link launches an app that was not already running. See Schemes. |
app.updateServerUrl | The HTTPS URL of the server or cloud bucket that hosts application updates. When set, a packaged macOS application adds the built-in Check for Updates… menu item, and a Windows window menu can expose the same flow through the checkForUpdates role. Native dialogs report progress on both platforms. Leave it empty to implement a custom flow with the auto-update API. See Built-in update flow. |
app.bundle | Platform-specific packaging, branding, signing, and installer settings. Configure the macOS, Windows, and Linux objects independently. |
app.bundle.macOS properties
| Property | Description |
|---|---|
icon | The path to the application icon in .icns format. |
executableName | The file-system name used for the executable and the .app bundle. It may contain only ASCII letters, digits, spaces, hyphens, and underscores. Do not include a file extension. If omitted, app.name is used. See Executable name. |
extras | An array of files and directories to copy into the packaged application’s Contents directory. An entry may be a path, a directory path ending in / to copy its contents, or a path with a wildcard in its final file-name segment, such as "schemas/*.json". For a custom destination, use an object such as { "from": "native/lib.dylib", "to": "Frameworks/lib.dylib", "sign": true }; from is the source path, to is relative to Contents, and the optional sign Boolean requests code signing for copied binaries. See Third-party resources. |
bundleID | The unique application bundle identifier in reverse-DNS format, for example, "com.teamdev.Stats". |
codesignIdentity | The name of the code-signing certificate installed in Keychain Access, such as a Developer ID Application identity. Leave it empty only when producing an unsigned development build. |
codesignKeychain | The path to the keychain containing the code-signing identity. Set it when the identity is stored outside the default keychain, which is common on CI build agents. |
codesignEntitlements | The path to the .plist file containing the entitlements applied during code signing. |
provisioningProfile | The path to an Apple-issued .provisionprofile file to embed in the application bundle. It is required when the entitlements include keychain-access-groups, for example, for the Touch ID WebAuthn authenticator. |
teamID | The Team ID of the Apple Developer account used to sign and notarize the application. |
appleID | The Apple Developer account email used for notarization. |
password | The app-specific password for the Apple ID used for notarization. Store it in an environment variable instead of committing it to the configuration file. |
extraInfo | The path to a valid .plist file whose keys are merged into the generated application Info.plist. Values in this file override generated values with the same keys. Use it for settings such as privacy usage descriptions or LSUIElement. |
installer.dmg | Settings for the macOS DMG installer. |
installer.dmg.name | The DMG file name. Leave it empty to generate a name from the application name, version, and architecture, such as Stats-1.0.0-arm64.dmg. |
installer.dmg.volumeName | The mounted volume name shown in the Finder sidebar. Leave it empty to derive it from app.name. |
installer.dmg.volumeIcon | The path to the mounted volume’s .icns icon. Leave it empty to use the standard macOS volume icon. |
installer.dmg.eula | The path to an End User License Agreement text file. When set, users must accept or decline the agreement when opening the DMG; leave it empty to omit this dialog. |
installer.dmg.deltaStrategy | The delta-package generation strategy: "None" disables delta packages, "BestSpeed" optimizes their generation speed, and "BestSize" minimizes their size. |
installer.dmg.window.textSize | The text size used in the Finder window for the mounted DMG. |
installer.dmg.window.skipPrettifying | A Boolean. Set it to true to skip Finder window customization, for example, on a CI agent without a logged-in macOS user. |
installer.dmg.window.backgroundImage | The path to the DMG window background image. Leave it empty for no background image. |
installer.dmg.window.position | The initial x and y coordinates of the DMG window on the main screen. |
installer.dmg.window.size | The width and height of the DMG window in points. |
installer.dmg.window.icon.size | The application icon size in the DMG window. |
installer.dmg.window.icon.position | The x and y coordinates of the application icon in the DMG window. |
installer.dmg.window.appDropLink | The x and y coordinates of the Applications folder drop link in the DMG window. |
app.bundle.Windows properties
| Property | Description |
|---|---|
icon | The path to the application icon in .ico format. |
executableName | The file-system name used for the application executable and other platform artifacts. It may contain only ASCII letters, digits, spaces, hyphens, and underscores. Do not include the .exe extension. If omitted, app.name is used. See Executable name. |
extras | An array of files and directories to copy into the packaged application. An entry may be a path, a directory path ending in / to copy its contents, or a path with a wildcard in its final file-name segment, such as "schemas/*.json". For a custom destination, use an object such as { "from": "native/lib.dll", "to": "lib/lib.dll", "sign": true }; from is the source path, to is relative to the packaged application, and the optional sign Boolean requests code signing for copied binaries. See Third-party resources. |
signCommand | The command used to sign application binaries and the installer. Include @@BINARY_PATH@@ where the absolute path of each binary should be inserted, for example, "signtool sign /a @@BINARY_PATH@@". MōBrowser runs the command once for every binary that requires signing. |
installer.exe | Settings for the Windows executable installer. |
installer.exe.name | The installer file name. Leave it empty to generate a name from the application name and version, such as Stats-1.0.0.exe. |
installer.exe.icon | The path to the installer’s .ico icon. Leave it empty to use the system default icon. |
installer.exe.installationGif | The path to the GIF displayed while installation is in progress. Leave it empty to show the default system progress dialog. |
installer.exe.packageId | The NuGet package identifier under which the application is stored on the system. If omitted, MōBrowser uses app.name without spaces. It is also the default installation folder name. |
installer.exe.installationFolderName | The folder under %LOCALAPPDATA% in which the application is installed. It may contain spaces and other file-system-safe characters. If omitted, packageId is used. |
installer.exe.displayName | The application name shown by Windows and used for shortcuts. If omitted, app.name is used. |
installer.exe.shortcuts | A comma-separated list of shortcut locations. Supported values are None, StartMenu, Desktop, Startup, and StartMenuRoot; for example, "Desktop,StartMenuRoot". If omitted, Desktop,StartMenuRoot is used. |
installer.exe.deltaStrategy | The delta-package generation strategy: "None" disables delta packages, "BestSpeed" optimizes their generation speed, and "BestSize" minimizes their size. |
installer.msix | Settings for the Microsoft Store (MSIX) package. See Publishing to Microsoft Store. |
app.bundle.Linux properties
| Property | Description |
|---|---|
icon | The path to the application icon in PNG format. A 128×128 image is sufficient for most Linux distributions. |
executableName | The file-system name used for the application executable and other platform artifacts. It may contain only ASCII letters, digits, spaces, hyphens, and underscores. Do not include a file extension. If omitted, app.name is used. See Executable name. |
extras | An array of files and directories to copy into the packaged application. An entry may be a path, a directory path ending in / to copy its contents, or a path with a wildcard in its final file-name segment, such as "schemas/*.json". For a custom destination, use an object such as { "from": "native/lib.so", "to": "lib/lib.so" }; from is the source path and to is relative to the packaged application. See Third-party resources. |
vite.config.ts
The framework uses Vite as the build tool for both the main and the renderer processes.
Each MōBrowser project includes a vite.config.ts file. It defines the build configuration for the main and the renderer processes.