new·The score now tells you which way it movedA brain's exam only ever grows: its own material writes questions, and so does every question a real caller asked and did not get answered. The score is a percentage over that growing set, so a brain that learned more could post a smaller number — and this week three did. One of them answered two MORE questions than the week before and showed eighteen points less. Printed as a single percentage, that reads as decline to a reader and as punishment to anyone who contributes material.all news →
mozg.beta
Sign in

Expo · Router · all subjects

custom-tabs

36 notes, read out of this brain and free to use. Each one was extracted from a source and is re-checked against its exam.

Dynamic routes with TabTrigger

Dynamic routes are allowed in TabTrigger and can be provided with values via the href prop. For example, <TabTrigger name="dynamic page" href="/hello-world" /> will create a tab for a [slug].tsx dynamic route with params { slug: 'hello-world' }. This is useful for displaying an arbitrary number of tabs based on end-user data.

Ambiguous routes in TabTrigger href

The href values provided to TabTrigger must always point to a single route. If a route exists in multiple shared groups (e.g., /(one,two)/route.tsx), using href="/route" is not allowed as it is ambiguous. Instead, specify the route group within the href, such as href="/(one)/route".

Custom tabs components overview

Expo Router offers unstyled, flexible custom tab components via the `expo-router/ui` submodule. There are four main components: Tabs (wrapper containing the View for tabs), TabList (containing View for list of TabTrigger components), TabTrigger (trigger component to switch to specified tab using href prop and name), and TabSlot (slot to render currently selected tab).

Minimum custom tabs structure

A bare minimum custom tab layout consists of a TabList (containing TabTrigger components for each tab) and a TabSlot, all within the Tabs component. Example: <Tabs><TabSlot /><TabList><TabTrigger name="home" href="/"><Text>Home</Text></TabTrigger></TabList></Tabs>

TabTrigger required props in TabList

A TabTrigger within a TabList must include both a name prop (any string, user-defined name for the tab) and an href prop (the route path).

Nested routes with TabTrigger

A TabTrigger can link to deeply nested routes. For example, <TabTrigger name="route" href="/route" /> can show a route nested in multiple stack layouts like (stack-one)/(stack-two)/route.tsx. This navigation is similar to a deep link and will be controlled by that route's parent navigator.

TabSlot placement and nesting

The TabSlot component renders the current route. TabSlot can be nested inside other components within Tabs, but cannot be within the TabList.

Switching tabs without navigation

To switch tabs without performing any navigation action (which would change the URL), use TabTrigger instead of Link or imperative APIs. TabTrigger is an unstyled View that switches tabs when pressed without navigation side effects.

TabTrigger reset prop options

The reset prop on TabTrigger controls when a tab resets its navigation state. Options are: always (always reset), onLongPress (reset on long press), and never (never reset). This is useful for stack navigators nested inside a tab, e.g., <TabTrigger name="home" reset="always" /> returns user to index route.

TabTrigger dual role in TabList vs outside

When TabTrigger is a child of TabList, it defines available routes and must include name and href props. When TabTrigger is outside TabList, it performs the same action as the TabTrigger in TabList with the same name prop, without requiring href. All TabTriggers must be descendants of the Tabs component to work.

Custom tab components rendering

All custom tab components render unstyled as a View, except TabTrigger which renders as a Pressable. You can provide a custom style prop to customize appearance. Use the asChild prop to override the underlying component and act as a slot forwarding props to immediate child.

Multiple tab bars implementation

You can create multiple tab bars by hiding TabList (e.g., style={{ display: 'none' }}) and constructing custom tab bars using TabTrigger components outside TabList. TabTrigger components outside TabList do not need the href prop.

TabTrigger isFocused prop forwarding

TabTrigger forwards an isFocused prop, allowing you to create a custom tab button component that reacts to focused status. This can be used for styling tabs based on whether they are currently active.

Custom tab button with isFocused example

