Native Features (Alpha)

Experimental native navigation features for iOS and Android

One ships @vxrn/native, a package of native primitives for capabilities outside React Navigation. These are experimental and the API may change.

These features are experimental. Install the package and try them out, but expect API changes.

Install

Terminal window
npm install @vxrn/native

@vxrn/native links automatically during the native build.

wwwwwwwwwwwwwwwwwww

Color API

Type-safe platform colors for iOS and Android. Works with PlatformColor under the hood.

import { Color } from '@vxrn/native'
// iOS system colors
<View style={{ backgroundColor: Color.ios.systemBlue }} />
<Text style={{ color: Color.ios.label }} />
// Android Material Design 3
<View style={{ backgroundColor: Color.android.dynamic.primary }} />
<View style={{ backgroundColor: Color.android.material.surface }} />

iOS Colors

All UIKit semantic colors: systemBlue, systemRed, systemGreen, label, secondaryLabel, systemBackground, separator, systemFill, systemGray through systemGray6, and more.

Android Colors

  • Color.android.black, Color.android.white — system colors via PlatformColor
  • Color.android.material.primary — static Material Design 3 baseline colors
  • Color.android.dynamic.primary — dynamic colors that adapt to the user’s wallpaper (Android 12+)
wwwwwwwwwwwwwwwwwww

Zoom Transitions

Native iOS 18+ zoom transitions. The destination screen expands from a source element with a fluid animation.

Requires iOS 18+. Falls back to standard push transition on older versions.
import { ZoomTransitionSource, ZoomTransitionEnabler } from '@vxrn/native'
// source screen — wrap the tappable element
function ListScreen() {
const router = useRouter()
return (
<ZoomTransitionSource identifier="item-1">
<Pressable onPress={() => router.push('/detail?id=item-1')}>
<Text>Mountain View</Text>
</Pressable>
</ZoomTransitionSource>
)
}
// destination screen — place the enabler anywhere
function DetailScreen() {
const { id } = useParams()
return (
<View>
<Stack.Screen options={{ headerShown: false }} />
<ZoomTransitionEnabler zoomTransitionSourceIdentifier={id} />
<Text>Detail for {id}</Text>
</View>
)
}

Tips

  • Set headerShown: false on the detail screen to avoid the header overlapping during the zoom animation
  • The identifier must match between ZoomTransitionSource and ZoomTransitionEnabler
  • Use ZoomTransitionAlignmentRectDetector on the destination to align the zoom to a specific element
wwwwwwwwwwwwwwwwwww

Toolbar

Native iOS UIToolbar rendered at the bottom of the screen. Supports SF Symbols, badges, and Liquid Glass on iOS 26.

Use React Navigation’s unstable_headerLeftItems and unstable_headerRightItems screen options for header actions. Use ToolbarHost directly for a bottom UIToolbar; One does not add a second toolbar abstraction.

import { ToolbarHost, ToolbarItem, Color } from '@vxrn/native'
function MyScreen() {
return (
<View style={{ flex: 1 }}>
<Text>Content</Text>
<ToolbarHost>
<ToolbarItem
identifier="add"
title="Add"
systemImageName="plus"
tintColor={Color.ios.systemBlue}
onSelected={() => console.log('add tapped')}
/>
<ToolbarItem identifier="spacer" type="fluidSpacer" />
<ToolbarItem
identifier="share"
title="Share"
systemImageName="square.and.arrow.up"
barButtonItemStyle="prominent"
onSelected={() => console.log('share tapped')}
/>
<ToolbarItem
identifier="settings"
systemImageName="gearshape"
badgeConfiguration={{ value: '3' }}
onSelected={() => console.log('settings tapped')}
/>
</ToolbarHost>
</View>
)
}

ToolbarItem Props

PropTypeDescription
identifierstringUnique ID (required)
titlestringButton title
systemImageNamestringSF Symbol name
type'normal' | 'fixedSpacer' | 'fluidSpacer' | 'searchBar'Item type
barButtonItemStyle'plain' | 'prominent'Button style
badgeConfiguration{ value?: string }Badge (iOS 26+)
disabledbooleanDisable the button
hiddenbooleanHide the button
onSelected() => voidTap handler
wwwwwwwwwwwwwwwwwww

Native iOS UIMenu and UIAction for context menus in toolbars.

import { MenuAction } from '@vxrn/native'
<MenuAction
identifier="edit"
title="Edit"
icon="pencil"
onSelected={() => console.log('edit')}
/>
<MenuAction
identifier="delete"
title="Delete"
icon="trash"
destructive
onSelected={() => console.log('delete')}
/>
wwwwwwwwwwwwwwwwwww

SplitView

Native UISplitViewController for iPad multi-column layouts.

SplitView requires react-native-screens gamma mode. Add this to the top of your Podfile:
ENV['RNS_GAMMA_ENABLED'] ||= '1'
Then run pod install. The SplitView.Column markers must be direct children of SplitView; One maps them to the native screen columns and supplies the routed detail through slot.
import { SplitView } from '@vxrn/native'
function Layout() {
return (
<SplitView slot={Detail}>
<SplitView.Column>
<SidebarContent />
</SplitView.Column>
</SplitView>
)
}

On iPhone, SplitView collapses to a single column automatically.

wwwwwwwwwwwwwwwwwww

Platform Support

FeatureiOSAndroidWeb
Color APIsystem colors via PlatformColorMaterial 3 dynamic/staticreturns null
Zoom TransitionsiOS 18+unsupportedunsupported
ToolbarUIToolbar + SF Symbolsunsupportedunsupported
Menu ActionsUIMenu / UIActionunsupportedunsupported
SplitViewiPad (gamma required)routed slot fallbackrouted slot fallback

Unsupported toolbar and zoom components return null, so they are safe to keep in cross-platform component trees. SplitView renders its routed slot without native columns on Android and web.

Edit this page on GitHub.