All skills
expo avatar

/expo-ui

@472d040 official
by expoexpo/skills2.6k stars
156

Framework (OSS). Build native UI with the @expo/ui package: real SwiftUI on iOS and Jetpack Compose on Android. Default to @expo/ui for sheets (BottomSheet), pickers, sliders, toggles, menus, and grouped-form sections — do NOT reach for Reanimated, @gorhom/bottom-sheet, or RN built-in Picker/Switch; use @expo/ui instead. Fall back to RN built-ins only when @expo/ui is missing the component. NOTE: @expo/ui List renders native grouped rows like an iOS Settings screen — it is NOT a virtualized list; use FlatList/FlashList for large datasets. Covers universal components (Host, Column, Row, Button, Text, List, BottomSheet, FieldGroup, Switch, Slider, Picker, Menu), drop-in replacements for RN community libraries, and platform-specific SwiftUI/Jetpack Compose trees. Not for Expo Router navigation, Reanimated, or data fetching.

Use this Skill: https://skilld.dev/gh/expo/skills/expo-ui

This session only. Nothing lands on disk.

referencesswift-ui.md

≈1.1k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Platform-specific iOS UI: @expo/ui/swift-ui

iOS only. Code that imports from @expo/ui/swift-ui will crash on Android with "Unable to get view config" errors. Always place this code in an .ios.tsx component file or guard it with Platform.OS === 'ios'. Host must be imported from @expo/ui (the universal root), not from @expo/ui/swift-ui.

Use this layer only when the universal @expo/ui components don't cover what you need on iOS (see ./universal.md first). This requires a platform-specific tree.

File placement with Expo Router

Do not put .ios.tsx files inside app/ or src/app/. Expo Router does not support platform-extension suffixes for route files and will throw a "no fallback sibling" Render Error.

Place platform-specific component files in components/ (or any directory outside the route tree), then import them from a regular route file:

src/components/ProfileEditor.ios.tsx   ← SwiftUI tree lives here
src/app/profile-editor.tsx             ← regular Expo Router route, imports the component

src/app/profile-editor.tsx:

import ProfileEditor from '../components/ProfileEditor';
export default ProfileEditor;

Alternatively, keep everything in one regular route file and branch on Platform.OS:

// src/app/profile-editor.tsx
import { Platform } from 'react-native';
// import SwiftUI components only when on iOS to avoid Android crash
const SwiftUIForm = Platform.OS === 'ios' ? require('../components/ProfileEditor.ios').default : null;

Or more simply, put the Platform.OS guard and the SwiftUI import in the same route file (safe because Metro only bundles .ios.tsx imports on iOS builds when using platform extensions in components/).

Instructions

  • Expo UI's API mirrors SwiftUI's API. Use SwiftUI knowledge to decide which components or modifiers to use.
  • Components are imported from @expo/ui/swift-ui, modifiers from @expo/ui/swift-ui/modifiers.
  • Before writing any code, run the list-components script to get the exact components and modifiers available in the installed version:
    node <skill-root>/scripts/list-components.js <project-path>          # names only (compact)
    node <skill-root>/scripts/list-components.js <project-path> --docs   # with one-line descriptions
    (<skill-root> is the directory containing this references/ folder.)
  • The installed package's TypeScript types (.d.ts) are the most reliable source of truth for prop shapes and signatures — read the relevant {Component}/index.d.ts from the installed @expo/ui/swift-ui package in node_modules. Use the docs below as the human-readable reference.
  • When about to use a component, fetch its docs to confirm the API — https://docs.expo.dev/versions/latest/sdk/ui/swift-ui/{component-name}/index.md
  • When unsure about a modifier's API, refer to the docs — https://docs.expo.dev/versions/latest/sdk/ui/swift-ui/modifiers/index.md
  • Every SwiftUI tree must be wrapped in Host.
  • RNHostView is specifically for embedding RN components inside a SwiftUI tree. Example:
import { Host } from "@expo/ui";                       // Host always from universal root
import { VStack, RNHostView } from "@expo/ui/swift-ui"; // platform components from swift-ui
import { Pressable } from "react-native";

<Host matchContents>
  <VStack>
    <RNHostView matchContents>
      // Here, `Pressable` is an RN component so it is wrapped in `RNHostView`.
      <Pressable />
    </RNHostView>
  </VStack>
</Host>;

useNativeState

useNativeState creates observable state that updates synchronously on the UI thread via worklets, enabling immediate native state changes without waiting for a React render cycle. Requires react-native-worklets — without it updates still go through React and flickering remains. Best for real-time interactions where synchronous updates matter, e.g. a text field that masks or formats input as the user types.

Source: SKILL.md on GitHub

No alerts1mo3 checks · Risk SAFE
  • Gen Agent Trust Hub1mo

    This skill is safe and provides legitimate tools for building native UI with Expo. It includes a helper script for component discovery and a feedback utility from the official vendor.

  • Socket1mo

    No alerts

  • Snyk1mo

    Risk: LOW · No issues

Signed by skilld at 472d040. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 3 days ago.

Activeupdated last month
What it can do
Runs commands
version
1.0.0
All 1 allowed tools
Bash(node *expo-ui/scripts/list-components.js *)
  • expo
  • react-native
  • swiftui
  • jetpack-compose
  • ui-components
  • ios
  • android
  • native
  • cross-platform

README badge

README badge for expo/skills/expo-ui

Renders native UI from React using SwiftUI on iOS and Jetpack Compose on Android via the @expo/ui package. Includes universal cross-platform components (Host, Column, Row, Button, Text, List) for a single code tree, platform-specific APIs for iOS and Android when needed, and drop-in replacements for popular React Native community UI libraries like BottomSheet and DateTimePicker.

Generated from the current SKILL.md.

Does @expo/ui work in Expo Go?
Yes, on SDK 56+ the universal layer runs directly in Expo Go with `npx expo start`. On SDK 55 and earlier, you need to build a dev client with `npx expo run:ios` or `npx expo run:android`.
Can I use @expo/ui with Expo Router?
No. This skill does not cover Expo Router navigation—it focuses only on native UI components and rendering.
Do I have to write separate iOS and Android code?
Not necessarily. The universal components in @expo/ui run the same tree on both platforms. You only split into platform-specific SwiftUI or Jetpack Compose files if the universal layer lacks a component or modifier you need.
Can I migrate from React Native community UI libraries?
Yes. @expo/ui ships drop-in replacements for popular libraries like @gorhom/bottom-sheet and @react-native-community/datetimepicker, imported from @expo/ui/community/<name>.
Does this skill cover custom native modules or Reanimated?
No. This skill is limited to @expo/ui components, modifiers, and drop-in replacements—not custom native code, Reanimated, or data fetching.

Generated from the current SKILL.md. These answers refresh after source changes.