Navigating through Expo with Router v3 tabs, incorporating stack navigation, search functionality, and showcasing prominent titles

I've been working on designing a navigation header similar to the Apple Contacts app, with a large title and search function, but only for the Home Screen. All other tabs should have their own unique settings, like different titles or hidden navigation bars. Although I've built what I believe is a solid foundation, I'm facing challenges in getting the search and large title settings to work properly. You can find the reproducible repository here.

My overall project structure has an entry point that directs to /apps/_layout.tsx:

import { Stack } from 'expo-router';

export default function Layout() {
  return (
    <Stack screenOptions={{ headerShown: false }}>
      <Stack.Screen name="(tabs)" />
    </Stack>
  );
}

The main layout leads to the first stack, which is a tab view containing the main app. It's located at /apps/(tabs)/_layout.tsx and looks like this:

import { BlurView } from 'expo-blur';
import { Tabs } from 'expo-router';
import { SafeAreaProvider } from 'react-native-safe-area-context';

export default function TabsLayout() {
  return (
    <SafeAreaProvider>
      <Tabs
        screenOptions={{
          tabBarStyle: { position: 'absolute', elevation: 0 },
          tabBarBackground: () =>
            <BlurView tint="prominent" intensity={100} style={StyleSheet.absoluteFill} />
        }}>
        <Tabs.Screen name="index" />
        <Tabs.Screen name="tab2" />
      </Tabs>
    </SafeAreaProvider>
  );
}

The first tab is the index page located at /apps/(tabs)/index.tsx and it appears as follows:

import { BottomTabBarHeightContext, useBottomTabBarHeight } from '@react-navigation/bottom-tabs';
import { useHeaderHeight } from '@react-navigation/elements';
import { BlurView } from 'expo-blur';
import { Stack } from 'expo-router';
import { ScrollView } from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { Text, StyleSheet } from 'react-native';

export default function Home() {
  const items = Array.from({ length: 60 }, (_, index) => index);
  const headerHeight = useHeaderHeight();
  const bottomTabBarHeight = useBottomTabBarHeight();

  return (
    <ScrollView style={{ flex: 1, backgroundColor: 'blue' }}>
      <Stack.Screen
        options={{
          headerShown: true,
          headerTransparent: true,
          headerBackground: () =>
            <BlurView tint="prominent" intensity={100} style={StyleSheet.absoluteFill} />
        }}
      />
      <SafeAreaView
          edges={['left', 'right']}
          style={{
            flex: 1,
            backgroundColor: 'green',
            paddingTop: headerHeight,
            paddingBottom: bottomTabBarHeight
          }}
      >
        {items.map((item, index) => (
          <Text key={index} style={{...}}>{`Item ${item}`}</Text>
        ))}
      </SafeAreaView>
    </ScrollView>
  );
}

This setup provides a solid native foundation where content extends under the header and bottom tabs, allowing the content to scroll beneath them.

https://i.stack.imgur.com/gJGNE.gif

It even functions well in landscape orientation, which is promising:

https://i.stack.imgur.com/1T4xH.gif

Now, my goal is to implement a large title and add a search bar in the navigation bar, similar to the Apple Contacts app:

https://i.stack.imgur.com/iIcTC.gif

I've tried adding the following code snippets in different areas, but they don't seem to have any effect:

headerLargeTitle: true,
headerSearchBarOptions: {
  placeholder: 'Search'
}

The documentation on this topic is limited, and the examples provided are either outdated or not relevant to the file-based Expo Router v3. As it differs from React Navigation, the documentation for React Navigation doesn't align with Expo Router's usage. Could it be that my project structure is incorrect? Any assistance or insight would be greatly appreciated!

Answer №1

It appears that the Expo Router may have some limitations as it is relatively new, especially in terms of incomplete documentation. To address this, consider restructuring the layout so that the stacks are within the tabs instead of having a tab within a stack:

<SafeAreaProvider>
<NavigationContainer>
    <Tab.Navigator
    screenOptions={{
        headerShown: false,
        ...(Platform.OS === 'ios'
        ? {
            tabBarStyle: { position: 'absolute', elevation: 0 },
            tabBarBackground: () => (
                <BlurView tint="prominent" intensity={100} style={StyleSheet.absoluteFill} />
            )
            }
        : undefined)
    }}
    >
    <Tab.Screen name="HomeTab" component={HomeStack} />
    <Tab.Screen name="SettingsTab" component={SettingsStack} />
    <Tab.Screen name="MoreTab" component={MoreStack} />
    </Tab.Navigator>
</NavigationContainer>
</SafeAreaProvider>
const Stack = createNativeStackNavigator();

export function Home({ navigation }: NativeStackScreenProps<ParamListBase>) {
  const data = Array.from({ length: 50 });

  useLayoutEffect(() => {
    navigation.setOptions({
      headerSearchBarOptions: {
        onChangeText: (text) => console.log(text)
      }
    });
  }, [navigation]);

  return (
    <View style={{ flex: 1, backgroundColor: 'yellow' }}>
      <Text>Home screen</Text>
      <Button title="Go to Details" onPress={() => navigation.navigate('Details')} />
      {data.map((_, index) => (
        <Text key={index} style={{ padding: 10, fontSize: 18, fontWeight: 'bold', color: 'blue' }}>
          Item {index + 1}
        </Text>
      ))}
    </View>
  );
}

export function HomeStack() {
  return (
    <Stack.Navigator
      screenOptions={{
        headerTransparent: Platform.OS === 'ios',
        headerBlurEffect: 'systemThickMaterial'
      }}
    >
      <Stack.Screen name="Home" component={withScrollStackView(Home)} options={{ headerLargeTitle: true }} />
      <Stack.Screen name="Details" component={withScrollStackView(Details)} />
    </Stack.Navigator>
  );
}
export default function Details() {
  return (
    <View style={{ flex: 1, backgroundColor: 'yellow' }}>
      <Text>Details screen</Text>
    </View>
  );
}

