expo-native-ui
skillBuild beautiful native iOS/Android apps with Expo Router. Covers route structure, native tabs, animations, blur effects, liquid glass, SF Symbols, and platform patterns.
apm::install
apm install @wpank/expo-native-uiapm::skill.md
---
name: expo-native-ui
model: standard
version: 1.0.0
description: >
Build beautiful native iOS/Android apps with Expo Router. Covers route structure,
native tabs, animations, blur effects, liquid glass, SF Symbols, and platform patterns.
tags: [expo, react-native, ios, android, mobile, navigation, animations]
---
# Expo Native UI
Build production-quality native mobile apps with Expo Router following Apple Human Interface Guidelines and modern React Native patterns.
## Installation
### OpenClaw / Moltbot / Clawbot
```bash
npx clawhub@latest install expo-native-ui
```
## WHAT This Skill Does
Guides implementation of native mobile apps using Expo Router with:
- File-based routing with native navigation stacks
- Native tab bars (NativeTabs) and iOS 26 features
- SF Symbols integration via expo-symbols
- Blur effects (expo-blur) and liquid glass (expo-glass-effect)
- Reanimated animations and gesture handling
- Native controls: Switch, Slider, SegmentedControl, DateTimePicker
## WHEN To Use
- Building a new Expo Router app
- Adding native tab navigation
- Implementing iOS-style blur or liquid glass effects
- Creating smooth animations with entering/exiting transitions
- Integrating SF Symbols for icons
- Setting up route structure with groups and dynamic routes
## KEYWORDS
expo router, react native, native tabs, sf symbols, expo blur, liquid glass, reanimated, ios, android, mobile app, navigation stack, form sheet, modal, context menu, link preview
## References
Consult these resources for detailed implementation:
| Reference | Purpose |
|-----------|---------|
| `references/route-structure.md` | Route conventions, dynamic routes, groups, query params |
| `references/tabs.md` | NativeTabs, migration from JS tabs, iOS 26 features |
| `references/icons.md` | SF Symbols with expo-symbols, animations, weights |
| `references/controls.md` | Native iOS controls: Switch, Slider, DateTimePicker, Picker |
| `references/visual-effects.md` | Blur effects and liquid glass |
| `references/animations.md` | Reanimated: entering, exiting, layout, scroll-driven |
| `references/search.md` | Search bar integration, useSearch hook, filtering |
| `references/gradients.md` | CSS gradients via experimental_backgroundImage |
| `references/media.md` | Camera, audio, video, file saving |
| `references/storage.md` | SQLite, AsyncStorage, SecureStore |
| `references/webgpu-three.md` | WebGPU, Three.js for 3D graphics |
| `references/toolbar-and-headers.md` | Stack headers, toolbar customization (iOS) |
## Core Principles
### Running the App
**Try Expo Go first** before creating custom builds:
```bash
npx expo start # Scan QR with Expo Go
```
Custom builds (`npx expo run:ios`) only needed for:
- Local Expo modules (custom native code in `modules/`)
- Apple targets (widgets, app clips via `@bacons/apple-targets`)
- Third-party native modules not in Expo Go
### Code Style
- **Kebab-case file names**: `comment-card.tsx`
- **Path aliases in tsconfig** over relative imports
- **Never co-locate** components/utilities in `app/` directory
- **Always ensure** a route matches "/" (may be in a group)
- **Escape nested backticks** carefully in strings
### Library Preferences
| Use | Instead Of |
|-----|------------|
| `expo-audio` | `expo-av` |
| `expo-video` | `expo-av` |
| `expo-symbols` | `@expo/vector-icons` |
| `react-native-safe-area-context` | RN SafeAreaView |
| `process.env.EXPO_OS` | `Platform.OS` |
| `React.use` | `React.useContext` |
| `expo-image` | intrinsic `img` element |
| `expo-glass-effect` | custom blur views |
### Responsiveness
```tsx
// Always wrap root in ScrollView with automatic insets
<ScrollView contentInsetAdjustmentBehavior="automatic">
{children}
</ScrollView>
// Use useWindowDimensions, not Dimensions.get()
const { width, height } = useWindowDimensions();
// Flexbox over Dimensions API
<View style={{ flex: 1, flexDirection: 'row', gap: 16 }} />
```
## Navigation Patterns
### Link with Preview and Context Menu
```tsx
import { Link } from 'expo-router';
<Link href="/settings">
<Link.Trigger>
<Pressable><Card /></Pressable>
</Link.Trigger>
<Link.Preview />
<Link.Menu>
<Link.MenuAction title="Share" icon="square.and.arrow.up" onPress={handleShare} />
<Link.MenuAction title="Delete" icon="trash" destructive onPress={handleDelete} />
</Link.Menu>
</Link>
```
### Form Sheet Modal
```tsx
// In _layout.tsx
<Stack.Screen
name="sheet"
options={{
presentation: "formSheet",
sheetGrabberVisible: true,
sheetAllowedDetents: [0.5, 1.0],
contentStyle: { backgroundColor: "transparent" }, // Liquid glass on iOS 26+
}}
/>
```
### Native Tabs Structure
```
app/
_layout.tsx — <NativeTabs />
(index,search)/
_layout.tsx — <Stack />
index.tsx
search.tsx
```
```tsx
// app/_layout.tsx
import { NativeTabs, Icon, Label } from "expo-router/unstable-native-tabs";
export default function Layout() {
return (
<NativeTabs>
<NativeTabs.Trigger name="(index)">
<Icon sf="list.dash" />
<Label>Items</Label>
</NativeTabs.Trigger>
<NativeTabs.Trigger name="(search)" role="search" />
</NativeTabs>
);
}
```
## Styling Guidelines
- **Flex gap** over margin/padding where possible
- **`borderCurve: 'continuous'`** for rounded corners (not capsules)
- **`boxShadow`** style prop, never legacy RN shadow/elevation
- **Stack title** instead of custom text elements for page headers
- **Inline styles**, not `StyleSheet.create` unless reusing
- **`fontVariant: 'tabular-nums'`** for numeric counters
- **`selectable` prop** on Text displaying copiable data
```tsx
// Shadow example
<View style={{ boxShadow: "0 1px 2px rgba(0, 0, 0, 0.05)" }} />
// Continuous border curve
<View style={{ borderRadius: 12, borderCurve: 'continuous' }} />
```
## Behavior Patterns
- **Haptics**: Use `expo-haptics` conditionally on iOS
- **Search bar**: Prefer `headerSearchBarOptions` in Stack.Screen
- **Selectable text**: Add `selectable` prop to important data
- **Format large numbers**: 1.4M, 38k instead of 1,400,000
- **Never use** intrinsic elements (`img`, `div`) outside DOM components
## NEVER Do
1. **NEVER use** legacy modules: Picker, WebView, SafeAreaView from react-native, AsyncStorage (old), expo-permissions
2. **NEVER use** `Dimensions.get()` — always `useWindowDimensions`
3. **NEVER co-locate** components in the `app/` directory
4. **NEVER use** `Platform.OS` — use `process.env.EXPO_OS`
5. **NEVER use** legacy shadow styles — use CSS `boxShadow`
6. **NEVER start** with custom builds — try Expo Go first
7. **NEVER use** StyleSheet.create for one-time styles
8. **NEVER use** `@expo/vector-icons` — use `expo-symbols`