One ships @vxrn/native — a package of native navigation primitives that bring iOS and Android platform features to your app. These are experimental and the API may change.
npm install @vxrn/nativeNo Expo Modules dependency — @vxrn/native uses standard React Native native modules (RCTViewManager + TurboModules).
For iOS, add to your Podfile:
pod 'VxrnNative', :path => '../node_modules/@vxrn/native/ios'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 }} />All UIKit semantic colors: systemBlue, systemRed, systemGreen, label, secondaryLabel, systemBackground, separator, systemFill, systemGray through systemGray6, and more.
Color.android.black, Color.android.white — system colors via PlatformColorColor.android.material.primary — static Material Design 3 baseline colorsColor.android.dynamic.primary — dynamic colors that adapt to the user’s wallpaper (Android 12+)Native iOS 18+ zoom transitions. The destination screen expands from a source element with a fluid animation.
import { ZoomTransitionSource, ZoomTransitionEnabler } from '@vxrn/native'
// source screen — wrap the tappable elementfunction 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 anywherefunction DetailScreen() { const { id } = useParams() return ( <View> <Stack.Screen options={{ headerShown: false }} /> <ZoomTransitionEnabler zoomTransitionSourceIdentifier={id} /> <Text>Detail for {id}</Text> </View> )}headerShown: false on the detail screen to avoid the header overlapping during the zoom animationidentifier must match between ZoomTransitionSource and ZoomTransitionEnablerZoomTransitionAlignmentRectDetector on the destination to align the zoom to a specific elementNative iOS UIToolbar rendered at the bottom of the screen. Supports SF Symbols, badges, and Liquid Glass on iOS 26.
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> )}| Prop | Type | Description |
|---|---|---|
identifier | string | Unique ID (required) |
title | string | Button title |
systemImageName | string | SF Symbol name |
type | 'normal' | 'fixedSpacer' | 'fluidSpacer' | 'searchBar' | Item type |
barButtonItemStyle | 'plain' | 'prominent' | Button style |
badgeConfiguration | { value?: string } | Badge (iOS 26+) |
disabled | boolean | Disable the button |
hidden | boolean | Hide the button |
onSelected | () => void | Tap handler |
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')}/>Native UISplitViewController for iPad multi-column layouts.
react-native-screens gamma mode. Add this to the top of your Podfile:ENV['RNS_GAMMA_ENABLED'] ||= '1'pod install. Currently blocked on a react-native-screens upstream issue with Fabric — tracking for a fix.import { SplitView } from '@vxrn/native'
function Layout() { return ( <SplitView> <SplitView.Column> <SidebarContent /> </SplitView.Column> </SplitView> )}On iPhone, SplitView collapses to a single column automatically.
Platform-native tab bars using react-native-bottom-tabs. Renders UITabBarController on iOS and BottomNavigationView on Android.
npx expo install @bottom-tabs/react-navigation react-native-bottom-tabsimport { NativeTabs } from 'one/native-tabs'
export default function Layout() { return ( <NativeTabs> <NativeTabs.Screen name="feed" options={{ title: 'Feed', tabBarIcon: () => ({ sfSymbol: 'newspaper' }), }} /> <NativeTabs.Screen name="profile" options={{ title: 'Profile', tabBarIcon: () => ({ sfSymbol: 'person' }), }} /> </NativeTabs> )}Guard routes behind authentication:
<NativeTabs> <NativeTabs.Screen name="login" /> <NativeTabs.Protected guard={isAuthed}> <NativeTabs.Screen name="dashboard" /> <NativeTabs.Screen name="settings" /> </NativeTabs.Protected></NativeTabs>| Feature | iOS | Android | Web |
|---|---|---|---|
| Color API | system colors via PlatformColor | Material 3 dynamic/static | returns null |
| Zoom Transitions | iOS 18+ | — | — |
| Toolbar | UIToolbar + SF Symbols | — | — |
| Menu Actions | UIMenu / UIAction | — | — |
| SplitView | iPad (gamma required) | — | — |
| NativeTabs | UITabBarController | BottomNavigationView | — |
All components return null on unsupported platforms — safe to use in cross-platform code.
Edit this page on GitHub.