From harness-claude
Implements stack, tab, and drawer navigation in React Native using Expo Router or React Navigation, with type-safe routing and deep linking.
npx claudepluginhub intense-visions/harness-engineering --plugin harness-claudeThis skill uses the workspace's default tool permissions.
> Implement stack, tab, and drawer navigation in React Native with type-safe routing and deep linking
Implements stack, tab, drawer navigation in React Native apps using React Navigation. Covers installation, setup, deep linking, and navigation patterns.
Guides Expo React Native feature development with best practices and code templates for project setup, Expo Router navigation, screens, data fetching, authentication, deep linking, forms, and testing.
Implements file-based routing with Expo Router in Expo apps. Covers app directory structure, Stack/Tab layouts, dynamic routes, deep linking, and programmatic navigation.
Share bugs, ideas, or general feedback.
Implement stack, tab, and drawer navigation in React Native with type-safe routing and deep linking
Expo Router approach (recommended for Expo):
app/
_layout.tsx # Root layout with navigation container
(tabs)/
_layout.tsx # Tab navigator
index.tsx # Home tab
search.tsx # Search tab
profile.tsx # Profile tab
[userId].tsx # Dynamic screen /userId
settings/
_layout.tsx # Stack navigator for settings
index.tsx # Settings main
notifications.tsx # Notification settings
// app/_layout.tsx
import { Stack } from 'expo-router';
export default function RootLayout() {
return (
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="[userId]" options={{ title: 'Profile' }} />
<Stack.Screen name="settings" options={{ headerShown: false }} />
</Stack>
);
}
// app/(tabs)/_layout.tsx
import { Tabs } from 'expo-router';
import { HomeIcon, SearchIcon, ProfileIcon } from '@/components/icons';
export default function TabLayout() {
return (
<Tabs screenOptions={{ tabBarActiveTintColor: '#007AFF' }}>
<Tabs.Screen
name="index"
options={{
title: 'Home',
tabBarIcon: ({ color, size }) => <HomeIcon color={color} size={size} />,
}}
/>
<Tabs.Screen
name="search"
options={{
title: 'Search',
tabBarIcon: ({ color, size }) => <SearchIcon color={color} size={size} />,
}}
/>
<Tabs.Screen
name="profile"
options={{
title: 'Profile',
tabBarIcon: ({ color, size }) => <ProfileIcon color={color} size={size} />,
}}
/>
</Tabs>
);
}
import { Link, useRouter } from 'expo-router';
// Declarative — renders an anchor/pressable
<Link href="/settings/notifications">Notification Settings</Link>
<Link href={{ pathname: '/[userId]', params: { userId: '123' } }}>View Profile</Link>
// Imperative — from event handlers
const router = useRouter();
router.push('/settings');
router.replace('/login'); // No back button
router.back();
// types/navigation.ts
export type RootStackParamList = {
Home: undefined;
Profile: { userId: string };
Settings: undefined;
};
export type TabParamList = {
Feed: undefined;
Search: { query?: string };
Account: undefined;
};
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import { NavigationContainer } from '@react-navigation/native';
const Stack = createNativeStackNavigator<RootStackParamList>();
function App() {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={HomeScreen} />
<Stack.Screen name="Profile" component={ProfileScreen} />
</Stack.Navigator>
</NavigationContainer>
);
}
// app.config.ts
export default {
scheme: 'myapp',
// Universal links
ios: { associatedDomains: ['applinks:example.com'] },
android: {
intentFilters: [
{
action: 'VIEW',
autoVerify: true,
data: [{ scheme: 'https', host: 'example.com', pathPrefix: '/app' }],
},
],
},
};
// app/_layout.tsx
import { useAuth } from '@/hooks/useAuth';
import { Redirect, Stack } from 'expo-router';
export default function RootLayout() {
const { isAuthenticated, isLoading } = useAuth();
if (isLoading) return <SplashScreen />;
if (!isAuthenticated) return <Redirect href="/login" />;
return (
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
</Stack>
);
}
<Stack.Screen name="create-post" options={{ presentation: 'modal', headerTitle: 'New Post' }} />
Expo Router vs. React Navigation: Expo Router is built on React Navigation and adds file-based routing, automatic deep linking, and typed routes. Use Expo Router for new Expo projects. Use React Navigation directly when you need programmatic navigator composition or are not using Expo.
Navigation patterns by app type:
Performance tips:
lazy: true on tabs to defer rendering until first visitfreezeOnBlur: true to prevent re-renders on inactive screensCommon mistakes: