Build native iOS, macOS, and Android shells around your Vite app.
Nativite gives you:
- Native project generation for Apple and Android targets
- A typed JS bridge for calling native code from web code
- A declarative native chrome API (
title bar,navigation,toolbars, sheets, drawers, etc.) - Live device CSS variables (
--nv-*) for safe areas, keyboard, appearance, and more
Status: Nativite 1.0 defines a stable public configuration, package export, JavaScript bridge, and native chrome contract. Future releases may add capabilities, but documented 1.0 APIs are treated as supported public surface.
The nativite/chrome JavaScript API is the app-facing interface for native chrome. Native shells receive compiled Native Chrome Layout Protocol v2 (chrome.snapshot) messages; NCLP v2 is the stable host wire protocol for Nativite 1.0 and is documented in NCLP.md.
The complete 1.0 stable surface, experimental surface, and semver policy are documented in Public API Contract.
Nativite is for teams that already have a Vite web app and want:
- shared product logic across web + native
- gradual native capability adoption
- native UI controls without rewriting the app in Swift/Kotlin
bun add nativitePeer dependencies:
typescript ^5vite >=5(required if you usenativite/vite)
- macOS, Xcode, Xcode command line tools, and available simulators/devices (for iOS/macOS)
- Android Studio, Android SDK, a JDK, and a
gradlecommand onPATH(for Android) - Bun 1.x or Node 22+
Nativite does not install, download, vendor, or bootstrap native toolchain dependencies. Xcode, Xcode command line tools, simulators, Android Studio, the Android SDK, Java, and Gradle are machine prerequisites that should be installed and managed by the developer or their CI image. Nativite generates native project files and prints clear errors when required tooling is unavailable.
For store uploads, use the native platform requirements current for your
submission date. As of April 28, 2026, Apple requires apps uploaded to App Store
Connect to be built with Xcode 26 or later and an iOS/iPadOS 26 SDK or later.
For Google Play, Nativite's Android default targetSdk is 36; Google Play
requires new apps and updates to target an API level within one year of the
latest major Android release, with policy deadlines published by Google Play.
This is the shortest drop-in path for an existing Vite app.
bun add nativiteRun the init command from your Vite project root:
bunx nativite initThis creates nativite.config.ts using your package.json name and updates a
simple vite.config.ts to include nativite(). If your Vite config cannot be
edited safely, the command prints the exact manual import and plugin changes to
make.
By default, init keeps the first path narrow: it enables ios on macOS and
android on other hosts. Choose targets explicitly by repeating --platform:
bunx nativite init --platform ios --platform androidbunx nativite buildThis runs a production Vite build for each configured target, writes the web
bundle, and creates or updates the matching native project under .nativite/.
Use the native IDE or simulator workflow you already use for the target platform:
open .nativite/ios/MyApp.xcodeproj
open .nativite/androidXcode and Android Studio own native build settings, signing, simulator/device selection, and launch. Nativite owns generating the native project code and the web bundles those projects embed.
Run your normal Vite dev server when you want the generated debug native project to load web code from Vite instead of the embedded production bundle.
bunx vite devNativite's Vite plugin writes .nativite/dev.json with the resolved dev server
URL so native debug builds can discover it.
- Prefer history fallback routes that work from
index.html; generated native shells load the same app entry as Vite. - Use platform-specific files such as
Button.ios.tsx,Button.android.tsx,Button.mobile.tsx, orButton.native.tsxwhen a route or component needs native-only behavior. - Add
/// <reference types="nativite/globals" />tovite-env.d.tsbefore using compile-time globals such as__PLATFORM__or__IS_NATIVE__. - Keep framework routing, state, and data loading in the web app. Reach for
nativite/chromeor native plugins only when the native shell needs to own a native control or capability.
No platforms are configured.Add at least one entry inplatforms, or rerun init with--platform ios,--platform macos, or--platform android.- iOS/macOS launch issues usually mean Xcode command line tools or simulators are missing or unavailable.
- Android generation requires Android Studio, Android SDK, Java, and a
gradlecommand onPATH. - If init cannot edit your Vite config safely, apply the import and plugin changes it prints.
- If
nativite.config.tsdoes not load under Node, ensureviteis installed in the app project. Node CLI config loading uses Vite's TypeScript config loader.
For a fuller walkthrough, see the drop-in quickstart. Use the advanced chrome and plugin APIs after the generated native shell is running and you need native-owned UI or custom native capabilities.
If you prefer to configure the project by hand, create the files below.
import { defineConfig, ios } from "nativite";
export default defineConfig({
app: {
name: "MyApp",
bundleId: "com.example.myapp",
version: "1.0.0",
buildNumber: 1,
},
platforms: [ios()],
});// vite.config.ts
import { defineConfig } from "vite";
import { nativite } from "nativite/vite";
export default defineConfig({
plugins: [nativite()],
});bunx nativite buildThis prepares each configured native target for a production build. It runs a production Vite build, writes the matching web bundle, and creates or updates the native project that embeds that bundle:
.nativite/ios
.nativite/macos
.nativite/android
dist-ios
dist-macos
dist-android
Optional single-platform build:
bunx nativite build --platform iosFor app-store submission, archive or package the generated project with the
native toolchain. nativite build does not create final signed artifacts such as
.ipa, .aab, .apk, signed .app, or .dmg; those are produced by Xcode,
Android Studio, Gradle, xcodebuild, or CI using your signing configuration.
app.nameapp.bundleIdapp.versionapp.buildNumberplatforms(at least one)
ios({ minimumVersion?, errorOverlay?, overrides? })macos({ minimumVersion?, overrides? })android({ minSdk?, targetSdk?, overrides? })
Notes:
ios(),macos(), andandroid()can be called with no arguments. Defaults are iOS17.0, macOS14.0, AndroidminSdk26, and AndroidtargetSdk36.ios.errorOverlaycontrols the default Vite HMR overlay behaviour in dev.NATIVITE_DEV_ERROR_OVERLAYstill takes precedence when set.android.targetSdkmust be an integer when provided.
signingupdatespluginsplatformPluginsdefaultChromeiconsplash
updates currently enables OTA bundle checks and staged bundle application for
iOS and macOS generated runtimes. Android exposes the same JavaScript
ota.check() status shape, but it always reports { available: false } until
Android OTA staging/apply support is implemented.
Low-level transport and event subscription:
import { bridge, ota } from "nativite/client";
if (bridge.isNative) {
const cameraResult = await bridge.call("camera", "capture", { quality: 0.9 });
console.log(cameraResult);
}
const unsub = bridge.subscribe("location:update", (payload) => {
console.log(payload);
});
const update = await ota.check();
console.log(update.available, update.version);
unsub();ota.check() is implemented by the Apple runtimes today. On Android it is a
compatibility placeholder that resolves to { available: false }.
Declarative native chrome state:
import { chrome, navigation, statusBar, titleBar, toolbar } from "nativite/chrome";
const cleanup = chrome(
titleBar({
title: "Inbox",
trailingItems: [{ id: "compose", label: "Compose", style: "primary" }],
}),
navigation({
items: [
{ id: "inbox", label: "Inbox", icon: "tray.fill" },
{ id: "search", label: "Search", icon: "magnifyingglass", role: "search" },
],
activeItem: "inbox",
}),
toolbar({
items: [{ id: "filter", label: "Filter" }],
}),
statusBar({ style: "auto" }),
);
const unsub = chrome.on("navigation.itemPressed", ({ id }) => {
console.log("Selected", id);
});
cleanup();
unsub();Also available on chrome:
chrome.messaging.postToParent/postToChild/broadcast/onMessagechrome.splash.preventAutoHide()chrome.splash.hide()
Typed helpers for --nv-* device variables:
import { NVVars } from "nativite/css";
const topInset = NVVars.getNumber("safe-top");
const isDark = NVVars.getBoolean("is-dark");
const stop = NVVars.observeNumber("keyboard-height", (height) => {
console.log("Keyboard height:", height);
});
stop();Platform-conditioned value selection:
import { platform } from "nativite/utils";
const backLabel = platform(
{
ios: "Back",
android: "Navigate up",
web: "Back",
},
"Back",
);Use definePlatformPlugin() + platform() to add non-first-party runtimes:
import { definePlatformPlugin, platform } from "nativite";
const electronPlatform = definePlatformPlugin({
name: "electron-platform",
platform: "electron",
native: true,
mobile: false,
desktop: true,
extensions: [".electron", ".desktop", ".native"],
environments: ["electron"],
async generate(ctx) {},
async build(ctx) {},
});
// in defineConfig(...)
// platforms: [platform("electron", { /* custom config */ })]
// platformPlugins: [electronPlatform]The plugin:
- creates per-platform environments (
ios,ipad,macos,android, plus plugin-defined) - resolves platform file variants (
.ios,.mobile,.native, etc.) - writes
manifest.jsonduring build hooks - handles native-request routing + HMR behavior for platform variants
Add this in vite-env.d.ts:
/// <reference types="vite/client" />
/// <reference types="nativite/globals" />Then use:
__PLATFORM____IS_NATIVE____IS_MOBILE____IS_DESKTOP____DEV__
__IS_NATIVE__/__IS_MOBILE__/__IS_DESKTOP__ come from your platform plugin traits
(native, mobile, desktop). Defaults are native: true, mobile: false,
desktop: false when omitted. A platform can set both mobile and desktop
to true.
nativite: config helpers, schema, core public typesnativite/vite:nativite()nativite/client:bridge,otanativite/chrome:chrome+ chrome factory functions + chrome typesnativite/css:NVVars,NVVarNamenativite/utils: platform utility helpersnativite/globals: ambient globals types
package.json#exports is the complete public module list. Deep imports are
unsupported, and the CLI is exposed as the nativite binary rather than as an
importable nativite/cli runtime module.
See Public API Contract for the semver
rules that apply to package exports, nativite.config.ts, generated native
projects, NCLP/native bridge payloads, first-party plugins, and CLI commands.
-
No platforms are configured.- Add at least one entry in
platforms.
- Add at least one entry in
-
iOS/macOS build/launch issues
- Ensure Xcode command line tools are installed and simulators exist.
-
Android project generation fails with
gradle: command not found- Install Gradle or expose Android Studio's Gradle-compatible tooling on
PATH, then rerunbunx nativite build --platform android.
- Install Gradle or expose Android Studio's Gradle-compatible tooling on
-
nativite.config.tsfails to load when invoking the CLI with Node- Ensure
viteis installed in the app project. Node CLI config loading uses Vite's TypeScript config loader so Node 22+ can run the same TypeScript config files as Bun.
- Ensure
Deep technical docs are in docs/README.md.
Suggested starting points:
- Drop-in quickstart
- Chrome API internals
- Client bridge internals
- Vite plugin internals
- Platform registry
- iOS overview docs
- Android overview docs
bun install
bun run build
bun run typecheck
bun run lint
bun run test
bun run test:native:ios
bun run test:native:androidSee CONTRIBUTING.md for contribution workflow and SECURITY.md for vulnerability reporting.
Release notes are tracked in CHANGELOG.md and generated from changesets.
MIT
