Token导航 LogoToken导航

Agent Skill · 前端设计

expo-ui(世博会用户界面)

用于辅助前端页面、组件、样式和交互逻辑的开发与维护。它适合让 Agent 生成或审查 React、Next.js、Vue、Tailwind、CSS 等相关代码,整理组件结构,或定位布局和性能问题。使用时需要结合项目现有设计系统、路由和构建方式,避免只生成孤立片段;涉及页面改动时,应配合本地预览和构建检查确认视觉效果。

expo-ui 是前端设计类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为需要联网;许可证信息还不明确,第三方扫描已通过,按安装量在前端设计类的 10,216 个 Skill 里排第 8,536。

总安装 279周安装 12GitHub Stars 1下载 98核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
unknown

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:expo-ui(世博会用户界面)
来源仓库:https://github.com/caizongyuan/efficientcc
仓库路径:skills/expo-ui
安装命令:
npx skills add https://github.com/caizongyuan/efficientcc --skill expo-ui
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/caizongyuan/efficientcc --skill expo-ui

这个 Skill 是做什么的

前端设计类 Skill 关注页面结构、视觉规范和组件写法,输出通常是可运行的界面代码。

将 SwiftUI 组件体系映射到 React Native 环境,实现原生级界面体验。提供 Button、Form、List 等基础控件与 HStack/VStack 布局容器。支持修饰符链式调用模拟 SwiftUI 声明式语法糖,降低学习成本。

仅限 iOS/macOS/tvOS 平台可用,其他平台将抛出运行时异常警告。expo-ui 属于前端设计类 Skill,可作为该场景下的辅助能力补充。

来源仓库内路径:skills/expo-ui

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 Codex、Claude、Cursor、Gemini CLI;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/caizongyuan/efficientcc --skill expo-ui;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它需要需要联网,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

expo-ui 怎么安装?

在终端执行 npx skills add https://github.com/caizongyuan/efficientcc --skill expo-ui。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

expo-ui 支持哪些 AI 工具?

公开资料标注支持 Codex、Claude、Cursor、Gemini CLI。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

expo-ui 需要什么权限?

权限级别是需要联网。该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

expo-ui 是免费的吗?

公开资料没有标注明确许可证,收费和商用限制都无法确认。正式使用前请到来源仓库核对授权说明。

expo-ui 用的人多吗?

公开数据是累计安装 279 次,近一周安装 12 次,下载 98 次,GitHub 1 星。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

expo-ui 安全吗?

第三方扫描结果为通过,但这只覆盖已知的静态检查项,不能替代自己读一遍源码和安装命令。

值得选的地方和要先想清楚的地方

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。
  • 第三方扫描已通过,常见的高风险行为没有命中。

先想清楚的地方

  • 许可证没有明确标注,能不能商用、能不能再分发都不确定。
  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理前端设计类任务、又不想每次都手写提示词的人。

不太适合

  • 需要明确授权才能用到公司项目里的场景。
  • 需要厂商级技术支持和 SLA 的生产系统。

前端设计类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
frontend-design

前端设计 · Codex、Claude

8,823,360126,072只读MIT通过
vercel-composition-patterns

Vercel 组合模式 · Codex、Claude

3,858,81625,893需要联网MIT通过
vercel-react-native-skills

Vercel React native skills 前端 · Codex、Claude

2,468,66425,878只读MIT通过
adapt

adapt 搜索 · Codex、Claude

2,027,04023,125操作浏览器未明确通过
bolder

bolder 分析 · Codex、Claude

2,004,28823,130只读未明确通过
expo-ui

世博会用户界面 · 本页

2791需要联网未明确通过

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

Expo UI

Core Functionality

Expo UI brings native SwiftUI components to React Native, providing a 1-to-1 mapping to SwiftUI views for building modern iOS, macOS, and tvOS interfaces. Use Expo UI to leverage SwiftUI's declarative syntax and native performance while maintaining React Native's flexibility.

Available in SDK 54 and above.

When to Use

Use this skill when:

  • Building native iOS interfaces with SwiftUI components from React Native
  • Integrating SwiftUI primitives like Button, Form, List, TextField, Picker
  • Creating layouts with HStack, VStack, and Spacer
  • Applying SwiftUI modifiers for styling and behavior
  • Working with glass morphism effects and advanced form controls (v10 preview)
  • Implementing native tab navigation with Expo Router
  • Mixing SwiftUI components with React Native views

