expo-router
Version:
Expo Router is a file-based router for React Native and web applications.
724 lines (723 loc) • 24.7 kB
TypeScript
import type { PropsWithChildren } from 'react';
import type { ColorValue, ImageSourcePropType, StyleProp, TextStyle, ViewStyle } from 'react-native';
import type { TabsHostProps, TabsScreenProps } from 'react-native-screens';
import type { SFSymbol } from 'sf-symbols-typescript';
import type { DefaultRouterOptions, ParamListBase, TabNavigationState } from '../react-navigation/native';
import type { StandardUseNavigationBuilderOptions } from '../standard-navigation';
import type { ScreenProps } from '../useScreens';
import type { ErrorBoundaryProps } from '../views/Try';
/**
* Event map for `NativeTabs` navigation events.
* Only `tabPress` is currently supported.
*/
export type NativeTabNavigationEventMap = {
tabPress: {
data: {
__internalTabsType: 'native';
/**
* `true` when the native side prevented the selection because the target
* tab is `disabled`. The event is still emitted so listeners are notified,
* but no navigation occurs.
*/
isPrevented: boolean;
};
canPreventDefault: false;
};
};
export type NativeScreenProps = Partial<Omit<TabsScreenProps, 'screenKey'>>;
/**
* Props passed to the underlying tab host implementation in `react-native-screens`.
*/
export type NativeTabsHostNativeProps = Partial<Omit<TabsHostProps, 'navStateRequest' | 'onTabSelected' | 'children'>>;
export interface NativeTabOptions extends DefaultRouterOptions {
/**
* @platform android
* @platform iOS
*/
icon?: SymbolOrImageSource;
/**
* @platform android
* @platform iOS
*/
selectedIcon?: SymbolOrImageSource;
/**
* @platform android
* @platform iOS
* @platform web
*/
title?: string;
/**
* @platform android
* @platform iOS
* @platform web
*/
badgeValue?: string;
/**
* @platform android
* @platform iOS
* @platform web
*/
selectedLabelStyle?: NativeTabsLabelStyle;
/**
* @platform android
* @platform iOS
* @platform web
*/
labelStyle?: NativeTabsLabelStyle;
/**
* @platform iOS
*/
role?: NativeTabsTabBarItemRole;
/**
* @platform iOS
*/
disableAutomaticSearchActivation?: boolean;
/**
* @platform iOS
*/
prominent?: boolean;
/**
* @platform android
* @platform iOS
*/
selectedIconColor?: ColorValue;
/**
* @platform iOS
*/
selectedBadgeBackgroundColor?: ColorValue;
/**
* @platform android
* @platform iOS
* @platform web
*/
badgeBackgroundColor?: ColorValue;
/**
* @platform android
* @platform web
*/
badgeTextColor?: ColorValue;
/**
* @platform android
* @platform iOS
* @platform web
*/
backgroundColor?: ColorValue;
/**
* @platform iOS
*/
blurEffect?: NativeTabsBlurEffect;
/**
* @platform iOS
*/
shadowColor?: ColorValue;
/**
* @platform android
* @platform iOS
*/
iconColor?: ColorValue;
/**
* @platform android
*/
tintColor?: ColorValue;
/**
* @platform iOS
*/
disableTransparentOnScrollEdge?: boolean;
/**
* @platform iOS
*/
titlePositionAdjustment?: {
horizontal?: number;
vertical?: number;
};
/**
* @platform iOS
*/
selectedTitlePositionAdjustment?: {
horizontal?: number;
vertical?: number;
};
/**
* @platform android
* @platform web
*/
indicatorColor?: ColorValue;
/**
* @platform android
*/
rippleColor?: ColorValue;
/**
* @platform android
*/
disableIndicator?: boolean;
/**
* @platform android
*/
labelVisibilityMode?: NativeTabsTabBarItemLabelVisibilityMode;
/**
* @platform android
* @platform iOS
* @platform web
*/
hidden?: boolean;
/**
* @platform android
* @platform iOS
*/
disabled?: boolean;
/**
* @platform android
* @platform iOS
*/
tabBarItemTestID?: string;
/**
* @platform android
* @platform iOS
*/
tabBarItemAccessibilityLabel?: string;
/**
* @platform iOS
*/
specialEffects?: TabsScreenProps['specialEffects'];
/**
* @platform android
* @platform iOS
*/
nativeProps?: NativeScreenProps;
/**
* @platform android
* @platform iOS
*/
disableAutomaticContentInsets?: boolean;
/**
* @platform android
* @platform iOS
*/
contentStyle?: Pick<ViewStyle, 'backgroundColor' | 'experimental_backgroundImage' | 'padding' | 'paddingTop' | 'paddingBottom' | 'paddingLeft' | 'paddingRight' | 'paddingBlock' | 'paddingBlockEnd' | 'paddingBlockStart' | 'paddingInline' | 'paddingInlineEnd' | 'paddingInlineStart' | 'paddingEnd' | 'paddingHorizontal' | 'paddingVertical' | 'paddingStart' | 'alignContent' | 'alignItems' | 'justifyContent' | 'flexDirection' | 'gap'>;
}
/**
* How an image-based icon is tinted on iOS: `template` lets the tab bar recolor the icon,
* `original` keeps the image's own colors. SF Symbols are always tinted by the system.
* @platform ios
*/
export type IconRenderingMode = 'template' | 'original';
export type SymbolOrImageSource = {
/**
* The name of the SF Symbol to use as an icon.
* @platform iOS
*/
sf?: SFSymbol;
/**
* The name of the iOS asset catalog image to use as an icon.
* @platform iOS
*/
xcasset?: string;
/**
* The name of the drawable resource to use as an icon.
* @platform android
*/
drawable?: string;
} | {
/**
* The image source to use as an icon.
*/
src?: ImageSourcePropType | Promise<ImageSourcePropType | null>;
/**
* Controls how the icon is rendered on iOS.
* @platform ios
* @default 'template'
*/
renderingMode?: IconRenderingMode;
};
export type NativeTabsLabelStyle = Pick<TextStyle, 'fontFamily' | 'fontSize' | 'fontStyle' | 'fontWeight' | 'color'>;
export declare const SUPPORTED_BLUR_EFFECTS: readonly ["none", "systemDefault", "extraLight", "light", "dark", "regular", "prominent", "systemUltraThinMaterial", "systemThinMaterial", "systemMaterial", "systemThickMaterial", "systemChromeMaterial", "systemUltraThinMaterialLight", "systemThinMaterialLight", "systemMaterialLight", "systemThickMaterialLight", "systemChromeMaterialLight", "systemUltraThinMaterialDark", "systemThinMaterialDark", "systemMaterialDark", "systemThickMaterialDark", "systemChromeMaterialDark"];
/**
* @see [Apple documentation](https://developer.apple.com/documentation/uikit/uiblureffect/style)
*/
export type NativeTabsBlurEffect = (typeof SUPPORTED_BLUR_EFFECTS)[number];
export interface NativeTabsProps extends PropsWithChildren {
/** A component to render when an individual tab screen throws an error. */
unstable_screenErrorBoundary?: React.ComponentType<ErrorBoundaryProps>;
/**
* Enables React Activity for tab screens. Inactive tabs are hidden while preserving their state.
* @default false
*/
activityEnabled?: boolean;
/**
* The style of the every tab label in the tab bar.
*/
labelStyle?: StyleProp<NativeTabsLabelStyle> | {
default?: StyleProp<NativeTabsLabelStyle>;
selected?: StyleProp<NativeTabsLabelStyle>;
};
/**
* The color of every tab icon in the tab bar.
*/
iconColor?: ColorValue | {
default?: ColorValue;
selected?: ColorValue;
};
/**
* The tint color of the tab icon.
*
* Can be overridden by icon color and label color for each tab individually.
*/
tintColor?: ColorValue;
/**
* The background color of the tab bar.
*
* On iOS 26 and later, the system draws the tab bar background and this prop has no effect.
*/
backgroundColor?: ColorValue;
/**
* The background color of every badge in the tab bar.
*/
badgeBackgroundColor?: ColorValue;
/**
* When set to `true`, hides the tab bar.
*
* @default false
*/
hidden?: boolean;
/**
* Specifies the minimize behavior for the tab bar.
*
* Available starting from iOS 26.
*
* The following values are currently supported:
*
* - `automatic` - resolves to the system default minimize behavior
* - `never` - the tab bar does not minimize
* - `onScrollDown` - the tab bar minimizes when scrolling down and
* expands when scrolling back up
* - `onScrollUp` - the tab bar minimizes when scrolling up and expands
* when scrolling back down
*
* @see The supported values correspond to the official [Apple documentation](https://developer.apple.com/documentation/uikit/uitabbarcontroller/minimizebehavior).
*
* @default automatic
*
* @platform iOS 26+
*/
minimizeBehavior?: NativeTabsTabBarMinimizeBehavior;
/**
* The blur effect applied to the tab bar.
*
* On iOS 26 and later, the tab bar background is drawn by the system and this prop has no effect.
*
* @platform iOS
*/
blurEffect?: NativeTabsBlurEffect;
/**
* The color of the shadow.
*
* On iOS 26 and later, the tab bar background is drawn by the system and this prop has no effect.
*
* @see [Apple documentation](https://developer.apple.com/documentation/uikit/uibarappearance/shadowcolor)
*
* @platform iOS
*/
shadowColor?: ColorValue;
/**
* @see [Apple documentation](https://developer.apple.com/documentation/uikit/uitabbaritem/titlepositionadjustment)
*
* @platform iOS
*/
titlePositionAdjustment?: {
horizontal?: number;
vertical?: number;
};
/**
* When set to `true`, the tab bar will not become transparent when scrolled to the edge.
*
* On iOS 26 and later, the tab bar background is drawn by the system and this prop has no effect.
*
* @platform iOS
*/
disableTransparentOnScrollEdge?: boolean;
/**
* When set to `true`, the tab bar can turn into a sidebar. The sidebar shows only on iPad, and on iOS 27 and later on larger iPhone models in landscape orientation.
*
* @platform iOS 18+
*/
sidebarAdaptable?: boolean;
/**
* Selects whether the sidebar or the tab bar shows.
* When `sidebarAdaptable` is not set, this prop also enables it.
*
* @see [Apple documentation](https://developer.apple.com/documentation/uikit/uitabbarcontroller/sidebar-swift.class/preferredplacement)
* @platform iOS 27+
*/
preferredPlacement?: 'automatic' | 'sidebar' | 'tabBar';
/**
* When set to `true`, the tab bar hides and shows without animation when `hidden` changes.
*
* @default false
* @platform iOS 18+
*/
disableHiddenAnimation?: boolean;
/**
* Disables the active indicator for the tab bar.
*
* @platform android
*/
disableIndicator?: boolean;
/**
* The behavior when navigating back with the back button.
*
* @platform android
*/
backBehavior?: 'none' | 'initialRoute' | 'history';
/**
* The visibility mode of the tab item label.
*
* @see [Material Components documentation](https://github.com/material-components/material-components-android/blob/master/docs/components/BottomNavigation.md#making-navigation-bar-accessible)
*
* @platform android
*/
labelVisibilityMode?: NativeTabsTabBarItemLabelVisibilityMode;
/**
* The color of the ripple effect when the tab is pressed.
*
* @platform android
*/
rippleColor?: ColorValue;
/**
* The color of the tab indicator.
*
* @platform android
* @platform web
*/
indicatorColor?: ColorValue;
/**
* The color of the badge text.
*
* @platform android
* @platform web
*/
badgeTextColor?: ColorValue;
/**
* When `true`, the tab bar lifts above the keyboard (input method editor, or IME) instead of being overlaid by it. By default, the keyboard overlays the tab bar.
*
* Requires `windowSoftInputMode="adjustResize"`. Has no effect on Android API levels earlier than 30 (Android 11).
*
* @default false
* @platform android
*/
tabBarRespectsIMEInsets?: boolean;
/**
* Listeners for navigation events on all tabs.
*
* Supported events:
* - `tabPress` - called when a tab is pressed
* - `focus` - called when the screen comes into focus
* - `blur` - called when the screen loses focus
*
* @example
* ```tsx
* <NativeTabs
* screenListeners={{
* tabPress: (e) => {
* console.log('Any tab pressed');
* },
* }}
* >
* ...
* </NativeTabs>
* ```
*/
screenListeners?: StandardUseNavigationBuilderOptions<TabNavigationState<ParamListBase>, object, NativeTabNavigationEventMap>['screenListeners'];
/**
* Props passed to the underlying native tab host implementation in `react-native-screens`.
* Use this to configure props that are not directly exposed by Expo Router.
*
* > **Note**: This is an unstable API and may change or be removed in minor versions.
*
* @platform android
* @platform ios
*/
unstable_nativeProps?: NativeTabsHostNativeProps;
}
export interface InternalNativeTabsProps extends NativeTabsProps {
nonTriggerChildren?: React.ReactNode;
tabConfigurationKey: string;
}
export interface OnTabChangeEventPayload {
/**
* The route key of the tab the native side has just selected.
*/
selectedKey: string;
/**
* The provenance value reported by the native side for this selection.
*
* The navigator echoes this back via `navStateRequest.baseProvenance` on
* subsequent JS-driven updates so the native side can distinguish stale
* updates from fresh ones. See `TabsHostNavStateRequest` in
* `react-native-screens` for the full contract.
*/
provenance: number;
isNativeAction: boolean;
/**
* Whether the native side prevented this selection because the target tab is
* `disabled`. When `true`, the navigator emits `tabPress` but skips navigation.
*
* @default false
*/
isPrevented?: boolean;
}
export interface NativeTabsViewProps extends Omit<InternalNativeTabsProps, 'labelStyle' | 'iconColor' | 'backgroundColor' | 'badgeBackgroundColor' | 'blurEffect' | 'indicatorColor' | 'badgeTextColor' | 'rippleColor' | 'disableIndicator' | 'labelVisibilityMode' | 'tabConfigurationKey'> {
focusedIndex: number;
/**
* Provenance counter associated with the currently rendered `focusedIndex`.
*/
provenance: number;
tabs: NativeTabsViewTabItem[];
onTabChange: (event: OnTabChangeEventPayload) => void;
}
export interface NativeTabsViewTabItem {
/** Internal navigation identity, separate from the native tab selection name. */
routeKey?: string;
options: NativeTabOptions;
name: string;
contentRenderer: () => React.ReactNode;
}
export declare const SUPPORTED_TAB_BAR_ITEM_LABEL_VISIBILITY_MODES: readonly ["auto", "selected", "labeled", "unlabeled"];
/**
* @see [Material Components documentation](https://github.com/material-components/material-components-android/blob/master/docs/components/BottomNavigation.md#making-navigation-bar-accessible)
*
* @platform android
*/
export type NativeTabsTabBarItemLabelVisibilityMode = (typeof SUPPORTED_TAB_BAR_ITEM_LABEL_VISIBILITY_MODES)[number];
export declare const SUPPORTED_TAB_BAR_MINIMIZE_BEHAVIORS: readonly ["automatic", "never", "onScrollDown", "onScrollUp"];
/**
* @see [Apple documentation](https://developer.apple.com/documentation/uikit/uitabbarcontroller/minimizebehavior)
*
* @platform iOS 26
*/
export type NativeTabsTabBarMinimizeBehavior = (typeof SUPPORTED_TAB_BAR_MINIMIZE_BEHAVIORS)[number];
export interface NativeTabTriggerProps {
/**
* The name of the route.
*
* This is required when used inside a Layout component.
*
* When used in a route it has no effect.
*/
name?: string;
/**
* Overrides React Activity behavior inherited from `NativeTabs` for this route when declared in
* a layout.
*/
activityEnabled?: boolean;
/**
* If true, the tab will be hidden from the tab bar.
*
* > **Note**: Marking a tab as `hidden` means it cannot be navigated to in any way.
*
* > **Note**: Dynamically hiding tabs will remount the navigator and the state will be reset.
*/
hidden?: boolean;
/**
* Props passed to the underlying native tab screen implementation.
* Use this to configure props not directly exposed by Expo Router, but available in `react-native-screens`.
*
* > **Note**: This will override any other props set by Expo Router and may lead to unexpected behavior.
*
* > **Note**: This is an unstable API and may change or be removed in minor versions.
*
* @platform android
* @platform iOS
*/
unstable_nativeProps?: NativeScreenProps;
/**
* If true, the tab will not pop stack to the root when selected again.
*
* @default false
* @platform iOS
*/
disablePopToTop?: boolean;
/**
* If true, the tab will not scroll to the top when selected again.
* @default false
*
* @platform iOS
*/
disableScrollToTop?: boolean;
/**
* If `true`, the tab is shown but cannot be selected by tapping it in the
* tab bar.
*
* > **Note:** This only suppresses the native tap interaction. JavaScript
* > navigation such as `router.push()` or `<Link />` still navigates to
* > the tab. Use this for tabs that should appear visible but be temporarily inert,
* > and gate navigation in your own code if you need to fully prevent access.
*
* Unlike `hidden`, the tab remains visible in the tab bar.
*
* @default false
*
* @platform android
* @platform ios
*/
disabled?: boolean;
/**
* A test identifier for the tab bar item.
*
* On iOS it maps to the item's accessibility identifier, which XCUITest and Maestro match.
* On Android it maps to the item's view tag, which Espresso-based drivers like Detox read
* but Maestro and Appium do not. Use `accessibilityLabel` to match the tab by id there.
*
* @platform android
* @platform iOS
*/
testID?: string;
/**
* The accessibility label of the tab bar item, announced by screen readers.
* Defaults to the visible tab label.
*
* On Android, maps to the item's `contentDescription` and requires API 26 or above.
*
* @platform android
* @platform iOS
*/
accessibilityLabel?: string;
/**
* The children of the trigger.
*
* Use `Icon`, `Label`, and `Badge` components to customize the tab.
*/
children?: React.ReactNode;
/**
* System-provided tab bar item with predefined icon and title
*
* Uses Apple's built-in tab bar items (e.g., bookmarks, contacts, downloads) with
* standard iOS styling and localized titles. Custom `icon` or `selectedIcon`
* properties will override the system icon, but the system-defined title cannot
* be customized.
*
* The `"search"` role makes this tab the system search tab
* ([`UISearchTab`](https://developer.apple.com/documentation/uikit/uisearchtab)) on iOS 26.1 and later.
* Do not change `role` after the tab renders.
*
* @see The supported values correspond to the official [Apple documentation](https://developer.apple.com/documentation/uikit/uitabbaritem/systemitem).
* @platform ios
*/
role?: NativeTabsTabBarItemRole;
/**
* When set to `true`, selecting the search tab does not activate its search field.
*
* By default, selecting a tab with `role="search"` focuses the header search bar
* of the stack nested in the tab and opens the keyboard.
*
* This prop has an effect only when `role` is `"search"`.
* On iOS 27, setting this prop to `true` also removes the separate placement of the search tab.
* A screen-level trigger applies this prop only after the screen first renders.
* To affect the first selection, set it on the layout trigger.
*
* @default false
* @platform ios 26.1+
*/
disableAutomaticSearchActivation?: boolean;
/**
* Shows the tab on its own surface in the tab bar, which stays visible when the tab bar minimizes.
* Only one tab can be prominent. Has effect only on the layout trigger.
*
* @see The official [Apple documentation](https://developer.apple.com/documentation/uikit/uitabbarcontroller/prominenttabidentifier).
* @default false
* @platform ios 27+
*/
prominent?: boolean;
/**
* The default behavior differs between iOS and Android.
*
* On **Android**, the content of a native tabs screen is automatically wrapped in a `SafeAreaView`,
* and the **bottom** inset is applied. Other insets must be handled manually.
*
* On **iOS**, the first scroll view nested inside a native tabs screen has
* [automatic content inset adjustment](https://reactnative.dev/docs/scrollview#contentinsetadjustmentbehavior-ios) enabled
*
* When this property is set to `true`, automatic content inset adjustment is disabled for the screen
* and must be managed manually. You can use `SafeAreaView` from `react-native-screens/experimental`
* to handle safe area insets.
*
* @platform android
* @platform ios
*/
disableAutomaticContentInsets?: boolean;
/**
* The style applied to the content of the tab
*
* Note: Only certain style properties are supported.
*/
contentStyle?: NativeTabOptions['contentStyle'];
/**
* When set to `true`, the tab bar will not become transparent when scrolled to the edge.
*
* On iOS 26 and later, the tab bar background is drawn by the system and this prop has no effect.
*
* When set on a trigger, it takes precedence over the value set on `NativeTabs`.
*
* @platform iOS
*/
disableTransparentOnScrollEdge?: boolean;
/**
* The color of the ripple effect when this tab is selected - will be visible when other tabs are pressed.
*
* When set on a trigger, it takes precedence over the value set on `NativeTabs`.
*
* @platform android
*/
rippleColor?: ColorValue;
/**
* The color of the active indicator for this tab.
*
* When set on a trigger, it takes precedence over the value set on `NativeTabs`.
*
* @platform android
*/
indicatorColor?: ColorValue;
/**
* When set to `true`, disables the active indicator for this tab.
*
* When set on a trigger, it takes precedence over the value set on `NativeTabs`.
*
* @default false
*
* @platform android
*/
disableIndicator?: boolean;
/**
* The visibility mode of the tab item label for this tab.
*
* When set on a trigger, it takes precedence over the value set on `NativeTabs`.
*
* @see [Material Components documentation](https://github.com/material-components/material-components-android/blob/master/docs/components/BottomNavigation.md#making-navigation-bar-accessible)
*
* @platform android
*/
labelVisibilityMode?: NativeTabsTabBarItemLabelVisibilityMode;
/**
* Listeners for navigation events on this tab.
*
* Supported events:
* - `tabPress` - called when this tab is pressed
* - `focus` - called when this screen comes into focus
* - `blur` - called when this screen loses focus
*
* @example
* ```tsx
* <NativeTabs.Trigger
* name="home"
* listeners={{
* tabPress: (e) => {
* console.log('Home tab pressed');
* },
* }}
* />
* ```
*/
listeners?: ScreenProps<any, TabNavigationState<ParamListBase>, NativeTabNavigationEventMap>['listeners'];
}
declare const SUPPORTED_TAB_BAR_ITEM_ROLES: readonly ["bookmarks", "contacts", "downloads", "favorites", "featured", "history", "more", "mostRecent", "mostViewed", "recents", "search", "topRated"];
export type NativeTabsTabBarItemRole = (typeof SUPPORTED_TAB_BAR_ITEM_ROLES)[number];
export {};