New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

capacitor-navigation

Package Overview
Dependencies
Maintainers
1
Versions
5
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

capacitor-navigation

Native navigation bars (nav bar + tab bar) and back event bridging for Capacitor

latest
Source
npmnpm
Version
0.4.0
Version published
Maintainers
1
Created
Source

capacitor-navigation

Native navigation primitives for Capacitor apps:

  • Top navigation bar
  • Bottom tab bar
  • Back gesture/back press event bridge
  • Layout metrics API for anchoring web UI to native chrome

This plugin is designed for SPA routers. Native bars are rendered by iOS/Android, and page transitions remain in your web layer.

Install

pnpm add capacitor-navigation
npx cap sync

Import

import { Navigation } from 'capacitor-navigation'

Quick Start

1) Show a native top bar

await Navigation.setNavigationBar({
  title: 'Home',
  subtitle: 'Native toolbar',
  showBack: true,
  shadow: true,
  rightButtons: [
    { id: 'search', icon: 'search', label: 'Search' },
    { id: 'settings', icon: 'settings', label: 'Settings' },
  ],
})

2) Show a native tab bar

await Navigation.setTabBar({
  items: [
    { id: 'home', label: 'Home', icon: 'house', androidIcon: 'home' },
    { id: 'feed', label: 'Feed', icon: 'list.bullet', androidIcon: 'list' },
    { id: 'profile', label: 'Profile', icon: 'person', androidIcon: 'person' },
  ],
  selected: 'home',
  tintColor: '#007AFF',
  darkTintColor: '#0A84FF',
  insetContent: true, // content stays above tab bar
})

3) Route in JS when native back happens

import { useRouter } from 'vue-router'

const router = useRouter()

const l1 = await Navigation.addListener('backPress', async () => {
  if (window.history.length > 1) await router.back()
})

const l2 = await Navigation.addListener('backTap', async () => {
  if (window.history.length > 1) await router.back()
})

API

Methods

  • setNavigationBar(options)
  • hideNavigationBar()
  • setTabBar(options)
  • updateTab({ id, badge?, label? })
  • selectTab({ id })
  • hideTabBar()
  • setBackGesture(options)
  • getLayoutMetrics()
  • setStatusBar(options)
  • setColorScheme({ scheme: 'light' | 'dark' | 'auto' })
  • getColorScheme(){ scheme: 'light' | 'dark' }
  • removeAllListeners()

Events

  • backGesture
  • backGestureProgress with { state, progress, committed }
  • backPress
  • backTap
  • tabSelect with { id }
  • navActionTap with { id }
  • layoutMetricsChange with NavigationLayoutMetrics
  • colorSchemeChange with { scheme: 'light' | 'dark' }

Layout Metrics

Use native metrics to position floating web UI under header or above tab bar.

type NavigationLayoutMetrics = {
  viewportWidth: number
  viewportHeight: number
  safeAreaTop: number
  safeAreaBottom: number
  navBarVisible: boolean
  navBarTop: number
  navBarHeight: number
  navBarBottom: number
  tabBarVisible: boolean
  tabBarTop: number
  tabBarHeight: number
  tabBarBottom: number
  contentTop: number
  contentBottom: number
  contentHeight: number
}

Example:

const metrics = await Navigation.getLayoutMetrics()
const top = metrics.navBarBottom + 8
const bottomPanelTop = metrics.tabBarTop - 56 - 10

The plugin also injects CSS variables:

  • --cap-nav-height
  • --cap-tab-height
  • --cap-content-top
  • --cap-content-bottom
  • --cap-content-height
  • --cap-nav-bottom
  • --cap-tab-top

Scroll Mode — Full vs Between Bars

By default, web content is inset between native bars (insetContent: true) so content never overlaps the bars. Set insetContent: false for full/under-bar scrolling.

await Navigation.setNavigationBar({ title: 'Home', insetContent: true })
await Navigation.setTabBar({ items: [...], insetContent: true })
ModeBehavior
insetContent: true (default)Content is bounded between bars — no overlap
insetContent: falseContent extends under bars — bars float over content

Platform implementation:

  • iOSscrollView.contentInset (native, rubberbanding preserved)
  • AndroidWebView.setPadding()
  • Webbody padding injected via <style> tag

Status Bar

Control the status bar icon color independently from the app color scheme — useful for full-screen hero pages or custom nav bars with dark backgrounds.

// White clock/icons (for dark backgrounds)
await Navigation.setStatusBar({ style: 'light' })

// Dark clock/icons (for light backgrounds)
await Navigation.setStatusBar({ style: 'dark' })

// Follow the active color scheme (default)
await Navigation.setStatusBar({ style: 'auto' })

// Hide status bar (iOS only)
await Navigation.setStatusBar({ hidden: true })

// Android: also set background color
await Navigation.setStatusBar({ style: 'light', backgroundColor: '#000000' })
Platformstyle: 'light'style: 'dark'style: 'auto'
iOS.lightContent (white icons).darkContent (dark icons).default
AndroidisAppearanceLightStatusBars = false= truefollows isDarkMode()

iOS note: No Info.plist change required. The plugin installs a child view controller at runtime that takes ownership of preferredStatusBarStyle via the Objective-C runtime.

Dark / Light Mode

Dark color variants

Pass both light and dark color hex values; the native layer picks the right one automatically.

