page wrnexusmobile { seo { title = "@wrnexus/mobile" description = "SSR-safe compatibility access to Capacitor plugins." } view {
W WRNexusJS
Native · Package reference

@wrnexus/mobile

SSR-safe compatibility access to Capacitor plugins.

v0.3.2Private registryNative

Install the package

After WorkRoot approves private registry access, install the release-aligned package:

bun add @wrnexus/mobile@0.3.2

Request 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 or undefined; 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() and platform() 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/mobile re-exports native from @wrnexus/native for applications that
  • prefer the higher-level cross-platform capability API.

Complete TypeScript API

Generated from the exact installed package declarations.

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

Copy-ready examples from the installed package documentation.

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" };
} }