For more information, you can refer directly to the React Navigation documentation, which offers guidance on utilizing stack navigators within each tab: https://reactnavigation.org/docs/tab-based-navigation#a-stack-navigator-for-each-tab

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The absence of a 'body' argument in the send() json() method within the Next.js API, coupled with TypeScript, raises an important argument

Currently, I have set up an API route within Next.js. Within the 'api' directory, my 'file.tsx' consists of the following code: import type { NextApiRequest, NextApiResponse } from "next"; const someFunction = (req: NextApiReq ...

Attaching dynamic data to a specific element within an array

I have successfully created a demo where elements can be dropped into a specific area and their top and left values are displayed. I have also added functionality to remove dropped items and move them between different blocks. However, I am encountering so ...

Unexpected behavior with Angular 10 behavior subject - encountering null value after invoking .next(value) function

Can anyone help me solve the mystery of why my user value turns null after I log in? This is the login page where an API is called and the result is obtained as shown below: https://i.stack.imgur.com/kDjSy.png Here is the authentication service implemen ...

The cache does not contain '.chunk-`X`' as requested in Next.js error

Hello everyone, I've encountered a recent issue with my code that previously worked fine. I was using getStaticProps with a cache time of 5 days, but now I'm facing an error related to the title. Here is a more detailed look at the error: error ...

Unexpectedly, a significant ngrx createEffect leads to an unusual error following an update, but the issue vanishes when certain code snippets like tap or filter are disabled

I have been in the process of upgrading a massive Angular 12 project to Angular 13 and have completed several steps. One significant change was the rewriting of Effects using a newer approach like createEffect(() => instead of @Effect However, during ...

Tips for modifying the width of the mat-header-cell in Angular

Is there a way to customize the mat-header-cell in Angular? I've been trying to change its width without success. Any suggestions would be greatly appreciated. <ng-container cdkColumnDef="name"> <mat-header-cell *cdkHeaderCellDe ...

What function does the ng-template serve when encapsulated within an ng-select component?

Upon observing various instances of ng-select, I've noticed that it often involves wrapping a ng-template, as exemplified below: <ng-select [items]="cities" [(ngModel)]="selectedCity" bindLabel="name" bindV ...

I'm looking to learn how to implement the delete method in an API using TypeScript. Can anyone help me out

I am seeking guidance on utilizing 'axios' within 'nuxt.js'. I have experimented with sample data, and I am particularly interested in learning how to utilize the 'axios' method within 'nuxt.js' using TypeScript. T ...

Attempting to incorporate an npm package (specifically Howler) into an Angular 2 application

I'm facing an issue with importing Howler into my Angular 2 app as it doesn't have a typings file. Despite my efforts in searching for a solution, I haven't been able to find anything helpful. Can someone guide me on how to import "howler" i ...

Issue with Figma React plugin's PostMessage functionality not behaving as anticipated

I am currently working on developing a plugin for Figma, following the react example provided on their GitHub page: https://github.com/figma/plugin-samples/tree/master/react One of the functionalities I have implemented is a button that triggers a specifi ...

The Intersection of Material-UI, TypeScript, and Powerful Autocomplete Features

In my TypeScript project, I'm attempting to develop a Material-UI AutoComplete component that retrieves the input value based on an object's property name -> obj[key] However, when using the prop getOptionLabel, I encountered the following er ...

Include scrollView on smaller screens based on conditions

While incorporating an overlay in my application, how can I integrate a ScrollView specifically for smaller devices? Initially, I am checking the width: const windowWidth = Dimensions.get("window").width; Subsequently, I am attempting to display the Scro ...

Unable to locate dependencies while testing the react package locally

Recently, I came across this npm package designed for React using Typescript. To debug it locally, I initiated npm link in a new React project but encountered an error: I suspect it may not be reading the packages correctly, but I'm unsure how to re ...

Develop an enhancement for the Date object in Angular 2 using Typescript

Using the built-in Date type, I can easily call date.getDate(), date.getMonth()...etc. However, I am looking for a way to create a custom function like date.myCustomFunctionToGetMonthInString(date) that would return the month in a string format such as &a ...

Please locate and return the requested result with the specified ID

Currently, I am extracting user data from my iOS app (me/friends, me/events, ...). I need to send this JSON to a server for further data processing. The issue that I am facing is that when I receive a response for my me/events object, it comes without any ...

Autofill feature for input fields in React Native

Hey there, I've been pondering whether it's possible to achieve a certain functionality using React Native. Specifically, I'm interested in displaying a user's predetermined phone number contact information above the keyboard when focus ...

Angular 2: Utilizing Http Subscribe Method with "this" Context Pointer

Question: http.request('js/app/config/config.json').subscribe(data => { this.url = data.json().url; }); It seems that "this" is pointing to Subscriber instead of the parent class. I was under the impression that the fat- ...

Best Practices for Displaying Videos in Ionic 2

Can videos be properly integrated into Ionic pages? I'm encountering an issue where the buttons become unusable in fullscreen mode when using the html 5 video element. <video id="video1" width="100%" preload="metadata" controls webkit-playsinline& ...

Exploring the MVVM architecture in React and the common warning about a missing dependency in the useEffect hook

I'm currently in the process of developing a React application using a View/ViewModel architecture. In this setup, the viewModel takes on the responsibility of fetching data and providing data along with getter functions to the View. export default f ...

Tips for sending the image file path to a React component

Hey, I'm working on a component that has the following structure: import React from "react"; interface CInterface { name: string; word: string; path: string; } export function C({ name, word, path }: CInterface) { return ( < ...