Example TabButton component using isFocused prop: import FontAwesome from '@expo/vector-icons/FontAwesome'; import { TabTriggerSlotProps } from 'expo-router/ui'; import { ComponentProps, Ref } from 'react'; import { Text, Pressable, View } from 'react-native'; type Icon = ComponentProps<typeof FontAwesome>['name']; export type TabButtonProps = TabTriggerSlotProps & { icon?: Icon; ref: Ref<View>; }; export function TabButton({ icon, children, isFocused, ...props }: TabButtonProps) { return ( <Pressable {...props} style={[ { display: 'flex', justifyContent: 'space-between', alignItems: 'center', flexDirection: 'column', gap: 5, padding: 10, }, isFocused ? { backgroundColor: 'white' } : undefined, ]}> <FontAwesome name={icon} /> <Text style={[{ fontSize: 16 }, isFocused ? { color: 'white' } : undefined]}>{children}</Text> </Pressable> ); }

Hooks for custom tab control

All custom tab components have hook versions providing control over the render tree. Using hooks is considered advanced usage; for most use cases, components with asChild prop should provide sufficient control. If developing custom TabTrigger, you may need custom TabList using useTabsWithChildren().

TabSlot renderFn property

TabSlot accepts a renderFn property to override how screens are rendered, enabling advanced functionality such as animations or persisting/unmounting screens.

Multiple tabs for same route

To create multiple tabs for the same route, add the route to a shared group (e.g., (movie,tv)/[id].tsx) and create a separate TabTrigger for each group variant.

Hide tab by not rendering TabTrigger

To hide a tab, do not render the corresponding TabTrigger. This removes that tab and its navigation state from the app.

Animated tabs with TabSlot

To create animated tabs, provide a custom renderer to TabSlot using the renderFn property to customize screen rendering. Use this to detect when screen is focused and animate appropriately.

Relative href behavior in TabTrigger

A TabTrigger with relative href is relative to the local pathname where Tabs was rendered, not to the currently displayed route. For example, <TabTrigger href="./profile" /> in /directory/_layout.tsx resolves to /directory/profile even when /directory/page is showing. Expo recommends against using relative hrefs in TabTrigger.

Custom tabs experimental status

Custom tab layouts are an experimental feature in Expo Router.

Tabs.Protected for protected routes in tab navigator

Protected routes are available for Tabs navigator using Tabs.Protected with the guard prop. Wrap tab screens in Tabs.Protected to conditionally show or hide tabs based on authentication state.

File structure for JavaScript tabs

To create a tabs layout using file-based routing, use the following directory structure: src/app/_layout.tsx (root layout), src/app/(tabs)/_layout.tsx (tabs layout file), src/app/(tabs)/index.tsx (first tab), src/app/(tabs)/settings.tsx (additional tabs). The (tabs) directory is a special directory name that tells Expo Router to use the Tabs layout.

Root layout configuration for tabs

In the root src/app/_layout.tsx file, import Stack from 'expo-router' and render a Stack component with a Stack.Screen for the (tabs) group with the option headerShown set to false to hide the root header.

Tabs layout file structure

The (tabs)/_layout.tsx file is the main layout file for the tab bar and each tab. It defines how the tab bar and each tab button look and behave. Import Tabs from 'expo-router' and render a Tabs component with Tabs.Screen children for each tab. screenOptions prop applies options to all tabs, while individual Tabs.Screen options props apply to specific tabs.

Tabs.Screen options for tab configuration

Each Tabs.Screen component accepts a name prop (matching the file name) and an options prop. Common options include: title (the label shown in the tab bar), tabBarIcon (a function receiving {color} parameter that returns an icon component for that tab), and href (to customize the link behavior or hide the tab).

Tab file as default tab

The tab file named index.tsx is the default tab that loads when the app starts. Additional tabs are created by adding other files to the (tabs) directory.

JavaScript tabs extends React Navigation Bottom Tabs Navigator

