Consult these resources as needed: ``` references/
references/ animations.md Reanimated: entering, exiting, layout, scroll-driven, gestures controls.md Native iOS: Switch, Slider, SegmentedControl, DateTimePicker, Picker form-sheet.md Form sheets with footers via Stack and react-native-screens gradients.md CSS gradients via experimental_backgroundImage (New Arch only) icons.md SF Symbols via expo-image (sf: source), names, animations, weights media.md Camera, audio, video, and file saving route-structure.md Route conventions, dynamic routes, groups, folder organization search.md Search bar with headers, useSearch hook, filtering patterns storage.md SQLite, AsyncStorage, SecureStore tabs.md NativeTabs, migration from JS tabs, iOS 26 features toolbar-and-headers.md Stack headers and toolbar buttons, menus, search (iOS only) visual-effects.md Blur (expo-blur) and liquid glass (expo-glass-effect) webgpu-three.md 3D graphics, games, GPU visualizations with WebGPU and Three.js zoom-transitions.md Apple Zoom: fluid zoom transitions with Link.AppleZoom (iOS 18+)
npx expo run:ios or npx expo run:android:npx expo start and scan the QR code with Expo Gonpx expo run:ios/android or eas build ONLY when using:modules/)@bacons/apple-targets)app.jsonexpo-* packages (camera, location, notifications, etc.)comment-card.tsx./references/route-structure.md for detailed route conventions.app directory.expo-audio not expo-avexpo-video not expo-avexpo-image with source="sf:name" for SF Symbols, not expo-symbols or @expo/vector-iconsreact-native-safe-area-context not react-native SafeAreaViewprocess.env.EXPO_OS not Platform.OSReact.use not React.useContextexpo-image Image component instead of intrinsic element imgexpo-glass-effect for liquid glass backdrops<ScrollView contentInsetAdjustmentBehavior="automatic" /> instead of <SafeAreaView> for smarter safe area insetscontentInsetAdjustmentBehavior="automatic" should be applied to FlatList and SectionList as welluseWindowDimensions over Dimensions.get() to measure screen size<Switch /> from React Native and @react-native-community/datetimepickercontentInsetAdjustmentBehavior="automatic" setheaderSearchBarOptions in Stack.Screen options to add a search bar<Text selectable /> prop on text containing data that could be copiedcontentInsetAdjustmentBehavior="automatic"{ borderCurve: 'continuous' } for rounded corners unless creating a capsule shapecontentContainerStyle padding and gap instead of padding on the ScrollView itself (reduces clipping)selectable prop to every <Text/> element displaying important data or error messages{ fontVariant: 'tabular-nums' } for alignmentboxShadow style prop. NEVER use legacy React Native shadow or elevation styles.<View style={{ boxShadow: "0 1px 2px rgba(0, 0, 0, 0.05)" }} /><Link href="/path" /> from 'expo-router' for navigation between routes.import { Link } from 'expo-router'; // Basic link <Link href="/path" /> // Wrapping custom components <Link href="/path" asChild> <Pressable>...</Pressable> </Link>
<Link.Preview> to follow iOS conventions. Add context menus and previews frequently to enhance navigation._layout.tsx files to define stacks<Stack.Screen options={{ title: "Home" }} />import { Link } from "expo-router"; <Link href="/settings" asChild> <Link.Trigger> <Pressable> <Card /> </Pressable> </Link.Trigger> <Link.Menu> <Link.MenuAction title="Share" icon="square.and.arrow.up" onPress={handleSharePress} /> <Link.MenuAction title="Block" icon="nosign" destructive onPress={handleBlockPress} /> <Link.Menu title="More" icon="ellipsis"> <Link.MenuAction title="Copy" icon="doc.on.doc" onPress={() => {}} /> <Link.MenuAction title="Delete" icon="trash" destructive onPress={() => {}} /> </Link.Menu> </Link.Menu> </Link>;
<Link href="/settings"> <Link.Trigger> <Pressable> <Card /> </Pressable> </Link.Trigger> <Link.Preview /> </Link>
<Stack.Screen name="modal" options={{ presentation: "modal" }} /><Stack.Screen name="sheet" options={{ presentation: "formSheet", sheetGrabberVisible: true, sheetAllowedDetents: [0.5, 1.0], contentStyle: { backgroundColor: "transparent" }, }} />
contentStyle: { backgroundColor: "transparent" } makes the background liquid glass on iOS 26+.app/ _layout.tsx — <NativeTabs /> (index,search)/ _layout.tsx — <Stack /> index.tsx — Main list search.tsx — Search view
// app/_layout.tsx import { NativeTabs, Icon, Label } from "expo-router/unstable-native-tabs"; import { Theme } from "../components/theme"; export default function Layout() { return ( <Theme> <NativeTabs> <NativeTabs.Trigger name="(index)"> <Icon sf="list.dash" /> <Label>Items</Label> </NativeTabs.Trigger> <NativeTabs.Trigger name="(search)" role="search" /> </NativeTabs> </Theme> ); } `Create a shared group route so both tabs can push common screens:` // app/(index,search)/_layout.tsx import { Stack } from "expo-router/stack"; import { PlatformColor } from "react-native"; export default function Layout({ segment }) { const screen = segment.match(/\((.*)\)/)?.[1]!; const titles: Record<string, string> = { index: "Items", search: "Search" }; return ( <Stack screenOptions={{ headerTransparent: true, headerShadowVisible: false, headerLargeTitleShadowVisible: false, headerLargeStyle: { backgroundColor: "transparent" }, headerTitleStyle: { color: PlatformColor("label") }, headerLargeTitle: true, headerBlurEffect: "none", headerBackButtonDisplayMode: "minimal", }} > <Stack.Screen name={screen} options={{ title: titles[screen] }} /> <Stack.Screen name="i/[id]" options={{ headerLargeTitle: false }} /> </Stack> ); }