Platform Support

Supported Platforms:

  • iOS (primary platform)
  • macOS
  • tvOS

Requirements:

  • SDK 54+
  • Development builds (not available in Expo Go)
  • iOS 26+ and Xcode 26+ for glassEffect modifier
Roadmap: Android (Jetpack Compose) and Web (DOM) support planned for future releases.

Version Notice

⚠️ Version Conflict: Official documentation is based on v9 (stable, ~0.2.0-beta.7). Local examples use v10 which may contain breaking changes. Always check TypeScript types for current API signatures. When using v10 preview features (marked with ⚠️), be aware that APIs may differ from v9 stable documentation.

Installation

Install the package in your Expo project:

npx expo install @expo/ui

For existing React Native apps, ensure expo is installed first.

Module Overview

Getting Started Module

  • Focus: Installation, SDK requirements, platform support
  • Key APIs: @expo/ui, @expo/ui/swift-ui, Host component
  • Documentation: references/expo-ui-guide.md

Core Components Module

  • Functionality: Basic SwiftUI primitives and Host container
  • Key APIs: Host, Button, Text, Image, CircularProgress, LinearProgress
  • Documentation: references/expo-ui-api-reference.md

Layouts Module

  • Functionality: SwiftUI layout system and container components
  • Key APIs: HStack, VStack, Form, Section, List, Spacer
  • Documentation: references/expo-ui-guide.md, references/expo-ui-api-reference.md

Input Controls Module

  • Functionality: User input and selection components
  • Key APIs: Picker, Slider, Switch, TextField, ColorPicker, DateTimePicker
  • Documentation: references/expo-ui-api-reference.md, references/expo-ui-v10-examples.md

Advanced Components Module

  • Functionality: Specialized UI components
  • Key APIs: BottomSheet, ContextMenu, Gauge, CircularProgress, LinearProgress
  • Documentation: references/expo-ui-api-reference.md

Modifiers Module

  • Functionality: SwiftUI modifier system for styling and behavior
  • Key APIs: padding, frame, background, glassEffect, buttonStyle, controlSize
  • Documentation: references/expo-ui-guide.md, references/expo-ui-v10-examples.md

v10 Preview Module

  • Functionality: Preview features in v10 (may contain breaking changes)
  • Key APIs: GlassEffectContainer, LabeledContent, DisclosureGroup, ContentUnavailableView
  • Documentation: references/expo-ui-v10-examples.md

Interop Module

  • Functionality: Integration between React Native and SwiftUI
  • Key APIs: Host, UIHostingController, UIViewRepresentable
  • Documentation: references/expo-ui-guide.md

Core Concepts

Host Component

The <Host> component bridges React Native (UIKit) and SwiftUI. Use UIHostingController to render SwiftUI views within React Native.

Key characteristics:

  • Acts like <svg> in DOM or <Canvas> in react-native-skia
  • Accepts style prop for flexbox layouts
  • Use matchContents prop to size to children
  • Required to enter SwiftUI context

Basic usage:

import { CircularProgress, Host } from '@expo/ui/swift-ui';

<Host matchContents>
  <CircularProgress />
</Host>

With flexbox layout:

<Host style={{ flex: 1, margin: 32 }}>
  <VStack spacing={32}>
    <Text>Hello, world!</Text>
    <Button onPress={handlePress}>Click</Button>
  </VStack>
</Host>

Layout System

SwiftUI uses a different layout system than React Native's flexbox. Use these components for layouts:

HStack and VStack:

  • Arrange children horizontally or vertically
  • Use spacing prop for consistent gaps
  • Supports nesting for complex layouts
import { HStack, VStack, Spacer } from '@expo/ui/swift-ui';

<VStack spacing={16}>
  <HStack spacing={8}>
    <Text>Label</Text>
    <Spacer />
    <Switch value={enabled} onValueChange={setEnabled} />
  </HStack>
</VStack>

Form and Section:

  • Group related controls together
  • Automatically styled for iOS Settings appearance
  • Use <Section> to create grouped content
<Form>
  <Section>
    <Text>Settings</Text>
    <Switch label="Airplane Mode" value={mode} onValueChange={setMode} />
  </Section>
</Form>

Important: Flexbox styles only work on <Host> component, not inside SwiftUI context. Use HStack/VStack for layouts once inside SwiftUI.

