Skip to main content
@capgo/capacitor-native-navigation UI & System Open source

Native Navigation Capacitor plugin

Render native navbars, tabbars, and transition shells over a full-screen Capacitor WebView

Install

bun add @capgo/capacitor-native-navigation bunx cap sync
npm, pnpm or yarn
  • npm install @capgo/capacitor-native-navigation
  • pnpm add @capgo/capacitor-native-navigation
  • yarn add @capgo/capacitor-native-navigation

Demo

Native Navigation demos

Native navigation chrome, tab selection, SVG icons, and styling options rendered as animated WebP demos.

Source assets
Animated native navigation shell demo showing native navbar, tabs, and WebView content
Native shell
Animated native navigation tap flow showing tab selection, push transition, and native back
Tap flow
Animated native SVG icon demo showing inline SVG icons, native tint, labels, and tab selection
SVG icons
Animated native navigation options demo showing dynamic colors, selected labels, badges, and zoom transitions
Styling options

Guide

How to use Native Navigation in Capacitor

Test on device

Download the Capgo app, then scan the QR code.

Native Navigation plugin preview QR code

Using @capgo/capacitor-native-navigation

@capgo/capacitor-native-navigation renders native top navigation, bottom tab chrome, and route transition shells over a single full-screen Capacitor WebView. Your web framework still owns routes and content, while native owns the app frame.

Install and sync

npm install @capgo/capacitor-native-navigation
npx cap sync

Configure the native frame

import { NativeNavigation } from '@capgo/capacitor-native-navigation';

await NativeNavigation.configure({
  contentInsetMode: 'css',
  animationDuration: 360,
  colors: {
    tint: '#0f172a',
    inactiveTint: '#64748b',
  },
});

Render a native navbar

await NativeNavigation.setNavbar({
  title: 'Inbox',
  subtitle: 'Native chrome',
  transparent: true,
  backButton: { visible: false },
  rightItems: [
    {
      id: 'compose',
      title: 'Compose',
      icon: {
        svg: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 20h9"/><path d="M16.5 3.5a2.12 2.12 0 0 1 3 3L7 19l-4 1 1-4Z"/></svg>',
      },
    },
  ],
});

Render a native tabbar

await NativeNavigation.setTabbar({
  selectedId: 'inbox',
  labelVisibilityMode: 'selected',
  icons: true,
  colors: {
    dynamic: true,
    tint: '#0f172a',
    inactiveTint: '#64748b',
  },
  tabs: [
    {
      id: 'inbox',
      title: 'Inbox',
      icon: {
        svg: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 4h16v16H4z"/><path d="m4 13 4 4h8l4-4"/></svg>',
      },
    },
    {
      id: 'search',
      title: 'Search',
      icon: {
        svg: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="7"/><path d="m20 20-3-3"/></svg>',
      },
    },
  ],
});

Connect native events to your router

Native bars emit intent. Your router still performs the route change:

await NativeNavigation.addListener('navbarBack', () => {
  router.back();
});

await NativeNavigation.addListener('navbarItemTap', ({ id }) => {
  if (id === 'compose') router.push('/compose');
});

await NativeNavigation.addListener('tabSelect', ({ id }) => {
  router.push(`/${id}`);
});

Animate route changes

Use a transition transaction around your normal web route update:

const transition = await NativeNavigation.beginTransition({
  direction: 'forward',
});

router.push('/message/42');
await router.ready?.();

await NativeNavigation.setNavbar({
  title: 'Message',
  backButton: { visible: true, title: 'Inbox' },
});

await NativeNavigation.finishTransition({
  id: transition.id,
  direction: 'forward',
});

Add a zoom transition

Use the zoom helpers for routes that open from a card, grid item, or media preview.

import { beginZoomTransition, finishZoomTransition } from '@capgo/capacitor-native-navigation';

const card = document.querySelector('[data-message-card]');
if (card) {
  const transition = await beginZoomTransition(card, { cornerRadius: 18 });

  router.push('/message/42');
  await router.ready?.();

  await NativeNavigation.setNavbar({
    title: 'Message',
    backButton: { visible: true, title: 'Inbox' },
  });

  await finishZoomTransition(undefined, {
    id: transition.id,
    cornerRadius: 18,
  });
}

Pad content with native insets

When contentInsetMode is css, the plugin writes CSS variables for the native bars:

.page {
  padding-top: var(--cap-native-navigation-top);
  padding-bottom: var(--cap-native-navigation-bottom);
}

