home / skills / openclaw / skills / native-ui

native-ui skill

/skills/wpank/native-ui

This skill helps you build native mobile UIs with Expo Router and React Native by outlining routing, styling, and platform conventions.

npx playbooks add skill openclaw/skills --skill native-ui

Review the files below or copy the command above to add this skill to your agents.

Files (16)
SKILL.md
8.9 KB
---
name: native-ui
model: standard
version: 1.0.0
description: >
  Building native mobile UIs with Expo Router and React Native. Covers routing,
  navigation, styling, native controls, animations, and platform conventions
  following Apple Human Interface Guidelines.
tags: [expo, react-native, ios, mobile, navigation, native-ui]
---

# Native UI with Expo Router

Patterns and conventions for building native mobile applications with Expo Router and React Native.

## References

Consult these as needed:

- `./references/route-structure.md` — Route conventions, dynamic routes, groups, folder organization
- `./references/tabs.md` — Native tab bar with NativeTabs, iOS 26 features
- `./references/icons.md` — SF Symbols with expo-symbols, icon names, animations, weights
- `./references/controls.md` — Native iOS controls: Switch, Slider, SegmentedControl, DateTimePicker
- `./references/visual-effects.md` — Blur effects (expo-blur) and liquid glass (expo-glass-effect)
- `./references/animations.md` — Reanimated: entering, exiting, layout, scroll-driven, gestures
- `./references/search.md` — Search bar with headers, useSearch hook, filtering patterns
- `./references/gradients.md` — CSS gradients via experimental_backgroundImage (New Architecture only)
- `./references/media.md` — Camera, audio, video, file saving
- `./references/storage.md` — SQLite, AsyncStorage, SecureStore
- `./references/webgpu-three.md` — 3D graphics and GPU visualizations with WebGPU/Three.js
- `./references/toolbar-and-headers.md` — Stack headers and toolbar with buttons, menus, search bars (iOS)
- `./references/form-sheet.md` — Form sheet presentation patterns

## Running the App

**Always try Expo Go first before creating custom builds.**

1. Start with `npx expo start` and scan the QR code
2. Test features in Expo Go
3. Only create custom builds when required

### When Custom Builds Are Required

Use `npx expo run:ios/android` or `eas build` only 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
- Custom native configuration beyond `app.json`

Expo Go supports all `expo-*` packages, Expo Router, Reanimated, Gesture Handler, push notifications, and deep links out of the box.


## Installation

### OpenClaw / Moltbot / Clawbot

```bash
npx clawhub@latest install native-ui
```


---

## Code Style

- Escape nested backticks and quotes correctly
- Always use import statements at the top of the file
- Use kebab-case for file names: `comment-card.tsx`
- Remove old route files when restructuring navigation
- No special characters in file names
- Configure `tsconfig.json` path aliases; prefer aliases over relative imports

## Routes

See `./references/route-structure.md` for detailed conventions.

- Routes belong in the `app` directory
- Never co-locate components, types, or utilities in `app/` — this is an anti-pattern
- Always have a route matching `/`, possibly inside a group route

## 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 glass backdrops |

Never use deprecated modules: Picker, WebView, SafeAreaView, AsyncStorage (from RN core), or legacy `expo-permissions`.

---

## Responsiveness

- Wrap root components in a scroll view
- Use `<ScrollView contentInsetAdjustmentBehavior="automatic" />` instead of `<SafeAreaView>`
- Apply `contentInsetAdjustmentBehavior="automatic"` to FlatList and SectionList too
- Use flexbox instead of Dimensions API
- Prefer `useWindowDimensions` over `Dimensions.get()` for screen measurement

## Behavior

- Use `expo-haptics` conditionally on iOS for delightful interactions
- Use views with built-in haptics (`<Switch />`, `@react-native-community/datetimepicker`)
- First child of a Stack route should almost always be a ScrollView with `contentInsetAdjustmentBehavior="automatic"`
- Prefer `headerSearchBarOptions` in Stack.Screen options for search bars
- Use `<Text selectable />` on data that users may want to copy
- Format large numbers: 1.4M, 38k
- Never use intrinsic elements (`img`, `div`) outside webviews or Expo DOM components

---

## Styling

Follow Apple Human Interface Guidelines.

### General Rules

- Prefer flex gap over margin and padding
- Prefer padding over margin
- Always account for safe area via stack headers, tabs, or `contentInsetAdjustmentBehavior="automatic"`
- Ensure both top and bottom safe area insets are handled
- Inline styles preferred over `StyleSheet.create` unless reusing styles
- Add entering/exiting animations for state changes
- Use `{ borderCurve: 'continuous' }` for rounded corners (not capsule shapes)
- Use navigation stack title instead of custom text headers
- On ScrollView, use `contentContainerStyle` for padding/gap (avoids clipping)
- CSS and Tailwind are not supported — use inline styles

### Text Styling