Modifiers System

SwiftUI modifiers customize appearance and behavior. Import from @expo/ui/swift-ui/modifiers and apply via modifiers prop.

import { Text, Host } from '@expo/ui/swift-ui';
import { padding, frame, background } from '@expo/ui/swift-ui/modifiers';

<Text
  size={32}
  modifiers={[
    padding({ all: 16 }),
    frame({ width: 200, height: 100 }),
    background('#007aff')
  ]}>
  Styled Text
</Text>

Common modifiers:

  • padding() - Add insets around content
  • frame() - Set explicit dimensions
  • background() - Set background color or content
  • clipShape() - Clip to shapes (roundedRectangle, circle, etc.)

Components

Basic Components

Button

Platform: iOS, tvOS (borderless variant not available on Apple TV)

import { Button, Host } from '@expo/ui/swift-ui';

<Host style={{ flex: 1 }}>
  <Button variant="default" onPress={handlePress}>
    Edit profile
  </Button>
</Host>

Props:

  • onPress?: () => void - Callback when button is pressed
  • systemImage?: SFSymbol - SF Symbol name (only used if children is a string)
  • role?: 'default' | 'cancel' | 'destructive' - Button role (iOS only)
  • controlSize?: 'mini' | 'small' | 'regular' | 'large' | 'extraLarge' - Control size
  • variant?: ButtonVariant - Button style variant
  • children?: string | React.ReactNode - Button content
  • color?: string - Button color
  • disabled?: boolean - Disabled state

Variants: default, bordered, borderless, borderedProminent, plain, glass, glassProminent, accessoryBar, accessoryBarAction, card, link

Text

Platform: iOS, macOS, tvOS

import { Text, Host } from '@expo/ui/swift-ui';

<Host>
  <Text size={24} weight="bold" color="primary">
    Hello, world!
  </Text>
</Host>

Props:

  • children: string - Text content (must be a string, not React.ReactNode)
  • size?: number - Font size
  • weight?: 'ultraLight' | 'thin' | 'light' | 'regular' | 'medium' | 'semibold' | 'bold' | 'heavy' | 'black' - Font weight
  • design?: 'default' | 'rounded' | 'serif' | 'monospaced' - Font design
  • lineLimit?: number - Maximum number of lines
  • color?: string - Text color

Important: Text children must be a string. Use individual size and weight props instead of a font object.

Image
import { Image, Host } from '@expo/ui/swift-ui';

<Host>
  <Image
    systemName="airplane"
    color="white"
    size={18}
    modifiers={[
      frame({ width: 28, height: 28 }),
      background('#ffa500'),
      clipShape('roundedRectangle')
    ]}
  />
</Host>
CircularProgress and LinearProgress
import { CircularProgress, LinearProgress, Host } from '@expo/ui/swift-ui';

<Host style={{ width: 300 }}>
  <CircularProgress progress={0.5} color="blue" />
  <LinearProgress progress={0.7} color="orange" />
</Host>

Form Controls

Switch (Toggle)

Platform: iOS, tvOS

import { Switch, Host } from '@expo/ui/swift-ui';

<Host matchContents>
  <Switch
    checked={checked}
    onValueChange={setChecked}
    label="Play music"
    variant="switch"
    color="#ff0000"
  />
</Host>

Variants: switch (toggle), checkbox

TextField

Platform: iOS, tvOS

import { TextField, Host } from '@expo/ui/swift-ui';

<Host matchContents>
  <TextField
    autocorrection={false}
    defaultValue="Enter text"
    onChangeText={setValue}
  />
</Host>
Picker

Platform: iOS (wheel not available on Apple TV)

import { Picker, Host } from '@expo/ui/swift-ui';

<Host matchContents>
  <Picker
    options={['$', '$$', '$$$', '$$$$']}
    selectedIndex={selectedIndex}
    onOptionSelected={({ nativeEvent: { index } }) => setSelectedIndex(index)}
    variant="segmented"
  />
</Host>

Variants: segmented, wheel, menu

Slider

Platform: iOS (not available on Apple TV)

import { Slider, Host } from '@expo/ui/swift-ui';

<Host style={{ minHeight: 60 }}>
  <Slider value={value} onValueChange={setValue} />
</Host>
ColorPicker

Platform: iOS (not available on Apple TV)

import { ColorPicker, Host } from '@expo/ui/swift-ui';

