AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Building Native Ui

skill-expo-skills-building-native-ui · by expo

Complete guide for building beautiful apps with Expo Router. Covers fundamentals, styling, components, navigation, animations, patterns, and native tabs.

No reviews yet
0 installs
4 views
0.0% view→install

Install

$ agentstack add skill-expo-skills-building-native-ui

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-expo-skills-building-native-ui)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
1mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Building Native Ui? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Expo UI Guidelines

References

Consult these resources as needed:

references/
  animations.md          Reanimated: entering, exiting, layout, scroll-driven, gestures
  controls.md            Native iOS: Switch, Slider, SegmentedControl, DateTimePicker, Picker
  form-sheet.md          Form sheets in expo-router: configuration, footers and background interaction.
  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+)

Running the App

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:

  1. Start with Expo Go: Run npx expo start and scan the QR code with Expo Go
  2. Check if features work: Test your app thoroughly in Expo Go
  3. Only create custom builds when required - see below

When Custom Builds Are Required

You need npx expo run:ios/android or eas build ONLY when using:

  • Local Expo modules (custom native code in modules/)
  • Apple targets (widgets, app clips, extensions via @bacons/apple-targets)
  • Third-party native modules not included in Expo Go
  • Custom native configuration that can't be expressed in app.json

When Expo Go Works

Expo Go supports a huge range of features out of the box:

  • All expo-* packages (camera, location, notifications, etc.)
  • Expo Router navigation
  • Most UI libraries (reanimated, gesture handler, etc.)
  • Push notifications, deep links, and more

If you're unsure, try Expo Go first. Creating custom builds adds complexity, slower iteration, and requires Xcode/Android Studio setup.

Code Style

  • Be cautious of unterminated strings. Ensure nested backticks are escaped; never forget to escape quotes correctly.
  • Always use import statements at the top of the file.
  • Always use kebab-case for file names, e.g. comment-card.tsx
  • Always remove old route files when moving or restructuring navigation
  • Never use special characters in file names
  • Configure tsconfig.json with path aliases, and prefer aliases over relative imports for refactors.

Routes

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

  • Routes belong in the app directory.
  • Never co-locate components, types, or utilities in the app directory. This is an anti-pattern.
  • Ensure the app always has a route that matches "/", it may be inside a group route.

