building-native-ui by expo/skills
npx skills add https://github.com/expo/skills --skill building-native-ui根据需要查阅以下资源:
references/
animations.md Reanimated:进入、退出、布局、滚动驱动、手势动画
controls.md 原生 iOS 控件:Switch、Slider、SegmentedControl、DateTimePicker、Picker
form-sheet.md expo-router 中的表单页:配置、页脚和背景交互
gradients.md 通过 experimental_backgroundImage 实现的 CSS 渐变(仅限新架构)
icons.md 通过 expo-image (sf: source) 使用的 SF Symbols:名称、动画、权重
media.md 相机、音频、视频和文件保存
route-structure.md 路由约定、动态路由、分组、文件夹组织
search.md 带标题的搜索栏、useSearch 钩子、过滤模式
storage.md SQLite、AsyncStorage、SecureStore
tabs.md NativeTabs、从 JS 标签页迁移、iOS 26 功能
toolbar-and-headers.md 堆栈标题和工具栏按钮、菜单、搜索(仅限 iOS)
visual-effects.md 模糊效果 (expo-blur) 和液态玻璃效果 (expo-glass-effect)
webgpu-three.md 使用 WebGPU 和 Three.js 的 3D 图形、游戏、GPU 可视化
zoom-transitions.md Apple Zoom:使用 Link.AppleZoom 实现的流畅缩放过渡(iOS 18+)
关键提示:在创建自定义构建之前,务必先尝试使用 Expo Go。
大多数 Expo 应用无需任何自定义原生代码即可在 Expo Go 中运行。在运行 npx expo run:ios 或 npx expo run:android 之前:
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
npx expo start 并使用 Expo Go 扫描二维码仅当使用以下内容时才需要 npx expo run:ios/android 或 eas build:
modules/ 中的自定义原生代码)@bacons/apple-targets 实现的小部件、App Clip、扩展)app.json 中表达的自定义原生配置Expo Go 开箱即用地支持大量功能:
expo-* 包(相机、定位、通知等)如果不确定,请先尝试 Expo Go。 创建自定义构建会增加复杂性、降低迭代速度,并且需要设置 Xcode/Android Studio。
comment-card.tsx详细的路由约定请参阅 ./references/route-structure.md。
app 目录。expo-audio 而非 expo-avexpo-video 而非 expo-avexpo-image 并设置 source="sf:name",而非 expo-symbols 或 @expo/vector-iconsreact-native-safe-area-context 而非 react-native 的 SafeAreaViewprocess.env.EXPO_OS 而非 Platform.OSReact.use 而非 React.useContextexpo-image 的 Image 组件,而非原生元素 imgexpo-glass-effect<ScrollView contentInsetAdjustmentBehavior="automatic" /> 替代 <SafeAreaView>,以获得更智能的安全区域边距contentInsetAdjustmentBehavior="automatic" 也应应用于 FlatList 和 SectionListuseWindowDimensions 而非 Dimensions.get() 来测量屏幕尺寸<Switch /> 和 @react-native-community/datetimepickercontentInsetAdjustmentBehavior="automatic" 的 ScrollViewScrollView 时,它几乎总应是路由组件内的第一个组件headerSearchBarOptions 来添加搜索栏<Text selectable /> 属性遵循 Apple 人机界面指南。
contentInsetAdjustmentBehavior="automatic" 实现{ borderCurve: 'continuous' } 处理圆角contentContainerStyle 的 padding 和 gap,而非直接设置 ScrollView 本身的 padding(减少裁剪)<Text/> 元素添加 selectable 属性{ fontVariant: 'tabular-nums' } 以实现对齐使用 CSS 的 boxShadow 样式属性。切勿使用旧版 React Native 的 shadow 或 elevation 样式。
<View style={{ boxShadow: "0 1px 2px rgba(0, 0, 0, 0.05)" }} />
支持 'inset' 阴影。
使用来自 'expo-router' 的 <Link href="/path" /> 在路由之间进行导航。
import { Link } from 'expo-router';
// 基础链接
<Link href="/path" />
// 包裹自定义组件
<Link href="/path" asChild>
<Pressable>...</Pressable>
</Link>
尽可能包含 <Link.Preview> 以遵循 iOS 惯例。经常添加上下文菜单和预览以增强导航体验。
_layout.tsx 文件来定义堆栈在 Stack.Screen 的 options 中设置页面标题:
<Stack.Screen options={{ title: "Home" }} />
为 Link 组件添加上下文菜单(长按触发):
import { Link } from "expo-router";
<Link href="/settings" asChild>
<Link.Trigger>
<Pressable>
<Card />
</Pressable>
</Link.Trigger>
<Link.Menu>
<Link.MenuAction
title="分享"
icon="square.and.arrow.up"
onPress={handleSharePress}
/>
<Link.MenuAction
title="屏蔽"
icon="nosign"
destructive
onPress={handleBlockPress}
/>
<Link.Menu title="更多" icon="ellipsis">
<Link.MenuAction title="复制" icon="doc.on.doc" onPress={() => {}} />
<Link.MenuAction
title="删除"
icon="trash"
destructive
onPress={() => {}}
/>
</Link.Menu>
</Link.Menu>
</Link>;
经常使用链接预览以增强导航体验:
<Link href="/settings">
<Link.Trigger>
<Pressable>
<Card />
</Pressable>
</Link.Trigger>
<Link.Preview />
</Link>
链接预览可以与上下文菜单一起使用。
以模态框形式呈现屏幕:
<Stack.Screen name="modal" options={{ presentation: "modal" }} />
优先使用此方法,而非构建自定义模态框组件。
以动态表单页形式呈现屏幕:
<Stack.Screen
name="sheet"
options={{
presentation: "formSheet",
sheetGrabberVisible: true,
sheetAllowedDetents: [0.5, 1.0],
contentStyle: { backgroundColor: "transparent" },
}}
/>
contentStyle: { backgroundColor: "transparent" } 可以在 iOS 26+ 上实现液态玻璃背景效果。一个包含标签页且每个标签页内都有堆栈的标准应用布局:
app/
_layout.tsx — <NativeTabs />
(index,search)/
_layout.tsx — <Stack />
index.tsx — 主列表
search.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>项目</Label>
</NativeTabs.Trigger>
<NativeTabs.Trigger name="(search)" role="search" />
</NativeTabs>
</Theme>
);
}
创建一个共享的分组路由,以便两个标签页都可以推送共同的屏幕:
// 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: "项目", 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>
);
}
每周安装量
21.7K
代码仓库
GitHub 星标
1.5K
首次出现
2026年1月19日
安全审计
安装于
opencode16.9K
codex16.3K
gemini-cli16.1K
github-copilot15.9K
cursor15.4K
amp12.9K
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+)
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:
npx expo start and scan the QR code with Expo GoYou need npx expo run:ios/android or eas build ONLY when using:
modules/)@bacons/apple-targets)app.jsonExpo Go supports a huge range of features out of the box:
expo-* packages (camera, location, notifications, etc.)If you're unsure, try Expo Go first. Creating custom builds adds complexity, slower iteration, and requires Xcode/Android Studio setup.
comment-card.tsxSee ./references/route-structure.md for detailed route conventions.
app directory.expo-audio not expo-avexpo-video not expo-avexpo-image with source="sf:name" for SF Symbols, not expo-symbols or @expo/vector-iconsreact-native-safe-area-context not react-native SafeAreaViewprocess.env.EXPO_OS not <ScrollView contentInsetAdjustmentBehavior="automatic" /> instead of <SafeAreaView> for smarter safe area insetscontentInsetAdjustmentBehavior="automatic" should be applied to FlatList and SectionList as welluseWindowDimensions over Dimensions.get() to measure screen size<Switch /> from React Native and @react-native-community/datetimepickercontentInsetAdjustmentBehavior="automatic" setScrollView to the page it should almost always be the first component inside the route componentheaderSearchBarOptions in Stack.Screen options to add a search bar<Text selectable /> prop on text containing data that could be copiedFollow Apple Human Interface Guidelines.
contentInsetAdjustmentBehavior="automatic"{ borderCurve: 'continuous' } for rounded corners unless creating a capsule shapecontentContainerStyle padding and gap instead of padding on the ScrollView itself (reduces clipping)selectable prop to every <Text/> element displaying important data or error messages{ fontVariant: 'tabular-nums' } for alignmentUse CSS boxShadow style prop. NEVER use legacy React Native shadow or elevation styles.
<View style={{ boxShadow: "0 1px 2px rgba(0, 0, 0, 0.05)" }} />
'inset' shadows are supported.
Use <Link href="/path" /> from 'expo-router' for navigation between routes.
import { Link } from 'expo-router';
// Basic link
<Link href="/path" />
// Wrapping custom components
<Link href="/path" asChild>
<Pressable>...</Pressable>
</Link>
Whenever possible, include a <Link.Preview> to follow iOS conventions. Add context menus and previews frequently to enhance navigation.
_layout.tsx files to define stacksSet the page title in Stack.Screen options:
<Stack.Screen options={{ title: "Home" }} />
Add long press context menus to Link components:
import { Link } from "expo-router";
<Link href="/settings" asChild>
<Link.Trigger>
<Pressable>
<Card />
</Pressable>
</Link.Trigger>
<Link.Menu>
<Link.MenuAction
title="Share"
icon="square.and.arrow.up"
onPress={handleSharePress}
/>
<Link.MenuAction
title="Block"
icon="nosign"
destructive
onPress={handleBlockPress}
/>
<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>;
Use link previews frequently to enhance navigation:
<Link href="/settings">
<Link.Trigger>
<Pressable>
<Card />
</Pressable>
</Link.Trigger>
<Link.Preview />
</Link>
Link preview can be used with context menus.
Present a screen as a modal:
<Stack.Screen name="modal" options={{ presentation: "modal" }} />
Prefer this to building a custom modal component.
Present a screen as a dynamic form sheet:
<Stack.Screen
name="sheet"
options={{
presentation: "formSheet",
sheetGrabberVisible: true,
sheetAllowedDetents: [0.5, 1.0],
contentStyle: { backgroundColor: "transparent" },
}}
/>
contentStyle: { backgroundColor: "transparent" } makes the background liquid glass on iOS 26+.A standard app layout with tabs and stacks inside each tab:
app/
_layout.tsx — <NativeTabs />
(index,search)/
_layout.tsx — <Stack />
index.tsx — Main list
search.tsx — Search view
// 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>
);
}
Create a shared group route so both tabs can push common screens:
// 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>
);
}
Weekly Installs
21.7K
Repository
GitHub Stars
1.5K
First Seen
Jan 19, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode16.9K
codex16.3K
gemini-cli16.1K
github-copilot15.9K
cursor15.4K
amp12.9K
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
102,200 周安装
Platform.OSReact.use not React.useContextexpo-image Image component instead of intrinsic element imgexpo-glass-effect for liquid glass backdrops