<Host style={{ width: 400, height: 200 }}>
  <ColorPicker
    label="Select a color"
    selection={color}
    onValueChanged={setColor}
  />
</Host>
DateTimePicker

Platform: iOS (not available on Apple TV)

import { DateTimePicker, Host } from '@expo/ui/swift-ui';

<Host matchContents>
  <DateTimePicker
    onDateSelected={setDate}
    displayedComponents="date"
    initialDate={date.toISOString()}
    variant="wheel"
  />
</Host>

Components: date (default) or hourAndMinute for time

Advanced Components

BottomSheet

Platform: iOS

import { BottomSheet, Host, Text } from '@expo/ui/swift-ui';

<Host style={{ position: 'absolute', width }}>
  <BottomSheet isOpened={isOpened} onIsOpenedChange={setIsOpened}>
    <Text>Hello, world!</Text>
  </BottomSheet>
</Host>
ContextMenu

Platform: iOS, tvOS

Also known as DropdownMenu
import { ContextMenu, Button, Picker, Host } from '@expo/ui/swift-ui';

<Host style={{ width: 150, height: 50 }}>
  <ContextMenu>
    <ContextMenu.Items>
      <Button systemImage="person.crop.circle" onPress={handleAction}>
        Hello
      </Button>
      <Picker label="Options" options={options} variant="menu" />
    </ContextMenu.Items>
    <ContextMenu.Trigger>
      <Button variant="bordered">Show Menu</Button>
    </ContextMenu.Trigger>
  </ContextMenu>
</Host>
Gauge

Platform: iOS (not available on Apple TV)

import { Gauge, Host, PlatformColor } from '@expo/ui/swift-ui';

<Host matchContents>
  <Gauge
    min={{ value: 0, label: '0' }}
    max={{ value: 1, label: '1' }}
    current={{ value: 0.5 }}
    color={[
      PlatformColor('systemRed'),
      PlatformColor('systemYellow'),
      PlatformColor('systemGreen')
    ]}
    type="circularCapacity"
  />
</Host>
List

Platform: iOS, tvOS

import { List, LabelPrimitive, Host } from '@expo/ui/swift-ui';

<Host style={{ flex: 1 }}>
  <List
    editModeEnabled={editMode}
    onSelectionChange={(items) => alert(`Selected: ${items.join(', ')}`)}
    moveEnabled={canMove}
    onMoveItem={(from, to) => alert(`Moved ${from} to ${to}`)}
    onDeleteItem={(index) => alert(`Deleted ${index}`)}
    listStyle="automatic"
  >
    {data.map((item, index) => (
      <LabelPrimitive
        key={index}
        title={item.text}
        systemImage={item.systemImage}
      />
    ))}
  </List>
</Host>

Features: Edit mode, move, delete, selection

Modifiers

Common Modifiers

Import from @expo/ui/swift-ui/modifiers:

import { padding, frame, background, clipShape } from '@expo/ui/swift-ui/modifiers';

Layout modifiers:

  • padding({all, horizontal, vertical, top, bottom, leading, trailing}) - Add spacing
  • frame({width, height, minWidth, maxWidth, minHeight, maxHeight}) - Set dimensions
  • fixedSize() - Prevent automatic sizing

Styling modifiers:

  • background(color or content) - Set background
  • foregroundStyle(color) - Set foreground color
  • clipShape(shape) - Clip to shape (roundedRectangle, circle, capsule)
  • cornerRadius(radius) - Round corners
  • font({size, weight, design}) - Font styling (Note: Text component uses individual props: size, weight, design)

Behavior modifiers:

  • disabled(bool) - Enable/disable interaction
  • animation(Animation.spring(), condition) - Animate changes

Glass Morphism Modifiers

⚠️ v10 Preview: glassEffect requires Xcode 26+ and iOS 26+. May differ from v9 APIs.
import { glassEffect, glassEffectId, animation, Animation } from '@expo/ui/swift-ui/modifiers';

<Text
  size={16}
  weight="medium"
  modifiers={[
    padding({ all: 16 }),
    glassEffect({
      glass: { variant: 'clear' }
    })
  ]}>
  Glass effect text
</Text>

Advanced glass effects with namespace:

import { Namespace } from '@expo/ui/swift-ui';

const namespaceId = useId();

