Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Official Expo team skill for building native UI in Expo apps, fine-tuned for Opus models and usable with Claude Code or Cursor.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/form-sheet.md
1# Form Sheets in Expo Router23This skill covers implementing form sheets with footers using Expo Router's Stack navigator and react-native-screens.45## Overview67Form sheets are modal presentations that appear as a card sliding up from the bottom of the screen. They're ideal for:89- Quick actions and confirmations10- Settings panels11- Login/signup flows12- Action sheets with custom content1314**Requirements:**1516- Expo Router Stack navigator1718## Basic Usage1920### Form Sheet with Footer2122Configure the Stack.Screen with transparent backgrounds and sheet presentation:2324```tsx25// app/_layout.tsx26import { Stack } from "expo-router";2728export default function Layout() {29return (30<Stack>31<Stack.Screen name="index" />32<Stack.Screen33name="about"34options={{35presentation: "formSheet",36sheetAllowedDetents: [0.25],37headerTransparent: true,38contentStyle: { backgroundColor: "transparent" },39sheetGrabberVisible: true,40}}41>42<Stack.Header style={{ backgroundColor: "transparent" }}></Stack.Header>43</Stack.Screen>44</Stack>45);46}47```4849### Form Sheet Screen Content5051> Requires Expo SDK 55 or later.5253Use `flex: 1` to allow the content to fill available space, enabling footer positioning:5455```tsx56// app/about.tsx57import { View, Text, StyleSheet } from "react-native";5859export default function AboutSheet() {60return (61<View style={styles.container}>62{/* Main content */}63<View style={styles.content}>64<Text>Sheet Content</Text>65</View>6667{/* Footer - stays at bottom */}68<View style={styles.footer}>69<Text>Footer Content</Text>70</View>71</View>72);73}7475const styles = StyleSheet.create({76container: {77flex: 1,78},79content: {80flex: 1,81padding: 16,82},83footer: {84padding: 16,85},86});87```8889### Formsheet with interactive content below9091Use `sheetLargestUndimmedDetentIndex` (zero-indexed) to keep content behind the form sheet interactive — e.g. letting users pan a map beneath it. Setting it to `1` allows interaction at the first two detents but dims on the third.9293```tsx94// app/_layout.tsx95import { Stack } from 'expo-router';9697export default function Layout() {98return (99<Stack screenOptions={{ headerShown: false }}>100<Stack.Screen name="index" />101<Stack.Screen102name="info-sheet"103options={{104presentation: "formSheet",105sheetAllowedDetents: [0.2, 0.5, 1.0],106sheetLargestUndimmedDetentIndex: 1,107/* other options */108}}109/>110</Stack>111)112}113```114115## Key Options116117| Option | Type | Description |118| --------------------- | ---------- | ----------------------------------------------------------- |119| `presentation` | `string` | Set to `'formSheet'` for sheet presentation |120| `sheetGrabberVisible` | `boolean` | Shows the drag handle at the top of the sheet |121| `sheetAllowedDetents` | `number[]` | Array of detent heights (0-1 range, e.g., `[0.25]` for 25%) |122| `headerTransparent` | `boolean` | Makes header background transparent |123| `contentStyle` | `object` | Style object for the screen content container |124| `title` | `string` | Screen title (set to `''` for no title) |125126## Common Detent Values127128- `[0.25]` - Quarter sheet (compact actions)129- `[0.5]` - Half sheet (medium content)130- `[0.75]` - Three-quarter sheet (detailed forms)131- `[0.25, 0.5, 1]` - Multiple stops (expandable sheet)132133## Complete Example134135```tsx136// _layout.tsx137import { Stack } from "expo-router";138139export default function Layout() {140return (141<Stack>142<Stack.Screen name="index" options={{ title: "Home" }} />143<Stack.Screen144name="confirm"145options={{146contentStyle: { backgroundColor: "transparent" },147presentation: "formSheet",148title: "",149sheetGrabberVisible: true,150sheetAllowedDetents: [0.25],151headerTransparent: true,152}}153>154<Stack.Header style={{ backgroundColor: "transparent" }}>155<Stack.Header.Right />156</Stack.Header>157</Stack.Screen>158</Stack>159);160}161```162163```tsx164// app/confirm.tsx165import { View, Text, Pressable, StyleSheet } from "react-native";166import { router } from "expo-router";167168export default function ConfirmSheet() {169return (170<View style={styles.container}>171<View style={styles.content}>172<Text style={styles.title}>Confirm Action</Text>173<Text style={styles.description}>174Are you sure you want to proceed?175</Text>176</View>177178<View style={styles.footer}>179<Pressable style={styles.cancelButton} onPress={() => router.back()}>180<Text style={styles.cancelText}>Cancel</Text>181</Pressable>182<Pressable style={styles.confirmButton} onPress={() => router.back()}>183<Text style={styles.confirmText}>Confirm</Text>184</Pressable>185</View>186</View>187);188}189190const styles = StyleSheet.create({191container: {192flex: 1,193},194content: {195flex: 1,196padding: 20,197alignItems: "center",198justifyContent: "center",199},200title: {201fontSize: 18,202fontWeight: "600",203marginBottom: 8,204},205description: {206fontSize: 14,207color: "#666",208textAlign: "center",209},210footer: {211flexDirection: "row",212padding: 16,213gap: 12,214},215cancelButton: {216flex: 1,217padding: 14,218borderRadius: 10,219backgroundColor: "#f0f0f0",220alignItems: "center",221},222cancelText: {223fontSize: 16,224fontWeight: "500",225},226confirmButton: {227flex: 1,228padding: 14,229borderRadius: 10,230backgroundColor: "#007AFF",231alignItems: "center",232},233confirmText: {234fontSize: 16,235fontWeight: "500",236color: "white",237},238});239```240241## Troubleshooting242243### Content not filling sheet244245Make sure the root View uses `flex: 1`:246247```tsx248<View style={{ flex: 1 }}>{/* content */}</View>249```250251### Sheet background showing through252253Set `contentStyle: { backgroundColor: 'transparent' }` in options and style your content container with the desired background color instead.254