Library Preferences

  • Never use modules removed from React Native such as Picker, WebView, SafeAreaView, or AsyncStorage
  • Never use legacy expo-permissions
  • expo-audio not expo-av
  • expo-video not expo-av
  • expo-image with source="sf:name" for SF Symbols, not expo-symbols or @expo/vector-icons
  • react-native-safe-area-context not react-native SafeAreaView
  • process.env.EXPO_OS not Platform.OS
  • React.use not React.useContext
  • expo-image Image component instead of intrinsic element img
  • expo-glass-effect for liquid glass backdrops
  • Color from expo-router for native semantic colors, not raw PlatformColor (type-safe, auto-adapts to light/dark)
  • In SDK 56+, never import from @react-navigation/* directly — use expo-router/react-navigation instead (covers @react-navigation/native, /core, /elements, /routers)

Responsiveness

  • Always wrap root component in a scroll view for responsiveness
  • Use ` instead of ` for smarter safe area insets
  • contentInsetAdjustmentBehavior="automatic" should be applied to FlatList and SectionList as well
  • Use flexbox instead of Dimensions API
  • ALWAYS prefer useWindowDimensions over Dimensions.get() to measure screen size

Behavior

  • Use expo-haptics conditionally on iOS to make more delightful experiences
  • Use views with built-in haptics like ` from React Native and @react-native-community/datetimepicker`
  • When a route belongs to a Stack, its first child should almost always be a ScrollView with contentInsetAdjustmentBehavior="automatic" set
  • When adding a ScrollView to the page it should almost always be the first component inside the route component
  • Prefer headerSearchBarOptions in Stack.Screen options to add a search bar
  • Use the `` prop on text containing data that could be copied
  • Consider formatting large numbers like 1.4M or 38k
  • Never use intrinsic elements like 'img' or 'div' unless in a webview or Expo DOM component

Styling

Follow Apple Human Interface Guidelines.

General Styling Rules

  • Prefer flex gap over margin and padding styles
  • Prefer padding over margin where possible
  • Always account for safe area, either with stack headers, tabs, or ScrollView/FlatList contentInsetAdjustmentBehavior="automatic"
  • Ensure both top and bottom safe area insets are accounted for
  • Inline styles not StyleSheet.create unless reusing styles is faster
  • Add entering and exiting animations for state changes
  • Use { borderCurve: 'continuous' } for rounded corners unless creating a capsule shape
  • ALWAYS use a navigation stack title instead of a custom text element on the page
  • When padding a ScrollView, use contentContainerStyle padding and gap instead of padding on the ScrollView itself (reduces clipping)
  • CSS and Tailwind are not supported - use inline styles

Colors

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:

// theme/colors.ts
import { Platform } from "react-native";
import { Color } from "expo-router";

export const colors = {
  label: Platform.select({
    ios: Color.ios.label,
    android: Color.android.dynamic.onSurface,
    default: "#000000",
  })!,
  secondaryLabel: Platform.select({
    ios: Color.ios.secondaryLabel,
    android: Color.android.dynamic.onSurfaceVariant,
    default: "#3c3c43",
  })!,
  separator: Platform.select({
    ios: Color.ios.separator,
    android: Color.android.dynamic.outlineVariant,
    default: "#c6c6c8",
  })!,
  systemBackground: Platform.select({
    ios: Color.ios.systemBackground,
    android: Color.android.dynamic.surface,
    default: "#ffffff",
  })!,
  systemBlue: Platform.select({
    ios: Color.ios.systemBlue,
    android: Color.android.dynamic.primary,
    default: "#007aff",
  })!,
};
import { colors } from "@/theme/colors";

  Title
;
  • iOS re-resolves these colors automatically when the system theme changes. On Android, call useColorScheme() inside any component that renders them so it re-renders when the theme flips (required when React Compiler memoizes the component).
  • Don't pass Color / PlatformColor values into Reanimated styles — use static colors there (see references/animations.md).
  • 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.

Text Styling

  • Add the selectable prop to every `` element displaying important data or error messages
  • Counters should use { fontVariant: 'tabular-nums' } for alignment

Shadows

Use CSS boxShadow style prop. NEVER use legacy React Native shadow or elevation styles.

'inset' shadows are supported.

Navigation

Link

Use `` from 'expo-router' for navigation between routes.

import { Link } from 'expo-router';

// Basic link

// Wrapping custom components

  ...

Whenever possible, include a `` to follow iOS conventions. Add context menus and previews frequently to enhance navigation.

Stack

  • ALWAYS use _layout.tsx files to define stacks
  • Use Stack from 'expo-router/stack' for native navigation stacks

Page Title

Set the page title in Stack.Screen options:

Context Menus

Add long press context menus to Link components:

import { Link } from "expo-router";

  
    
      
    
  
  
    
    
    
       {}} />
       {}}
      />
    
  
;

Link Previews

Use link previews frequently to enhance navigation:


  
    
      
    
  
  

Link preview can be used with context menus.

Modal

Present a screen as a modal:

Prefer this to building a custom modal component.

Sheet

Present a screen as a dynamic form sheet:

  • Using contentStyle: { backgroundColor: "transparent" } makes the background liquid glass on iOS 26+.

Common route structure

A standard app layout with tabs and stacks inside each tab:

app/
  _layout.tsx — 
  (index,search)/
    _layout.tsx — 
    index.tsx — Main list
    search.tsx — Search view
// app/_layout.tsx
import { NativeTabs } from "expo-router/unstable-native-tabs";
import { ThemeProvider, DarkTheme, DefaultTheme } from "expo-router/react-navigation";
import { useColorScheme } from "react-native";

export default function Layout() {
  const colorScheme = useColorScheme();
  return (
    
      
        
          
          Items
        
        
      
    
  );
}

Create a shared group route so both tabs can push common screens:

// app/(index,search)/_layout.tsx
import { Stack } from "expo-router/stack";
import { colors } from "@/theme/colors";

export default function Layout({ segment }) {
  const screen = segment.match(/\((.*)\)/)?.[1]!;
  const titles: Record = { index: "Items", search: "Search" };

  return (
    
      
      
    
  );
}

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.