<Namespace id={namespaceId}>
  <Image
    systemName="paintbrush.fill"
    modifiers={[
      glassEffect({ glass: { variant: 'clear' } }),
      glassEffectId('uniqueId', namespaceId),
      animation(Animation.spring({ duration: 0.8 }), isExpanded)
    ]}
  />
</Namespace>

v10 Preview Modifiers ⚠️

⚠️ v10 Preview: These modifiers may have different APIs in v9 stable. Check TypeScript types.

Button style modifiers:

  • buttonStyle('glass' | 'glassProminent' | 'bordered' | 'borderless' | 'borderedProminent' | 'plain')
  • controlSize('mini' | 'small' | 'regular' | 'large' | 'extraLarge')
  • labelStyle('iconOnly' | 'titleAndIcon' | 'titleOnly')
<Button
  modifiers={[
    buttonStyle('glass'),
    controlSize('large')
  ]}>
  Glass Button
</Button>

Form control modifiers:

  • pickerStyle('menu' | 'segmented' | 'wheel')
  • tag(value) - Tag picker options for selection
  • font({size}) - Font customization
<Picker
  modifiers={[pickerStyle('menu')]}
  selection={selectedIndex}
  onSelectionChange={setSelectedIndex}>
  {options.map((option, index) => (
    <Text key={index} modifiers={[tag(index)]}>
      {option}
    </Text>
  ))}
</Picker>

v10 Preview Features ⚠️

⚠️ v10 Preview: The following components and features are from v10 and may contain breaking changes from v9 stable APIs. Always check TypeScript types for current signatures.

GlassEffectContainer

Platform: iOS/macOS

Container component for glass morphism effects with coordinated animations.

import { GlassEffectContainer, Namespace } from '@expo/ui/swift-ui';

const namespaceId = useId();

<Host style={{ flex: 1 }}>
  <Namespace id={namespaceId}>
    <GlassEffectContainer
      spacing={30}
      modifiers={[
        padding({ all: 30 }),
        cornerRadius(20)
      ]}>
      <VStack spacing={25}>
        <Text>Glass content</Text>
      </VStack>
    </GlassEffectContainer>
  </Namespace>
</Host>

LabeledContent

Wrapper for labeled form elements with multi-part label support.

import { LabeledContent } from '@expo/ui/swift-ui';

<LabeledContent label="Name">
  <Text>John Doe</Text>
</LabeledContent>

<LabeledContent
  label={
    <>
      <Text>Title</Text>
      <Text>Subtitle</Text>
    </>
  }>
  <Text>Value</Text>
</LabeledContent>

DisclosureGroup

Expandable/collapsible content sections.

import { DisclosureGroup } from '@expo/ui/swift-ui';

<DisclosureGroup
  isExpanded={isExpanded}
  onStateChange={setIsExpanded}
  label="Show Details">
  <Text>Detailed content here</Text>
</DisclosureGroup>

ContentUnavailableView

Empty state or placeholder view with icon, title, and description.

import { ContentUnavailableView } from '@expo/ui/swift-ui';

<ContentUnavailableView
  title="No items"
  systemImage="tray"
  description="Add items to get started."
/>

Enhanced Button Styles (v10)

import { buttonStyle, controlSize, fixedSize } from '@expo/ui/swift-ui/modifiers';

<Button
  label="Glass"
  modifiers={[
    buttonStyle('glass'),
    controlSize('large'),
    fixedSize()
  ]}
/>

<Button
  label="Glass Prominent"
  modifiers={[
    buttonStyle('glassProminent'),
    controlSize('extraLarge')
  ]}
/>

Enhanced Form Features (v10)

ColorPicker with opacity:

<ColorPicker
  label="Select color"
  selection={color}
  supportsOpacity
  onValueChanged={setColor}
/>

Picker with menu style and tags:

import { pickerStyle, tag } from '@expo/ui/swift-ui/modifiers';

<Picker
  label="Menu Picker"
  modifiers={[pickerStyle('menu')]}
  selection={selectedIndex}
  onSelectionChange={setSelectedIndex}>
  {options.map((option, index) => (
    <Text key={index} modifiers={[tag(index)]}>
      {option}
    </Text>
  ))}
</Picker>

Interop with React Native

Using React Native Components in SwiftUI

React Native components can be nested inside Expo UI components. Expo UI automatically creates a UIViewRepresentable wrapper.

import { Text, Host, VStack } from '@expo/ui/swift-ui';
import { View } from 'react-native';