The JavaScript tabs in Expo Router extend the Bottom Tabs Navigator from React Navigation. The specific APIs available depend on your versions. For example, Expo Router v6 extends Bottom Tabs Navigator v7. You can use the same configuration props to customize the bottom tab bar and individual tabs after checking version compatibility.

Hiding a tab from the tab bar

To make a route exist but not show up in the tab bar, pass href: null in the Tabs.Screen options. This disables the tab button while keeping the route accessible.

Dynamic routes in tab bar

You can use a dynamic route in a tab bar. For example, a [user] tab that shows a user's profile. Use the href option to link to a specific user's profile. You can pass a string href like '/evanbacon' or an href object with pathname and params properties. When adding a dynamic route in your tab layout, the dynamic route must be unique—you cannot have two screens for the same dynamic route. If you need multiple dynamic routes, create a custom navigator.

Dynamic route href options

When configuring a dynamic route in a tab's options, you can use href in two ways: 1) href: '/evanbacon' (string form), or 2) href: { pathname: '/[user]', params: { user: 'evanbacon' } } (object form with pathname and params).

JavaScript tabs example code

Example of a complete JavaScript tabs layout: ```tsx // src/app/_layout.tsx import { Stack } from 'expo-router'; export default function Layout() { return ( <Stack> <Stack.Screen name="(tabs)" options={{ headerShown: false }} /> </Stack> ); } // src/app/(tabs)/_layout.tsx import FontAwesome from '@expo/vector-icons/FontAwesome'; import { Tabs } from 'expo-router'; export default function TabLayout() { return ( <Tabs screenOptions={{ tabBarActiveTintColor: 'blue' }}> <Tabs.Screen name="index" options={{ title: 'Home', tabBarIcon: ({ color }) => <FontAwesome size={28} name="home" color={color} />, }} /> <Tabs.Screen name="settings" options={{ title: 'Settings', tabBarIcon: ({ color }) => <FontAwesome size={28} name="cog" color={color} />, }} /> </Tabs> ); } // src/app/(tabs)/index.tsx & src/app/(tabs)/settings.tsx import { View, Text, StyleSheet } from 'react-native'; export default function Tab() { return ( <View style={styles.container}> <Text>Tab [Home|Settings]</Text> </View> ); } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', }, }); ```

JavaScript tabs layout in Expo Router

Expo Router provides a JavaScript tabs layout implemented with React Navigation's bottom tabs navigator. It offers a familiar API to those who have already used React Navigation. JavaScript tabs is one of three tab navigator options available in Expo Router, alongside native tabs and custom tabs.

JavaScript Tabs component in layout

You can implement a JavaScript-based tab navigator using the Tabs component from 'expo-router'. All routes directly inside that directory become tabs. Use Tabs.Screen components to define each tab with a name prop matching the route file. You can set options like title and tabBarIcon. The index route becomes the default selected tab. The Tabs component uses React Navigation's native bottom tabs navigator.

JavaScript Tabs layout example

Example of a tabs layout: import { Tabs } from 'expo-router'; import MaterialIcons from '@expo/vector-icons/MaterialIcons'; export default function TabLayout() { return ( <Tabs> <Tabs.Screen name="index" options={{ title: 'Home', tabBarIcon: ({ color }) => <MaterialIcons size={28} name="house.fill" color={color} />, }} /> <Tabs.Screen name="feed" options={{ title: 'Feed' }} /> <Tabs.Screen name="profile" options={{ title: 'Profile' }} /> </Tabs> ); }

Custom tabs using expo-router/ui components

On web, use custom tabs from 'expo-router/ui' with components: Tabs (root), TabSlot (placeholder for current route), TabList (container for tab triggers), and TabTrigger (individual tab). These are unstyled and flexible components. Example: <Tabs><TabSlot /><TabList><TabTrigger name="index" href="/">Home</TabTrigger><TabTrigger name="explore" href="/explore">Explore</TabTrigger></TabList></Tabs>

Give your agent this brain