Start interactive tutorial

← Back to Projects

Movie Catalogue

Difficulty

  • intermediate

Average duration

2 hrs

Technologies

Difficulty

  • intermediate

Average duration

2 hrs

Technologies

📦 Navigation dependencies

RN Movies Preview

Create a mobile app to explore a movie catalog, view details, and navigate between categories, practicing navigation with React Navigation using Bottom Tabs and Native Stack.

The project uses local data (no API) to focus on navigation architecture, parameter typing, and screen composition.

🌱 How to start this project

  1. Clone the following template to your computer:
1https://github.com/breatheco-de/react-native-cli-hello
  1. Install JS dependencies:
1npm install
  1. (macOS/iOS only) Install CocoaPods:
1cd ios 2bundle install # optional if you use Bundler 3bundle exec pod install || pod install 4cd ..
  1. Start Metro (development server):
1npx react-native start --reset-cache
  1. Build and run the app:
  • Android
    1npm run android
  • iOS (macOS)
    1npm run ios

Install React Navigation and native dependencies (if not already in package.json):

1npm install @react-navigation/native @react-navigation/native-stack @react-navigation/bottom-tabs 2npm install react-native-screens react-native-safe-area-context

In App.tsx, wrap your navigators with NavigationContainer:

1import { NavigationContainer } from '@react-navigation/native'; 2import TabsNavigator from './src/navigation/TabsNavigator'; 3 4export default function App() { 5 return ( 6 <NavigationContainer> 7 <TabsNavigator /> 8 </NavigationContainer> 9 ); 10}

🗂️ Suggested project structure

1├─ src/ 2│ ├─ navigation/ 3│ │ ├─ TabsNavigator.tsx # Main tabs 4│ │ └─ StackNavigator.tsx # Stack for details and internal routes 5│ ├─ screens/ 6│ │ ├─ HomeScreen.tsx # Movie list 7│ │ ├─ MovieDetailScreen.tsx # Detail (receives typed params) 8│ │ ├─ CategoriesScreen.tsx # Category list 9│ │ └─ CategoryMoviesScreen.tsx # Movies filtered by category 10│ ├─ components/ 11│ │ ├─ MovieCard.tsx 12│ │ └─ Grid.tsx 13│ ├─ data/ 14│ │ └─ movies.ts # Local mock (no API) 15│ └─ types/ 16│ └─ index.ts # Shared types (Movie, Route params, etc.) 17├─ App.tsx 18├─ package.json 19└─ tsconfig.json