<Host>
  <VStack spacing={8}>
    <Text>SwiftUI Text</Text>
    <View style={{ width: 100, height: 100, backgroundColor: 'blue' }} />
  </VStack>
</Host>

Important limitations:

  • SwiftUI controls layout properties (center, bounds, frame, transform) - don't set these directly on React Native views
  • Once React Native components are rendered, the SwiftUI context is left
  • Add a new <Host> wrapper to return to Expo UI components
  • Best practice: Keep SwiftUI layouts self-contained with clear boundaries

Platform-Specific Notes

iOS-only components:

  • BottomSheet
  • ColorPicker
  • DateTimePicker (wheel variant)
  • Gauge
  • Slider
  • Glass effects (iOS 26+, Xcode 26+)

Apple TV exclusions:

  • Button borderless variant
  • ColorPicker
  • DateTimePicker
  • Gauge
  • Slider

Common patterns:

  • Use Platform.OS checks for platform-specific code
  • Provide fallbacks for unsupported platforms
  • Test on tvOS for tvOS-specific behaviors (focus, remote control)

Native Tabs with Expo Router

Native tabs provide platform-native tab navigation using system tab bars. Unlike JavaScript tabs, native tabs follow platform conventions and offer native performance.

Native tabs is experimental (SDK 54+, API subject to change). For fully custom designs, consider JavaScript tabs instead.

When to Use Native Tabs

Choose native tabs for:

  • Platform-native navigation feel (iOS UITabBarController, Android Material Tabs)
  • Native performance and system integration
  • Platform-specific features (iOS 26+ liquid glass effects, minimize behavior)
  • Standard tab patterns without extensive customization

Choose JavaScript/custom tabs for:

  • Complete visual control over tab bar appearance
  • Complex animations or custom layouts
  • Consistent cross-platform appearance

Installation

Ensure expo-router is installed and configured:

npx expo install expo-router

app.json configuration:

{
  "expo": {
    "plugins": ["expo-router"]
  }
}

Basic Usage

Create a tab layout using file-based routing:

File structure:

app/
├── _layout.tsx
├── index.tsx
└── settings.tsx

app/_layout.tsx:

import { NativeTabs, Icon, Label } from 'expo-router/unstable-native-tabs';

export default function TabLayout() {
  return (
    <NativeTabs>
      <NativeTabs.Trigger name="index">
        <Label>Home</Label>
        <Icon sf="house.fill" drawable="ic_home" />
      </NativeTabs.Trigger>
      <NativeTabs.Trigger name="settings">
        <Icon sf="gear" drawable="ic_settings" />
        <Label>Settings</Label>
      </NativeTabs.Trigger>
    </NativeTabs>
  );
}
Tabs are not automatically added. Explicitly define each tab with NativeTabs.Trigger.

Customize Tab Items

Icons:

SF Symbols (iOS):

<Icon sf={{ default: 'house', selected: 'house.fill' }} />

Android drawables:

<Icon drawable="ic_home" />

Custom images:

<Icon src={require('./assets/home.png')} />
<Icon src={{
  default: require('./assets/home.png'),
  selected: require('./assets/home-active.png')
}} />

Labels:

<Label>Home</Label>           // Show label
<Label hidden />               // Hide label

Badges:

<Badge>9+</Badge>              // Text badge
<Badge />                      // Dot badge (no text)

Tab Bar Styling

Global styling:

<NativeTabs
  backgroundColor="white"
  iconColor="blue"
  tintColor="purple"
  blurEffect="systemMaterial"
  labelStyle={{ fontSize: 12, fontWeight: '600' }}
  labelVisibilityMode="auto"
  minimizeBehavior="automatic"
>

Per-tab styling:

<NativeTabs.Trigger name="page">
  <NativeTabs.Trigger.TabBar
    backgroundColor="white"
    iconColor="red"
  />
  <Label>Page</Label>
</NativeTabs.Trigger>

Key props:

  • backgroundColor: Tab bar background color
  • iconColor: Default icon color
  • tintColor: Selected icon color
  • blurEffect: iOS blur effect ('systemMaterial', 'extraLight', 'dark', etc.)
  • labelStyle: Typography settings
  • labelVisibilityMode: Android label visibility ('auto' | 'selected' | 'labeled' | 'unlabeled')
  • minimizeBehavior: iOS 26+ minimize behavior ('automatic' | 'never' | 'onScrollDown' | 'onScrollUp')
  • backBehavior: Android back button behavior ('history' | 'none' | 'initialRoute')

Advanced Features

Hide tabs conditionally:

const shouldHideMessagesTab = true;

<NativeTabs.Trigger name="messages" hidden={shouldHideMessagesTab} />
Hidden tabs cannot be navigated to in any way.

Disable pop to top (iOS):

<NativeTabs.Trigger name="index" disablePopToTop>
  <Label>Home</Label>
</NativeTabs.Trigger>

Disable scroll to top (iOS):

<NativeTabs.Trigger name="index" disableScrollToTop>
  <Label>Home</Label>
</NativeTabs.Trigger>

iOS 26+ search tab:

<NativeTabs.Trigger name="search" role="search">
  <Label>Search</Label>
</NativeTabs.Trigger>

iOS 26+ tab bar minimize:

<NativeTabs minimizeBehavior="onScrollDown">
  <NativeTabs.Trigger name="index">
    <Label>Home</Label>
  </NativeTabs.Trigger>
</NativeTabs>

Vector icons integration:

import MaterialIcons from '@expo/vector-icons/MaterialIcons';
import { VectorIcon } from 'expo-router/unstable-native-tabs';
import { Platform } from 'react-native';

<NativeTabs.Trigger name="index">
  <Label>Home</Label>
  {Platform.select({
    ios: <Icon sf="house.fill" />,
    android: <Icon src={<VectorIcon family={MaterialIcons} name="home" />} />,
  })}
</NativeTabs.Trigger>

Platform-Specific Considerations

Android:

  • Limit of 5 tabs (Material Design constraint)
  • Uses Material Tabs component
  • Supports ripple effects and indicators

iOS:

  • No tab limit (5 recommended for UX)
  • Uses UITabBarController
  • Supports blur effects and liquid glass (iOS 26+)
  • Separate search tab support (iOS 26+)
  • Role-based system tabs (search, bookmarks, contacts, etc.)

tvOS:

  • Follows tvOS platform conventions
  • Different tab bar positioning

Known limitations:

  • No nested native tabs (can nest JavaScript tabs inside native tabs)
  • Limited FlatList support for scroll-to-top and minimize-on-scroll
  • Cannot measure tab bar height (varies by device: iPad, Vision Pro, etc.)
  • For FlatList scroll edge issues, use disableTransparentOnScrollEdge prop

Migration from JavaScript Tabs

Key differences:

JavaScript TabsNative Tabs
Tabs.ScreenNativeTabs.Trigger
Options objectChild components (<Icon>, <Label>)
Automatic tabsExplicit trigger definition
Mock headersUse nested <Stack />

Before (JavaScript tabs):

<Tabs.Screen
  name="home"
  options={{
    tabBarIcon: ({ focused, color, size }) => (
      <Icon name="home" color={color} size={size} />
    ),
  }}
/>

After (Native tabs):

<NativeTabs.Trigger name="home">
  <Icon sf="house.fill" />
  <Label>Home</Label>
</NativeTabs.Trigger>

Use Stacks inside tabs:

Native tabs don't have mock headers. Nest Stack layouts:

// app/home/_layout.tsx
import { Stack } from 'expo-router';

export default function HomeLayout() {
  return (
    <Stack>
      <Stack.Screen name="index" options={{ title: 'Home' }} />
      <Stack.Screen name="details" options={{ title: 'Details' }} />
    </Stack>
  );
}

Common Workflows

Conditional tabs based on auth:

const { user } = useAuth();

return (
  <NativeTabs>
    <NativeTabs.Trigger name="index">
      <Label>Home</Label>
    </NativeTabs.Trigger>
    {user && (
      <NativeTabs.Trigger name="profile">
        <Label>Profile</Label>
      </NativeTabs.Trigger>
    )}
  </NativeTabs>
);

Badge updates:

const [unreadCount, setUnreadCount] = useState(0);

<NativeTabs.Trigger name="messages">
  {unreadCount > 0 && (
    <Badge>{unreadCount > 99 ? '99+' : unreadCount}</Badge>
  )}
  <Label>Messages</Label>
</NativeTabs.Trigger>

Common Workflows

Build an iOS Settings Interface

import {
  Form,
  Section,
  HStack,
  VStack,
  Button,
  Switch,
  Text,
  Image,
  Spacer,
  Host
} from '@expo/ui/swift-ui';
import { background, clipShape, frame } from '@expo/ui/swift-ui/modifiers';

function SettingsView() {
  const [airplaneMode, setAirplaneMode] = useState(true);

  return (
    <Host style={{ flex: 1 }}>
      <Form>
        <Section>
          <HStack spacing={8}>
            <Image
              systemName="airplane"
              color="white"
              size={18}
              modifiers={[
                frame({ width: 28, height: 28 }),
                background('#ffa500'),
                clipShape('roundedRectangle')
              ]}
            />
            <Text>Airplane Mode</Text>
            <Spacer />
            <Switch value={airplaneMode} onValueChange={setAirplaneMode} />
          </HStack>
        </Section>
      </Form>
    </Host>
  );
}

Create a Progress View

import { CircularProgress, LinearProgress, VStack, Host } from '@expo/ui/swift-ui';

function ProgressView() {
  return (
    <Host style={{ flex: 1, margin: 32 }}>
      <VStack spacing={32}>
        <CircularProgress progress={0.5} color="blue" />
        <LinearProgress progress={0.7} color="orange" />
      </VStack>
    </Host>
  );
}

Build a Form with Validation

import {
  Form,
  Section,
  TextField,
  Switch,
  Picker,
  Button,
  Host
} from '@expo/ui/swift-ui';

function FormView() {
  const [name, setName] = useState('');
  const [enabled, setEnabled] = useState(true);
  const [category, setCategory] = useState(0);

  return (
    <Host style={{ flex: 1 }}>
      <Form>
        <Section header="User Information">
          <TextField
            label="Name"
            defaultValue={name}
            onChangeText={setName}
          />
          <Switch
            label="Enabled"
            value={enabled}
            onValueChange={setEnabled}
          />
          <Picker
            label="Category"
            options={['General', 'Advanced', 'Expert']}
            selectedIndex={category}
            onOptionSelected={({ nativeEvent: { index } }) => setCategory(index)}
            variant="segmented"
          />
        </Section>
      </Form>
    </Host>
  );
}

Resource References

For detailed API specifications, code examples, and implementation guides, refer to:

  • Installation and usage guide: references/expo-ui-guide.md

- Detailed installation steps - Host component explanation - Layout system (HStack, VStack) - Modifiers system - Common questions and FAQs

  • Complete API reference: references/expo-ui-api-reference.md

- All components with examples - Platform support details - Props and events documentation - Code snippets for each component

  • v10 preview features: references/expo-ui-v10-examples.md

- Glass morphism effects (GlassEffectContainer, glassEffect) - New button styles (glass, glassProminent) - Enhanced form components (LabeledContent, DisclosureGroup) - Breaking change warnings for v10

  • Native tabs guide: references/native-tabs-guide.md

- Native tabs installation and setup - Platform-specific behaviors (iOS, Android, tvOS) - Advanced features (iOS 26+ search, minimize behavior) - Migration from JavaScript tabs - Common patterns and workflows

  • Native tabs API reference: references/native-tabs-api.md

- Complete component API (NativeTabs, Icon, Label, Badge) - Props and types documentation - Platform-specific props - Vector icons integration - Known limitations and workarounds

Best Practices

  1. Use Host boundaries wisely - Wrap SwiftUI content in <Host> and keep SwiftUI layouts self-contained
  2. Prefer v9 stable APIs - Use documented v9 APIs unless v10 features are specifically needed
  3. Check platform support - Some components are iOS-only, provide fallbacks for tvOS
  4. Leverage TypeScript - Always check TypeScript types for accurate API signatures, especially for v10
  5. Use modifiers for styling - Apply modifiers via the modifiers prop rather than inline styles
  6. Test on real devices - Some features (like glass effects) require specific iOS/Xcode versions
  7. Mind the layout transition - Remember that flexbox stops working inside SwiftUI context
  8. Keep interop boundaries clear - Minimize mixing React Native and SwiftUI for better performance

平台分布

Codex

32.52%
按下载量换算32

Claude

32.56%
按下载量换算32

Cursor

19.54%
按下载量换算19

Gemini CLI

8.52%
按下载量换算8

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

需要联网

该 Skill 可能需要联网访问来源站点、仓库或外部 API;具体网络访问范围需要结合源码和 README 复核。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。目前只有一个公开来源,建议同时核对源仓库。

继续浏览同类 Skills