💡 Why I Built This
While React Navigation’s standard drawer works, I wanted to create a visually immersive experience where screens dynamically respond to drawer interactions. The goal? An iOS-style navigation with:
- Smooth 3D rotations
- Scale/perspective animations
- Custom profile integration
- Seamless gesture control
🧩 Key Tech Stack
-
expo-router
(File-based routing) -
react-native-reanimated
(Animations) -
@react-navigation/drawer
(Core navigation) -
react-native-gesture-handler
(Swipe gestures)
🚀 Step-by-Step Implementatio
1. Core Setup
npx create-expo-app@latest
npx expo install @react-navigation/drawer react-native-gesture-handler react-native-reanimated
2. Magic Animation Wrapper (drawer-scene-wrapper.tsx
)
This component makes screens dance when the drawer opens:
const DrawerSceneWrapper = ({ children }: { children: ReactNode }) => {
const progress = useDrawerProgress();
const animatedStyled = useAnimatedStyle(() => ({
transform: [
{ scale: interpolate(progress.value, [0, 1], [1, 0.8]) },
{ translateX: interpolate(progress.value, [0, 1], [0, 170]) },
{ rotateY: `${interpolate(progress.value, [0, 1], [0, -25])}deg` }
],
borderRadius: 20
}));
return <Animated.View style={[styles.container, animatedStyled]}>
{children}
</Animated.View>;
};
3. Custom Drawer UI (custom-drawer-content.tsx
)
Added user profile + logout button:
<SafeAreaView style={{ flex: 1, backgroundColor: "transparent" }}>
{/* Profile Section */}
<TouchableOpacity style={styles.userContainer}>
<Image source={{ uri: "USER_AVATAR_URL" }} style={styles.userImage} />
<View>
<Text style={styles.userName}>Haider Mukhtar</Text>
<Text style={styles.userEmail}>haider@example.com</Text>
</View>
</TouchableOpacity>
{/* Navigation Items */}
<DrawerContentScrollView {...props}>
<DrawerItemList {...props} />
</DrawerContentScrollView>
{/* Logout Button */}
<TouchableOpacity style={styles.logoutButton}>
<Ionicons name="log-out" size={24} color="#FFFFFF" />
<Text style={styles.logoutText}>Logout</Text>
</TouchableOpacity>
</SafeAreaView>
4. Drawer Configuration (_layout.tsx
)
Styled the drawer with effects:
<GestureHandlerRootView style={{ flex: 1 }}>
<Drawer
drawerContent={(props) => <CustomDrawerContent {...props} />}
screenOptions={{
headerShown: false,
drawerActiveBackgroundColor: "#33b3a6",
drawerInactiveBackgroundColor: "transparent",
drawerActiveTintColor: "#FFFFFF",
drawerInactiveTintColor: "#FFFFFF",
overlayColor: "transparent",
drawerStyle: {
backgroundColor: "transparent",
width: "60%",
paddingTop: 40,
},
drawerLabelStyle: {
marginLeft: -6,
fontSize: 18,
fontFamily: "PoppinsMedium500",
color: "#FFFFFF",
},
drawerItemStyle: {
marginLeft: -16,
marginRight: 35,
borderTopLeftRadius: 0,
borderBottomLeftRadius: 0,
borderTopRightRadius: 50,
borderBottomRightRadius: 50,
},
sceneStyle: {
backgroundColor: "#26867C",
},
// drawerType: 'front',
}}
>
<Drawer.Screen
name="index"
options={{
title: "Home",
drawerIcon: ({ color }) => <Ionicons name="home" color={color} />
}}
/>
{/* Add other screens here */}
</Drawer>
</GestureHandlerRootView>
5. Screen Implementation (index.tsx
)
Wrapped every screen in DrawerSceneWrapper
:
const HomeScreen = () => {
const navigation = useNavigation();
return (
<DrawerSceneWrapper>
<View style={styles.header}>
<TouchableOpacity
onPress={() => navigation.dispatch(DrawerActions.openDrawer())}
>
<Ionicons name="menu" size={28} color="#000" />
</TouchableOpacity>
<Text style={styles.headerTitle}>Home</Text>
</View>
{/* Screen content */}
</DrawerSceneWrapper>
);
}
👀 Demo Time!
Here’s what it looks like in action:
🎨 Design Highlights
- Animated Perspective 3D rotation + scaling synchronized with drawer gestures.
- Bubble Menu Items
Rounded active-state highlights with
borderTopRightRadius: 50
.
🚫 Common Pitfalls Solved
- Gesture Conflicts: Wrapped root in
<GestureHandlerRootView>
. - Animation Jank: Used
Extrapolation.CLAMP
for smooth interpolation. - Safe Areas: Leveraged
react-native-safe-area-context
for notch support. - TypeScript: Strict typing for drawer props (
DrawerContentComponentProps
).
📱 Try It Yourself!
Clone the full project:
👉 Github Repo: Animated-Drawer-Navigation-Expo
💬 Final Thoughts
This implementation proves that with:
- Reanimated’s interpolation magic ✨
- Expo’s zero-config tooling 🧰
- Strategic styling 🎨
… you can create navigation that delights users while maintaining code simplicity. What would you add to this? Share your ideas below! 👇
Top comments (1)
Demo Video:
dev-to-uploads.s3.amazonaws.com/up...