npx skills add ...
npx skills add expo/skills --skill expo-native-ui
Framework (OSS). Build beautiful, native-feeling Expo screens. Covers Apple HIG styling, semantic colors, native controls, SF Symbols, media, visual effects, gradients, storage, and responsive layout. For routing and navigation, use the expo-router skill; for motion and animation, use the expo-animation skill.
npx skills add expo/skills --skill expo-native-ui
For routes, links, stacks, tabs, modals, sheets, and headers, use the expo-router skill. For any motion — entering/exiting, gestures, springs, keyboard-driven UI — use the expo-animation skill.
Before picking any UI component, check
expo-uifirst.@expo/uiprovides native equivalents — BottomSheet, Button, Picker, Slider, Menu, Section, Switch, SegmentedControl, and more — rendered as real SwiftUI on iOS and Jetpack Compose on Android, available in Expo Go on SDK 56+ with no custom build. Load theexpo-uiskill to find the right component before falling back to React Native built-ins or community libraries. This skill (expo-native-ui) covers the surrounding structure: Expo Router navigation, layout, styling, and visual effects.
Consult these resources as needed:
CRITICAL: Always try Expo Go first before creating custom builds.
Most Expo apps work in Expo Go without any custom native code. Before running npx expo run:ios or npx expo run:android:
npx expo start and scan the QR code with Expo GoYou need npx expo run:ios/android or eas build ONLY when using:
modules/)@bacons/apple-targets)app.jsonExpo Go supports a wide range of features out of the box:
expo-* packages (camera, location, sensors, sqlite, etc.) — but not all: remote push notifications don't work in Expo Go on Android since SDK 53, and some packages need native capabilities Expo Go doesn't bundle (e.g. WebGPU — see references/webgpu-three.md)If you're unsure, try Expo Go first. Creating custom builds adds complexity, slower iteration, and requires Xcode/Android Studio setup.
comment-card.tsx@expo/ui (see expo-ui skill) before reaching for a React Native built-in or community library — it renders native SwiftUI/Compose and works in Expo Go on SDK 56+. For grouped/settings-style rows (short, fixed-length), use @expo/ui's List + ListItem. For large or unknown-length scrolling lists (feeds, search results, catalogs), use FlatList or FlashList — @expo/ui's List is not virtualized.expo-audio not expo-avexpo-video not expo-avexpo-symbols (SymbolView) for SF Symbols on iOS, not @expo/vector-icons — see references/icons.md. SF Symbols are Apple-only: on Android every icon needs a Material source (md prop on NativeTabs triggers; in-screen options under "Android: Material Icons" in icons.md), never SF-only iconographyreact-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 backdropsColor from expo-router for native semantic colors, not raw PlatformColor (type-safe, auto-adapts to light/dark)@react-navigation/* directly — use expo-router/react-navigation instead (covers @react-navigation/native, /core, /elements, /routers)<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" set<Text selectable /> prop on text containing data that could be copiedexpo-data-fetching skillkeyboardShouldPersistTaps="handled" so controls receive the first tap and unhandled taps can dismiss the keyboard. Use "always" only when unhandled taps should also keep it openexpo-animation skill's keyboard recipe (react-native-keyboard-controller) - never Keyboard.addListener plus a timing animationexpo-data-fetching; do not dismiss a form before its save succeedsBefore calling a screen complete, walk through its primary task, including one failure and recovery when it loads or saves data. Check keyboard access and back/dismiss behavior. Try long titles, missing images, no search results, and large system text; required actions must remain reachable. Report what you exercised and what you could not run.
Follow each platform's own design language: Apple Human Interface Guidelines on iOS, Material Design 3 on Android. Never dress one platform in the other's uniform - no FAB or ripple in iOS layouts; no hand-built iOS chrome (back-chevrons, large-title text, iOS-styled switches) on Android.
contentInsetAdjustmentBehavior="automatic"expo-animation skill — it owns the animate-or-not decision, timing values, and interruption rules{ borderCurve: 'continuous' } for rounded corners unless creating a capsule shapecontentContainerStyle padding and gap instead of padding on the ScrollView itself (reduces clipping)Use the Color API from expo-router for native semantic colors. It is a type-safe wrapper over PlatformColor that exposes iOS UIKit colors through Color.ios.* and Android Material 3 colors through Color.android.material.* (static) or Color.android.dynamic.* (adapts to the user's wallpaper on Android 12+). These resolve on-device and automatically adapt to light/dark mode and accessibility settings, so you no longer maintain separate light/dark hex tables or a colors.web.ts file.
Color is platform-specific, so wrap each value in Platform.select with a default hex fallback for web. Centralize the palette in theme/colors.ts and import colors everywhere:
useColorScheme() inside any component that renders them so it re-renders when the theme flips (required when React Compiler memoizes the component).Color / PlatformColor values into Reanimated styles — they are opaque native color objects, not strings; use static colors there.Platform.select({...})! returns string | OpaqueColorValue. Most React Native style props accept ColorValue (string | OpaqueColorValue) so this works fine. But some third-party props only accept string (e.g. tintColor on expo-image). Cast when needed: colors.label as string.selectable prop to every <Text/> element displaying important data or error messages{ fontVariant: 'tabular-nums' } for alignmentUse CSS boxShadow style prop. NEVER use legacy React Native shadow or elevation styles.
'inset' shadows are supported.
If you encounter errors, misleading or outdated information in this skill, report it so Expo can improve:
Only submit when you have something specific and actionable to report. Include as much relevant context as possible. If an AI agent repeatedly failed or the user had to take over an Expo task, load the expo-skill-feedback skill and follow its eval-candidate flow instead of reusing the command above.