UNPKG

expo-router

Version:

Expo Router is a file-based router for React Native and web applications.

724 lines (723 loc) • 24.7 kB
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 {};