@wrnexus/mobile
SSR-safe compatibility access to Capacitor plugins.
Private registry access required
This package is not available from the public npm registry. After WorkRoot approves access and supplies private registry instructions, install the release-aligned package:
bun add @wrnexus/mobile@0.2.59Request preview access. Never put registry tokens in source control.
SSR-safe access to Capacitor plugins from WRNexusJS browser code.
Overview
@wrnexus/mobile keeps optional native imports out of server rendering while giving browser-owned modules one consistent registry for Capacitor plugins. During SSR, mobile.isNative() is false and mobile.platform() is "web".
Install a plugin through the WRNexusJS CLI so the web and native projects stay aligned:
wrnexus mobile add @capacitor/camera @capacitor/haptics
Usage
Register and invoke a Capacitor plugin
Import Capacitor packages only from browser-owned code, never from API routes or SSR helpers.
import { Camera, CameraResultType } from "@capacitor/camera";
import { mobile } from "@wrnexus/mobile";
mobile.registerPlugin("Camera", Camera);
export async function takePhoto() {
if (!mobile.isNative()) return null;
return mobile.invoke("Camera", "getPhoto", {
quality: 85,
resultType: CameraResultType.Uri,
});
}
Provide a browser fallback
whenNative runs the first callback only in a Capacitor WebView and can return a web/SSR-safe fallback everywhere else.
import { Haptics, ImpactStyle } from "@capacitor/haptics";
import { mobile } from "@wrnexus/mobile";
mobile.registerPlugin("Haptics", Haptics);
export const confirmAction = () =>
mobile.whenNative(
() => mobile.invoke("Haptics", "impact", { style: ImpactStyle.Medium }),
() => navigator.vibrate?.(30),
);
Read an optional plugin without throwing
import type { NetworkPlugin } from "@capacitor/network";
import { mobile } from "@wrnexus/mobile";
const network = mobile.plugin<NetworkPlugin>("Network");
const status = network ? await network.getStatus() : { connected: true, connectionType: "unknown" };
API
registerPlugin(name, instance)registers a browser-imported plugin.plugin(name)returns a plugin orundefined;requirePlugin(name)throws when absent.invoke(plugin, method, options?)calls a registered method and returns its result.whenNative(native, fallback?)selects native behavior without breaking SSR.isNative()andplatform()report the current Capacitor environment.
Unavailable required plugins throw MobileUnavailableError with an actionable message.
Requirements / Notes
- Capacitor plugin imports must remain in browser-owned modules.
@wrnexus/mobilere-exportsnativefrom@wrnexus/nativefor applications that
prefer the higher-level cross-platform capability API.
Complete TypeScript API
This declaration comes from the exact installed package and lists its exported functions, classes, interfaces, and types.
export { native } from '@wrnexus/native';
/** @wrnexus/mobile — SSR-safe access to Capacitor's native bridge. */
type MobilePlatform = "ios" | "android" | "web" | string;
interface CapacitorBridge {
isNativePlatform?: () => boolean;
getPlatform?: () => MobilePlatform;
Plugins?: Record<string, unknown>;
}
declare class MobileUnavailableError extends Error {
constructor(message?: string);
}
/** Register a plugin imported by browser-only application code. */
declare function registerPlugin<T extends object>(name: string, instance: T): T;
/** True only inside a native Capacitor iOS or Android WebView. SSR-safe. */
declare function isNative(): boolean;
/** Current Capacitor platform, falling back to `web` during SSR and in browsers. */
declare function platform(): MobilePlatform;
/** Return an injected Capacitor plugin, or undefined when it is unavailable. */
declare function plugin<T extends object>(name: string): T | undefined;
/** Require an installed native plugin and produce a useful error when absent. */
declare function requirePlugin<T extends object>(name: string): T;
/** Invoke a plugin method without importing native code into an SSR module. */
declare function invoke<TResult = unknown>(pluginName: string, method: string, options?: unknown): Promise<TResult>;
/** Run native behavior when available, with an optional SSR/web fallback. */
declare function whenNative<T>(native: () => T | Promise<T>, fallback?: () => T | Promise<T>): Promise<T | undefined>;
declare const mobile: {
isNative: typeof isNative;
platform: typeof platform;
registerPlugin: typeof registerPlugin;
plugin: typeof plugin;
requirePlugin: typeof requirePlugin;
invoke: typeof invoke;
whenNative: typeof whenNative;
};
export { type CapacitorBridge, type MobilePlatform, MobileUnavailableError, invoke, isNative, mobile, platform, plugin, registerPlugin, requirePlugin, whenNative };
Examples
Examples are taken from this package's installed documentation and must be evaluated with its requirements and stability notes.
Register and invoke a Capacitor plugin
import { Camera, CameraResultType } from "@capacitor/camera";
import { mobile } from "@wrnexus/mobile";
mobile.registerPlugin("Camera", Camera);
export async function takePhoto() {
if (!mobile.isNative()) return null;
return mobile.invoke("Camera", "getPhoto", {
quality: 85,
resultType: CameraResultType.Uri,
});
}Provide a browser fallback
import { Haptics, ImpactStyle } from "@capacitor/haptics";
import { mobile } from "@wrnexus/mobile";
mobile.registerPlugin("Haptics", Haptics);
export const confirmAction = () =>
mobile.whenNative(
() => mobile.invoke("Haptics", "impact", { style: ImpactStyle.Medium }),
() => navigator.vibrate?.(30),
);Read an optional plugin without throwing
import type { NetworkPlugin } from "@capacitor/network";
import { mobile } from "@wrnexus/mobile";
const network = mobile.plugin<NetworkPlugin>("Network");
const status = network ? await network.getStatus() : { connected: true, connectionType: "unknown" };