Icon choices

Icons are native descriptors, not React or Vue nodes. Use SVG when you do not want to bundle native assets:

const icon = {
  svg: '<svg viewBox="0 0 24 24"><path d="M3 10.5 12 3l9 7.5"/></svg>',
  template: true,
  ios: { sfSymbol: 'house.fill' },
  android: { resource: 'ic_menu_view' },
};

Inline SVG supports path, line, polyline, polygon, circle, and rect, which covers common icon sets such as Lucide and Feather.

Combine with @capgo/capacitor-transitions

Use Native Navigation for the native navbar, tabbar, safe-area insets, and native intent events. Use @capgo/capacitor-transitions for the WebView page stack underneath that native chrome.

npm install @capgo/capacitor-native-navigation @capgo/capacitor-transitions
npx cap sync

Initialize both packages once:

import { NativeNavigation } from '@capgo/capacitor-native-navigation';
import '@capgo/capacitor-transitions';
import { initTransitions, setupRouterOutlet, setDirection } from '@capgo/capacitor-transitions/react';

initTransitions({ platform: 'auto' });

const outlet = document.querySelector('cap-router-outlet');
if (outlet) {
  setupRouterOutlet(outlet, { platform: 'auto', swipeGesture: 'auto' });
}

await NativeNavigation.configure({
  contentInsetMode: 'css',
});

Keep the transition outlet focused on pages, not duplicate web bars:

<cap-router-outlet platform="auto" swipe-gesture="auto">
  <cap-page>
    <cap-content slot="content" fullscreen>
      <main class="page">Inbox content</main>
    </cap-content>
  </cap-page>
</cap-router-outlet>

Drive both packages from the same router actions:

async function openMessage(id: string) {
  setDirection('forward');
  await router.push(`/messages/${id}`);
  await NativeNavigation.setNavbar({
    title: 'Message',
    backButton: { visible: true, title: 'Inbox' },
  });
}

await NativeNavigation.addListener('navbarBack', () => {
  setDirection('back');
  router.back();
});

await NativeNavigation.addListener('tabSelect', ({ id }) => {
  setDirection('root');
  router.push(`/${id}`);
});

Pick one animation layer per route change. Let @capgo/capacitor-transitions animate normal page pushes, and use Native Navigation's zoom helpers only for shared-element or zoom routes.

Full Reference

Keep going from Using @capgo/capacitor-native-navigation

If you are using Using @capgo/capacitor-native-navigation to plan native media and interface behavior, connect it with @capgo/capacitor-native-navigation for the implementation detail in @capgo/capacitor-native-navigation, Getting Started for the implementation detail in Getting Started, Using @capgo/capacitor-live-activities for the native capability in Using @capgo/capacitor-live-activities, @capgo/capacitor-live-activities for the implementation detail in @capgo/capacitor-live-activities, and Using @capgo/capacitor-video-player for the native capability in Using @capgo/capacitor-video-player.

FAQ

Native Navigation plugin FAQ

How do I install the Native Navigation plugin in a Capacitor app?

Run "bun add @capgo/capacitor-native-navigation" (or "npm install @capgo/capacitor-native-navigation"), then run "bunx cap sync" so the iOS and Android projects pick up the native code. Import it from "@capgo/capacitor-native-navigation" in your app code.

Does @capgo/capacitor-native-navigation work with React, Vue and Angular?

Yes. Native Navigation is a Capacitor package, so it works with any web framework that runs inside Capacitor, including Ionic, React, Vue, Angular, Svelte and plain JavaScript.

Which Capacitor version does @capgo/capacitor-native-navigation support?

Capgo plugins follow Capacitor's major version: use the plugin major version that matches your Capacitor major version (for example plugin v8 with Capacitor 8). The compatibility table in the GitHub README lists the maintained versions.

Is @capgo/capacitor-native-navigation free and open source?

Yes. The source code is public on GitHub at https://github.com/Cap-go/capacitor-native-navigation/ and the package is free to install from npm. Bug reports and pull requests are welcome.

Can I update code that uses Native Navigation without an App Store review?

Installing or upgrading the plugin changes native code, so it needs a new store build. After that, JavaScript, HTML and CSS changes that call the plugin can ship instantly with Capgo live updates.

Ship Native Navigation changes without waiting for app review

Once the plugin is in your store build, Capgo live updates push your JavaScript, HTML and CSS changes to users in minutes.

Start with Capgo