- Add `selectable` prop to `<Text/>` elements showing important data or errors
- Use `{ fontVariant: 'tabular-nums' }` on counters for alignment

### Shadows

Use CSS `boxShadow` style prop. Never use legacy RN shadow or elevation styles.

```tsx
<View style={{ boxShadow: "0 1px 2px rgba(0, 0, 0, 0.05)" }} />
```

Inset shadows are supported.

---

## Navigation

### Link

Use `<Link href="/path" />` from `expo-router` for navigation.

```tsx
import { Link } from 'expo-router';

<Link href="/path" />

<Link href="/path" asChild>
  <Pressable>...</Pressable>
</Link>
```

Include `<Link.Preview>` whenever possible to follow iOS conventions. Add context menus and previews frequently.

### Stack

- Always use `_layout.tsx` files to define stacks
- Use `Stack` from `expo-router/stack` for native navigation stacks
- Set page titles in Stack.Screen options: `options={{ title: "Home" }}`

### Context Menus

Add long-press context menus to Link components:

```tsx
<Link href="/settings" asChild>
  <Link.Trigger>
    <Pressable><Card /></Pressable>
  </Link.Trigger>
  <Link.Menu>
    <Link.MenuAction title="Share" icon="square.and.arrow.up" onPress={handleShare} />
    <Link.MenuAction title="Block" icon="nosign" destructive onPress={handleBlock} />
    <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 Previews

```tsx
<Link href="/settings">
  <Link.Trigger>
    <Pressable><Card /></Pressable>
  </Link.Trigger>
  <Link.Preview />
</Link>
```

Can be combined with context menus.

### Modal

Present a screen as a modal:

```tsx
<Stack.Screen name="modal" options={{ presentation: "modal" }} />
```

Prefer this over custom modal components.

### Sheet

Present as a dynamic form sheet:

```tsx
<Stack.Screen
  name="sheet"
  options={{
    presentation: "formSheet",
    sheetGrabberVisible: true,
    sheetAllowedDetents: [0.5, 1.0],
    contentStyle: { backgroundColor: "transparent" },
  }}
/>
```

`contentStyle: { backgroundColor: "transparent" }` enables liquid glass on iOS 26+.

---

## Common Route Structure

Standard app layout with tabs and stacks:

```
app/
  _layout.tsx        — <NativeTabs />
  (index,search)/
    _layout.tsx      — <Stack />
    index.tsx        — Main list
    search.tsx       — Search view
```

**Root layout:**

```tsx
// 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>
  );
}
```

**Shared group layout:**

```tsx
// 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>
  );
}
```

Overview

This skill teaches building native mobile UIs using Expo Router and React Native with Apple Human Interface Guidelines in mind. It bundles routing patterns, navigation stacks and tabs, native controls, animations, and platform conventions to create polished iOS-first experiences. The guidance emphasizes Expo Go workflows, responsive layouts, and composition patterns that scale across devices.

How this skill works

The skill inspects common app structure and provides conventions for route folders, stack and tab layouts, and Link usage with previews and context menus. It documents native controls (switches, sliders, date pickers), animations via Reanimated, visual effects (blur, liquid glass), and media/storage choices. It also prescribes styling patterns, safe-area handling, and when to use custom builds vs Expo Go.

When to use it

  • When building an iOS-first Expo app that should follow Apple HIG conventions
  • When you need a robust route structure with tabs, stacks, previews and context menus
  • When using native iOS controls, blur/glass effects, or Reanimated-driven animations
  • When deciding whether to iterate in Expo Go or create custom native builds
  • When you want consistent responsive behavior and safe-area handling across screens

Best practices

  • Keep routes inside the app directory and avoid co-locating components or utils there
  • Wrap primary screen content in ScrollView with contentInsetAdjustmentBehavior="automatic"
  • Prefer inline styles and flexbox, account for top and bottom safe-area insets
  • Use NativeTabs/Stack from expo-router and set titles via Stack.Screen options
  • Favor expo-* libraries recommended here (expo-audio, expo-symbols, expo-image) and avoid deprecated modules
  • Add entering/exiting animations and Link.Preview/Link.Menu for native-feeling interactions

Example use cases

  • Standard tabbed app with a shared group layout and large titles for lists
  • Detail screens presented as modal or form sheet with liquid glass backgrounds on iOS 26+
  • Search flow using header search bars, useSearch hook, and filtered lists
  • Media-heavy app using camera/audio/video via Expo media modules and secure storage with SecureStore
  • Prototype and iterate in Expo Go; switch to eas build or run:ios/android only for custom native modules or Apple-target features

FAQ

Do I need a custom build for Reanimated or Gesture Handler?

No. Expo Go supports Reanimated and Gesture Handler. Create custom builds only for native modules not included in Expo Go or for custom native code.

Where should route files live?

Put all route files in the app/ directory. Avoid putting components, types, or utilities inside app/—keep those in separate folders to prevent routing anti-patterns.