await Navigation.setTabBar({
  items: [...],
  tintColor: '#007AFF',               // light mode active
  darkTintColor: '#0A84FF',           // dark mode active
  unselectedTintColor: '#6C6C70',     // light mode inactive
  darkUnselectedTintColor: '#8E8E93', // dark mode inactive
  backgroundColor: '#F9F9F9',
  darkBackgroundColor: '#1C1C1E',
})

await Navigation.setNavigationBar({
  title: 'Home',
  tintColor: '#007AFF',
  darkTintColor: '#0A84FF',
  backgroundColor: '#F2F2F7',
  darkBackgroundColor: '#1C1C1E',
  titleColor: '#000000',
  darkTitleColor: '#FFFFFF',
})

iOS creates an adaptive UIColor(dynamicProvider:) — colors update automatically without re-calling the method. Android reads isDarkMode() at call time; re-call after colorSchemeChange to refresh.

Force dark / light from JS

// Force dark mode
await Navigation.setColorScheme({ scheme: 'dark' })

// Force light mode
await Navigation.setColorScheme({ scheme: 'light' })

// Follow system
await Navigation.setColorScheme({ scheme: 'auto' })

// Read current active scheme
const { scheme } = await Navigation.getColorScheme()
// 'light' | 'dark'
Platformdarklightauto
iOS 17+ / 26+UIWindowScene.traitOverrides.userInterfaceStyle = .dark + JS injection.light.unspecified
iOS 13–16UIWindow.overrideUserInterfaceStyle = .dark + JS injection.light.unspecified
AndroidAppCompatDelegate.MODE_NIGHT_YESMODE_NIGHT_NOMODE_NIGHT_FOLLOW_SYSTEM
Webhtml[data-color-scheme="dark"] + color-scheme: darklightremoves attribute

iOS also injects html[data-color-scheme] + html.style.colorScheme into the WKWebView so that prefers-color-scheme media queries and [data-color-scheme] CSS selectors both update reliably across all iOS versions.

React to system theme changes

await Navigation.addListener('colorSchemeChange', ({ scheme }) => {
  // scheme: 'light' | 'dark'
  // On Android: re-call setTabBar / setNavigationBar with correct colors
  // On iOS: not needed if using darkTintColor etc. (adaptive colors auto-update)
  applyTheme(scheme)
})

Platform Notes

iOS

  • Supports iosStyle: 'auto' | 'ios18' | 'ios26'
  • Supports blurStyle, backButtonStyle, and advanced back bubble tuning
  • Native edge pan emits progressive backGestureProgress

Android (14+)

  • Predictive back is enabled with native callback progress
  • Back bubble is supported and configurable through setBackGesture
  • getLayoutMetrics and layoutMetricsChange are implemented natively
  • Tab bar icons use Material icon names (androidIcon)

Option Highlights

setNavigationBar(options)

Common:

  • title, subtitle, visible
  • showBack, shadow
  • rightButton or rightButtons
  • tintColor, darkTintColor
  • backgroundColor, darkBackgroundColor
  • translucent
  • useSafeAreaTop, topInset
  • insetContent — push web content below bar (true = between bars, false = full/under)

iOS-focused:

  • iosStyle
  • blurStyle
  • backButtonStyle, backButtonLabel
  • backButtonBackgroundColor, backButtonSize, backButtonCornerRadius
  • titleColor, darkTitleColor

setTabBar(options)

  • items: TabItem[]
  • selected
  • tintColor, darkTintColor
  • unselectedTintColor, darkUnselectedTintColor
  • backgroundColor, darkBackgroundColor
  • translucent
  • useSafeAreaBottom, bottomInset
  • visible
  • insetContent — push web content above tab bar (true = between bars, false = full/under)

TabItem:

  • id, label
  • icon (SF Symbol for iOS)
  • androidIcon (Material icon name for Android)
  • badge

setBackGesture(options)

  • enabled
  • bubbleEnabled
  • bubbleTintColor
  • bubbleBackgroundColor
  • commitThreshold
  • commitVelocity
  • bubbleSize
  • bubbleRestingX
  • bubbleEmergeDistance
  • bubbleProgressDistance
  • bubbleTopInset
  • bubbleBottomInset
  • bubbleMinScale
  • bubbleMaxScale
  • bubbleMinAlpha
  • bubbleMaxAlpha
  • bubbleAllowEdgeOverflow

Vue Example (Anchor UI To Native Bars)

import { computed, onMounted, ref } from 'vue'
import { Navigation } from 'capacitor-navigation'
import type { NavigationLayoutMetrics } from 'capacitor-navigation'

const metrics = ref<NavigationLayoutMetrics | null>(null)

onMounted(async () => {
  metrics.value = await Navigation.getLayoutMetrics()
  await Navigation.addListener('layoutMetricsChange', data => {
    metrics.value = data
  })
})

const headerAccessoryStyle = computed(() => {
  if (!metrics.value) return { display: 'none' }
  return { top: `${Math.round(metrics.value.navBarBottom + 8)}px` }
})

const bottomAccessoryStyle = computed(() => {
  if (!metrics.value) return { display: 'none' }
  return { top: `${Math.round(metrics.value.tabBarTop - 56 - 10)}px` }
})

Behavior Model

  • Native bars are UI chrome only
  • Routing remains in your SPA router
  • Back events are emitted to JS; your app decides when/how to navigate

Keywords

capacitor

FAQs

Package last updated on 01 Mar 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts