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

I am attempting to code a program but it keeps displaying errors

What is hierarchical inheritance in AngularJS? I have been attempting to implement it, but I keep encountering errors. import {SecondcomponentComponent} from './secondcomponent/secondcomponent.Component'; import {thirdcomponentcomponent} from & ...

Issue with vue-class-component: encountering TS2339 error when trying to call a method within

My vuejs application is being built using vue-cli-service. After a successful build, I encountered TS2339 errors in my webstorm IDE: Test.vue: <template> <div>{{method()}}</div> </template> <script lang="ts"> impor ...

What is the proper way to include special symbols such as "++" and "#" in a request?

I am facing an issue while trying to make a request to an ASP .NET CORE API from an Angular application using Typescript. Upon sending the request, the API searches in an SQL database for any rows with the specified value. The problem arises when attempt ...

Encountering a Npm ERR! when deploying Angular 6 to Heroku due to missing Start script

I am experiencing an issue with my simple angular 6 app after deploying it to Heroku. When I check the logs using the command heroku logs, I encounter the following error: 2018-07-15T00:45:51.000000+00:00 app[api]: Build succeeded 2018-07-15T00:45:53.9012 ...

What is the best way to transfer a variable between components in Angular without using the HTML page, directly within the components themselves?

Within the child component, I am working with a string: @Input() helloMessage:string; I am looking to assign a value to this string from another string in the parent component and utilize it within the child component without displaying the value in the h ...

I am looking to enhance my array of objects by implementing a filter. It is important that the filter does not allow for duplicate checkboxes with the

My website : On the left-hand side of my webpage, there is a set of checkboxes with some repeated names that I don't want. For example, "Rice" is repeated twice but I only want it to display once. When checking the Rice checkbox, it should show all c ...

Reasons why a functional component may not trigger a rerender after a state change using useReducer()

When using react Hooks, specifically useReducer, I found that although the state changes, the functional component does not rerender. Additionally, when trying to open the drawer by pressing a button in the menu, even though the state changes the drawer re ...

Angular Material Popup - Interactive Map from AGM

I am in the process of developing a material dialog to collect user location information using AGM (angular google maps). I have successfully implemented a map on my main page, but when the dialog is opened, it only shows a blank space instead of the map. ...

failure to render updated content after modification of variable

I am facing an issue with triggering a function in the component: componentA.ts html = 'hey'; this.onElementSelected(r => this.change()); public change() { console.log(this.html); if (this.html === 'hey&ap ...

Input for uncomplicated changing identifier

I am looking to create types for dynamic keys that will be of type number. I have two similar types defined as follows: type UseCalculatePayments = () => { totalPayments: number; aggregate: number; condition: boolean; }; type UseCalculateCommissio ...

Updating UIImageView consumes high memory resources

I am currently dealing with a code block that is executed approximately every 20ms. This block serves as a rendering callback method for an audio unit of Core Audio, performing calculations and updating the image of a UIImageView in the process. To ensure ...

I encounter an issue when trying to declare an enum in TypeScript

At line 26 in my typescript file, the code snippet below shows an enum definition: export enum ItemType { Case = 'Case', Study = 'Study', Project = 'Project', Item = 'Item', } I am currently using Visual Stu ...

Guide on building a Vue3 component with TypeScript, Pug Template engine, and Composition API

Whenever I try to export components within <script setup lang="ts"> and then use them in <template lang="pug">, an error is thrown stating that the component is defined but never used. Here's an example: <template la ...

Getting a date object that is three months prior to the current date in Typescript

I need to retrieve the date object that is 3 months before the current date by using the following code snippet: toDate = new Date(); fromDate = this.toDate.getMonth() - 3; The issue I am facing is that the variable fromDate only contains a number, but I ...

When Typescript and React Native Imports Clash in an Express App, Resolving the Conflict of "npm run build" Command

When running my Express app using 'npm run serve', defined as 'npm run build && node lib/index.js' in the package.json scripts serve section, I encounter multiple duplicate declaration errors after installing React Native for a ...

When initially compiling Angular 5, an error (TS2339) may occur, but after a successful compilation, everything runs smoothly

In a unique scenario, I wrote code that fetches information from an API server without knowing the structure of the response fields. Once I receive the response, I need to create a form for updating the data and sending it back. To handle unknown ngModel p ...

Adding an object to an ArrayList: A step-by-step guide

I'm encountering issues with adding objects to the list. I have a list of floors, each floor containing rooms. I can successfully add a floor, but I'm unsure how to add rooms to the floor list. I've attempted to access floor[index] or id, b ...

Creating a cohesive "notification" feature in React with TypeScript that is integrated with one specific component

I am currently working on designing my application to streamline all notifications through a single "snackbar" style component (utilizing the material UI snackbar component) that encompasses the entire app: For instance class App extends React.Component ...

Sometimes the downloaded xlsx file may become corrupted

Currently, I am working on developing a project using Angular4 with Typescript. One of the tasks involved creating a blob utilizing the XLSX-populate library. Below is an example showing the code snippet for generating a valid xlsx object: var url = wind ...

Using mergeMap in conjunction with retryWhen allows for the resumption of retries from the exact point of failure, without needing

I have a list of URLs, the number of which is unknown until it stops (depending on some condition). This is how I am currently using them: from(observableUrls) .pipe( mergeMap(url => callHttpService(url) , 4), retryWhen( // Looking f ...