Capstone: Building a Complete App
Time to put everything together. We'll build a note-taking app with a complete navigation structure: authentication, a drawer for top-level navigation, tabs for main sections, stacks for drill-down, and a modal for creating new notes. This combines every concept from the course into one working app.
The Navigation Structure
Auth check
├── Login (when signed out)
└── Drawer (when signed in)
├── Main Tabs
│ ├── Notes Stack
│ │ ├── NotesList
│ │ └── NoteDetail
│ └── Profile
└── About
Modal: CreateNote (slides up over everything)
Type Definitions
Start with the TypeScript param lists for every navigator:
type AuthStackParamList = {
Login: undefined;
};
type NotesStackParamList = {
NotesList: undefined;
NoteDetail: { noteId: string; title: string };
};
type TabParamList = {
NotesTab: undefined;
Profile: undefined;
};
type DrawerParamList = {
MainTabs: undefined;
About: undefined;
};
type RootStackParamList = {
Main: undefined;
CreateNote: undefined;
};
Screen Components
The individual screens are intentionally simple — the focus is on the navigation wiring:
import React from 'react';
import { View, Text, Button, FlatList, TouchableOpacity, TextInput, StyleSheet } from 'react-native';
const AuthContext = React.createContext<{
signIn: () => void;
signOut: () => void;
} | null>(null);
function useAuth() {
const ctx = React.useContext(AuthContext);
if (!ctx) throw new Error('useAuth must be inside AuthProvider');
return ctx;
}
const NOTES = [
{ id: '1', title: 'Shopping List', body: 'Milk, eggs, bread' },
{ id: '2', title: 'Meeting Notes', body: 'Discuss Q2 roadmap' },
{ id: '3', title: 'Ideas', body: 'Build a note-taking app' },
];
function LoginScreen() {
const { signIn } = useAuth();
return (
<View style={styles.center}>
<Text style={styles.heading}>Notes App</Text>
<Text style={styles.subtitle}>Sign in to get started</Text>
<Button title="Sign In" onPress={signIn} />
</View>
);
}
function NotesListScreen({ navigation }) {
return (
<View style={{ flex: 1 }}>
<FlatList
data={NOTES}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<TouchableOpacity
style={styles.noteItem}
onPress={() => navigation.navigate('NoteDetail', { noteId: item.id, title: item.title })}
>
<Text style={styles.noteTitle}>{item.title}</Text>
<Text style={styles.noteBody} numberOfLines={1}>{item.body}</Text>
</TouchableOpacity>
)}
/>
<View style={styles.fab}>
<Button title="+ New Note" onPress={() => navigation.navigate('CreateNote')} />
</View>
</View>
);
}
function NoteDetailScreen({ route }) {
const note = NOTES.find((n) => n.id === route.params.noteId);
return (
<View style={styles.center}>
<Text style={styles.heading}>{note?.title}</Text>
<Text style={styles.body}>{note?.body}</Text>
</View>
);
}
function ProfileScreen() {
const { signOut } = useAuth();
return (
<View style={styles.center}>
<Text style={styles.heading}>Profile</Text>
<Text style={styles.subtitle}>Jason Brown</Text>
<Button title="Sign Out" onPress={signOut} />
</View>
);
}
function AboutScreen() {
return (
<View style={styles.center}>
<Text style={styles.heading}>About</Text>
<Text style={styles.body}>Notes App v1.0 — Built with React Navigation v7</Text>
</View>
);
}
function CreateNoteScreen({ navigation }) {
const [title, setTitle] = React.useState('');
return (
<View style={styles.modal}>
<TextInput
style={styles.input}
placeholder="Note title"
value={title}
onChangeText={setTitle}
autoFocus
/>
<Button
title="Save"
onPress={() => {
console.log('Created note:', title);
navigation.goBack();
}}
/>
</View>
);
}
Wiring the Navigators
Now assemble them from the inside out — Notes Stack, then Tabs, then Drawer, then the Root Stack with auth:
import { NavigationContainer } from '@react-navigation/native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
import { createDrawerNavigator, DrawerContentScrollView, DrawerItemList, DrawerItem } from '@react-navigation/drawer';
import { Ionicons } from '@expo/vector-icons';
const RootStack = createNativeStackNavigator();
const AuthStack = createNativeStackNavigator();
const NotesStack = createNativeStackNavigator();
const Tab = createBottomTabNavigator();
const Drawer = createDrawerNavigator();
function NotesStackNavigator() {
return (
<NotesStack.Navigator>
<NotesStack.Screen name="NotesList" component={NotesListScreen} options={{ title: 'Notes' }} />
<NotesStack.Screen
name="NoteDetail"
component={NoteDetailScreen}
options={({ route }) => ({ title: route.params.title })}
/>
</NotesStack.Navigator>
);
}
function MainTabs() {
return (
<Tab.Navigator
screenOptions={({ route }) => ({
headerShown: false,
tabBarIcon: ({ color, size }) => {
const iconName = route.name === 'NotesTab' ? 'document-text-outline' : 'person-outline';
return <Ionicons name={iconName} size={size} color={color} />;
},
})}
>
<Tab.Screen name="NotesTab" component={NotesStackNavigator} options={{ title: 'Notes' }} />
<Tab.Screen name="Profile" component={ProfileScreen} />
</Tab.Navigator>
);
}
function CustomDrawerContent(props) {
return (
<DrawerContentScrollView {...props}>
<View style={drawerStyles.header}>
<Text style={drawerStyles.name}>Notes App</Text>
</View>
<DrawerItemList {...props} />
</DrawerContentScrollView>
);
}
function MainDrawer() {
return (
<Drawer.Navigator
drawerContent={(props) => <CustomDrawerContent {...props} />}
screenOptions={({ route }) => ({
drawerIcon: ({ color, size }) => {
const iconName = route.name === 'MainTabs' ? 'home-outline' : 'information-circle-outline';
return <Ionicons name={iconName} size={size} color={color} />;
},
})}
>
<Drawer.Screen name="MainTabs" component={MainTabs} options={{ title: 'Home' }} />
<Drawer.Screen name="About" component={AboutScreen} />
</Drawer.Navigator>
);
}
The Root — Auth + Modal
The top-level navigator handles authentication and the modal:
export default function App() {
const [isSignedIn, setIsSignedIn] = React.useState(false);
const authContext = React.useMemo(() => ({
signIn: () => setIsSignedIn(true),
signOut: () => setIsSignedIn(false),
}), []);
return (
<AuthContext.Provider value={authContext}>
<NavigationContainer>
<RootStack.Navigator>
{isSignedIn ? (
<>
<RootStack.Screen
name="Main"
component={MainDrawer}
options={{ headerShown: false }}
/>
<RootStack.Group screenOptions={{ presentation: 'modal' }}>
<RootStack.Screen
name="CreateNote"
component={CreateNoteScreen}
options={({ navigation }) => ({
title: 'New Note',
headerLeft: () => (
<Button title="Cancel" onPress={() => navigation.goBack()} />
),
})}
/>
</RootStack.Group>
</>
) : (
<RootStack.Screen
name="Login"
component={LoginScreen}
options={{ headerShown: false }}
/>
)}
</RootStack.Navigator>
</NavigationContainer>
</AuthContext.Provider>
);
}
The Styles
const styles = StyleSheet.create({
center: { flex: 1, alignItems: 'center', justifyContent: 'center', padding: 16 },
heading: { fontSize: 28, fontWeight: 'bold', marginBottom: 8 },
subtitle: { fontSize: 16, color: '#666', marginBottom: 24 },
body: { fontSize: 16, color: '#333', textAlign: 'center' },
noteItem: { padding: 16, borderBottomWidth: 1, borderBottomColor: '#eee' },
noteTitle: { fontSize: 18, fontWeight: '600' },
noteBody: { fontSize: 14, color: '#666', marginTop: 4 },
fab: { padding: 16 },
modal: { flex: 1, padding: 16, paddingTop: 24 },
input: { fontSize: 18, borderBottomWidth: 1, borderBottomColor: '#ddd', paddingVertical: 8, marginBottom: 16 },
});
const drawerStyles = StyleSheet.create({
header: { padding: 20, borderBottomWidth: 1, borderBottomColor: '#eee', marginBottom: 8 },
name: { fontSize: 20, fontWeight: 'bold' },
});
What This Demonstrates
This single app uses every concept from the course:
- Stack Navigator — Notes list to detail drill-down
- Tab Navigator — Notes and Profile tabs with icons
- Drawer Navigator — Home and About with custom drawer content
- Nesting — Stack inside Tabs inside Drawer inside Root Stack
- Authentication Flow — conditional Login vs Main screens
- Modal — CreateNote presented as a modal with cancel button
- Route Params — passing note data to detail screen
- Dynamic Headers — title set from route params
Congratulations — you now have the knowledge to build any navigation structure a React Native app needs. The React Navigation docs are an excellent reference as you continue building.