📝 Instructions

  1. Implement Tabs + Stack with React Navigation:

    • Create TabsNavigator.tsx for the Home, Categories, and Favorites tabs.
    • Inside the Home tab, mount a Stack that navigates to MovieDetailScreen.
  2. Create the file src/data/movies.ts with local data:

    1export type Movie = { 2 id: number; 3 title: string; 4 year: number; 5 categories: string[]; 6 rating: number; // 0–10 7}; 8 9export const movies: Movie[] = [ 10 { id: 1, title: 'Inception', year: 2010, categories: ['Sci-Fi', 'Action'], rating: 8.8 }, 11 { id: 2, title: 'Interstellar', year: 2014, categories: ['Sci-Fi', 'Drama'], rating: 8.6 }, 12 // ... 13];
  3. Use typed routes/params in the Stack:

    • Define navigation types (e.g., RootStackParamList) in src/types/index.ts.
    • When navigating to the detail, send the movie id and retrieve it in MovieDetailScreen.
  4. Do not use fetch or external APIs: everything comes from movies.ts.

src/navigation/StackNavigator.tsx

1import { createNativeStackNavigator } from '@react-navigation/native-stack'; 2import HomeScreen from '../screens/HomeScreen'; 3import MovieDetailScreen from '../screens/MovieDetailScreen'; 4import { RootStackParamList } from '../types'; 5 6const Stack = createNativeStackNavigator<RootStackParamList>(); 7 8export default function StackNavigator() { 9 return ( 10 <Stack.Navigator> 11 <Stack.Screen 12 name="Home" 13 component={HomeScreen} 14 options={{ title: 'Movies' }} 15 /> 16 <Stack.Screen 17 name="MovieDetail" 18 component={MovieDetailScreen} 19 options={({ route }) => ({ title: route.params?.title ?? 'Detail' })} 20 /> 21 </Stack.Navigator> 22 ); 23}

src/navigation/TabsNavigator.tsx

1import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; 2import StackNavigator from './StackNavigator'; 3import CategoriesScreen from '../screens/CategoriesScreen'; 4import FavoritesScreen from '../screens/FavoritesScreen'; 5 6type TabsParamList = { 7 Feed: undefined; // Will contain the Stack (Home + Detail) 8 Categories: undefined; 9 Favorites: undefined; 10}; 11 12const Tab = createBottomTabNavigator<TabsParamList>(); 13 14export default function TabsNavigator() { 15 return ( 16 <Tab.Navigator> 17 <Tab.Screen 18 name="Feed" 19 component={StackNavigator} 20 options={{ title: 'Home' }} 21 /> 22 <Tab.Screen 23 name="Categories" 24 component={CategoriesScreen} 25 /> 26 <Tab.Screen 27 name="Favorites" 28 component={FavoritesScreen} 29 /> 30 </Tab.Navigator> 31 ); 32}

src/types/index.ts

1export type RootStackParamList = { 2 Home: undefined; 3 MovieDetail: { id: number; title?: string }; 4};

🖥️ Screen skeletons (example)

src/screens/HomeScreen.tsx

1import { View, FlatList, Pressable } from 'react-native'; 2import { NativeStackScreenProps } from '@react-navigation/native-stack'; 3import { RootStackParamList } from '../types'; 4import { movies } from '../data/movies'; 5import MovieCard from '../components/MovieCard'; 6 7type Props = NativeStackScreenProps<RootStackParamList, 'Home'>; 8 9export default function HomeScreen({ navigation }: Props) { 10 return ( 11 <View style={{ flex: 1, padding: 16 }}> 12 <FlatList 13 data={movies} 14 keyExtractor={(m) => String(m.id)} 15 renderItem={({ item }) => ( 16 <Pressable 17 onPress={() => 18 navigation.navigate('MovieDetail', { id: item.id, title: item.title }) 19 } 20 > 21 <MovieCard movie={item} /> 22 </Pressable> 23 )} 24 /> 25 </View> 26 ); 27}

src/screens/MovieDetailScreen.tsx

1import { View, Text } from 'react-native'; 2import { NativeStackScreenProps } from '@react-navigation/native-stack'; 3import { RootStackParamList } from '../types'; 4import { movies } from '../data/movies'; 5 6type Props = NativeStackScreenProps<RootStackParamList, 'MovieDetail'>; 7 8export default function MovieDetailScreen({ route }: Props) { 9 const { id } = route.params; 10 const movie = movies.find((m) => m.id === id); 11 12 if (!movie) return <Text style={{ padding: 16 }}>Movie not found</Text>; 13 14 return ( 15 <View style={{ flex: 1, padding: 16, gap: 6 }}> 16 <Text style={{ fontSize: 22, fontWeight: '600' }}>{movie.title}</Text> 17 <Text>Year: {movie.year}</Text> 18 <Text>Categories: {movie.categories.join(', ')}</Text> 19 <Text>Rating: {movie.rating}</Text> 20 </View> 21 ); 22}

💡 Tips

  • Params are usually handled as strict types with NativeStackScreenProps and a ParamList.
  • Customize Stack options for dynamic headers (e.g., use the movie title in the detail).
  • Keep components small and reusable (MovieCard, Grid).
  • If your params come as string (e.g., from inputs), convert them to number before searching for movies.

Signup and get access to this project for free

We will use it to give you access to your account.
Already have an account? Login here.

By signing up, you agree to the Terms and conditions and Privacy policy.

Difficulty

  • intermediate

Average duration

2 hrs

Technologies

Difficulty

  • intermediate

Average duration

2 hrs

Technologies

Difficulty

  • intermediate

Average duration

2 hrs

Technologies

Difficulty

  • intermediate

Average duration

2 hrs

Technologies

Signup and get access to this project for free

We will use it to give you access to your account.
Already have an account? Login here.

By signing up, you agree to the Terms and conditions and Privacy policy.

Difficulty

  • intermediate

Average duration

2 hrs

Technologies

Difficulty

  • intermediate

Average duration

2 hrs

Technologies