Skip to content

Part 2: Cleanup screens

Tutorial

Before we start building our own features, let’s adapt the default Expo Router template. The starter project includes its own theming and styling logic, which we’ll replace with the more powerful Unistyles approach.

App folder

Let’s start with the root layout file for the entire application.

  • Directoryapp/
    • Directory(tabs)/
    • _layout.tsx
    • +not_found.tsx

This file sets up the root Stack navigator and uses React Navigation’s ThemeProvider along with a useColorScheme hook. We no longer need these, as Unistyles will now manage the app’s theme state globally.

Let’s remove the old theming logic:

app/_layout.tsx
import React from 'react';
import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native';
import { useFonts } from 'expo-font';
import { Stack } from 'expo-router';
import { StatusBar } from 'expo-status-bar';
import 'react-native-reanimated';
import { useColorScheme } from '@/hooks/useColorScheme';
export default function RootLayout() {
const colorScheme = useColorScheme();
const [loaded] = useFonts({
SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'),
});
if (!loaded) {
// Async font loading only occurs in development.
return null;
}
return (
<React.Fragment>
<ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="+not-found" />
</Stack>
<StatusBar style="auto" />
</ThemeProvider>
</React.Fragment>
);
}

Next, for the +not_found.tsx file, we only need to swap the StyleSheet import to use Unistyles:

app/+not_found.tsx
import { StyleSheet } from 'react-native-unistyles';
import { StyleSheet } from 'react-native';

(tabs) folder

This folder contains the layout and screens for your TabsNavigator. For the index.tsx and explore.tsx files, the process is the same: we simply need to replace the standard StyleSheet import with the one from Unistyles.

app/(tabs)/index.tsx
import { Platform, StyleSheet } from 'react-native';
import { StyleSheet } from 'react-native-unistyles';

For the JSX, we won’t need all these boilerplate components, so we can keep it as simple as possible:

app/(tabs)/index.tsx
import { StyleSheet } from 'react-native-unistyles';
import { ThemedText } from '@/components/ThemedText';
import { ThemedView } from '@/components/ThemedView';
export default function HomeScreen() {
return (
<ThemedView style={styles.container}>
<ThemedText type="title">
Home Screen
</ThemedText>
</ThemedView>
);
}
const styles = StyleSheet.create(theme => ({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
}));

Repeat the same steps for the (tabs)/explore.tsx file.

app/(tabs)/explore.tsx
import { StyleSheet } from 'react-native-unistyles';
import { ThemedText } from '@/components/ThemedText';
import { ThemedView } from '@/components/ThemedView';
export default function TabTwoScreen() {
return (
<ThemedView style={styles.container}>
<ThemedText type="title">
Explore Screen
</ThemedText>
</ThemedView>
);
}
const styles = StyleSheet.create(theme => ({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
}));

The most interesting file here is _layout.tsx, which configures the Tabs navigator. The default code uses the useColorScheme hook to dynamically set the tabBarActiveTintColor. Since @react-navigation components aren’t aware of the Unistyles C++ core, they can’t be updated automatically. We need a way to get the current theme data into our component and trigger a re-render when the theme changes. This is the perfect use case for the useUnistyles hook. It subscribes the component to theme changes, giving you access to the theme object and ensuring the component re-renders when the theme is updated.

Let’s refactor tab layout to use our new theme:

app/(tabs)/_layout.tsx
import { Tabs } from 'expo-router';
import React from 'react';
import { Platform } from 'react-native';
import { HapticTab } from '@/components/HapticTab';
import { IconSymbol } from '@/components/ui/IconSymbol';
import TabBarBackground from '@/components/ui/TabBarBackground';
import { Colors } from '@/constants/Colors';
import { useColorScheme } from '@/hooks/useColorScheme';
import { useUnistyles } from 'react-native-unistyles';
export default function TabLayout() {
const colorScheme = useColorScheme();
const { theme } = useUnistyles();
return (
<Tabs
screenOptions={{
tabBarActiveTintColor: Colors[colorScheme ?? 'light'].tint,
tabBarInactiveTintColor: theme.colors.tint,
tabBarActiveTintColor: theme.colors.activeTint,
sceneStyle: {
backgroundColor: theme.colors.background
},
tabBarStyle: {
backgroundColor: theme.colors.foreground
},
headerShown: false,
tabBarButton: HapticTab,
tabBarBackground: TabBarBackground,
tabBarStyle: Platform.select({
ios: {
// Use a transparent background on iOS to show the blur effect
position: 'absolute',
},
default: {},
}),
}}>
<Tabs.Screen
name="index"
options={{
title: 'Home',
tabBarIcon: ({ color }) => <IconSymbol size={28} name="house.fill" color={color} />,
}}
/>
<Tabs.Screen
name="explore"
options={{
title: 'Explore',
tabBarIcon: ({ color }) => <IconSymbol size={28} name="paperplane.fill" color={color} />,
}}
/>
</Tabs>
);
}

With these changes, you can now switch your device’s color scheme, and you’ll see the tab bar’s tint color update instantly, powered by Unistyles!

ios app preview