Building Native UI
OpenAIOfficialCreated by Expo TeamDistributed by OpenAI
- Skill
- Guidelines
- Software development
- MIT
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a structured set of guidelines and best practices for developing native mobile applications with Expo Router. It covers essential topics including project structure, navigation patterns, UI styling, native control integration, and animation techniques. The documentation emphasizes adherence to Apple Human Interface Guidelines and provides specific preferences for libraries and coding styles to ensure consistent development across Expo projects.
What it does
- Defines route conventions and project organization for Expo Router.
- Provides styling rules for native components, shadows, and text.
- Outlines navigation patterns including stacks, modals, and sheets.
- Details the use of native controls like switches, sliders, and date pickers.
- Offers guidance on implementing animations with Reanimated.
- Specifies library preferences and architectural patterns for Expo apps.
When to use it
- When starting a new Expo Router project and needing a standard structure.
- When implementing navigation stacks, modals, or native tabs.
- When styling components to align with Apple Human Interface Guidelines.
- When choosing between Expo Go and custom native builds.
- When integrating native UI controls or complex animations.
Advantages
- Promotes consistent code structure and file naming conventions.
- Encourages the use of native-first components and patterns.
- Provides clear guidance on when to use Expo Go versus custom builds.
- Reduces technical debt by recommending modern library alternatives.
- Ensures responsiveness through recommended layout practices.
Usage examples
- “How should I structure my navigation in an Expo Router app?”
- The skill provides a standard layout pattern using _layout.tsx files, NativeTabs, and shared group routes to organize stacks and tabs effectively.
- “What is the recommended way to handle shadows in my UI?”
- The guide instructs to use the CSS boxShadow style prop instead of legacy shadow or elevation styles, supporting both standard and inset shadows.
- “Should I use Expo Go or a custom build for my project?”
- The skill advises starting with Expo Go for most features and only creating custom builds when specific native modules, Apple targets, or custom native configurations are required.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/openai/building-native-ui/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip building-native-ui.zip -d .agents/skills
Documentation by Expo Team
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:
Start with Expo Go: Run
npx expo startand scan the QR code with Expo GoCheck if features work: Test your app thoroughly in Expo Go
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.tsxAlways 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
appdirectory.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-audionotexpo-avexpo-videonotexpo-avexpo-imagewithsource="sf:name"for SF Symbols, notexpo-symbolsor@expo/vector-iconsreact-native-safe-area-contextnot react-native SafeAreaViewprocess.env.EXPO_OSnotPlatform.OSReact.usenotReact.useContextexpo-imageImage component instead of intrinsic elementimgexpo-glass-effectfor liquid glass backdrops
Responsiveness
Always wrap root component in a scroll view for responsiveness
Use
<ScrollView contentInsetAdjustmentBehavior="automatic" />instead of<SafeAreaView>for smarter safe area insetscontentInsetAdjustmentBehavior="automatic"should be applied to FlatList and SectionList as wellUse flexbox instead of Dimensions API
ALWAYS prefer
useWindowDimensionsoverDimensions.get()to measure screen size
Behavior
Use expo-haptics conditionally on iOS to make more delightful experiences
Use views with built-in haptics like
<Switch />from React Native and@react-native-community/datetimepickerWhen a route belongs to a Stack, its first child should almost always be a ScrollView with
contentInsetAdjustmentBehavior="automatic"setWhen adding a
ScrollViewto the page it should almost always be the first component inside the route componentPrefer
headerSearchBarOptionsin Stack.Screen options to add a search barUse the
<Text selectable />prop on text containing data that could be copiedConsider 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 shapeALWAYS use a navigation stack title instead of a custom text element on the page
When padding a ScrollView, use
contentContainerStylepadding and gap instead of padding on the ScrollView itself (reduces clipping)CSS and Tailwind are not supported - use inline styles
Text Styling
Add the
selectableprop to every<Text/>element displaying important data or error messagesCounters should use
{ fontVariant: 'tabular-nums' }for alignment
Shadows
Use 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.
Navigation
Link
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.
Stack
ALWAYS use
_layout.tsxfiles to define stacksUse Stack from 'expo-router/stack' for native navigation stacks
Page Title
Set the page title in Stack.Screen options:
<Stack.Screen options={{ title: "Home" }} />Context Menus
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>;Link Previews
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.
Modal
Present a screen as a modal:
<Stack.Screen name="modal" options={{ presentation: "modal" }} />Prefer this to building a custom modal component.
Sheet
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" },
}}
/>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 — <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>
);
}Files
- ATTRIBUTION.mdAdded by AutoGineer826 B
- LICENSELicence file added by AutoGineer1.1 KiB
- SKILL.md10.2 KiB
- agents/openai.yaml407 B
- references/animations.md4.6 KiB
- references/controls.md4.9 KiB
- references/form-sheet.md6.2 KiB
- references/gradients.md2.7 KiB
- references/icons.md4.7 KiB
- references/media.md6.1 KiB
- references/route-structure.md5.0 KiB
- references/search.md5.1 KiB
- references/storage.md2.9 KiB
- references/tabs.md13.0 KiB
- references/toolbar-and-headers.md7.9 KiB
- references/visual-effects.md4.2 KiB
- references/webgpu-three.md14.1 KiB
- references/zoom-transitions.md4.7 KiB
references/tabs.md
Native Tabs
Always prefer NativeTabs from 'expo-router/unstable-native-tabs' for the best iOS experience.
SDK 54+. SDK 55 recommended.
SDK Compatibility
| Aspect | SDK 54 | SDK 55+ |
|---|---|---|
| Import | import { NativeTabs, Icon, Label, Badge, VectorIcon } | import { NativeTabs } only |
| Icon | <Icon sf="house.fill" /> | <NativeTabs.Trigger.Icon sf="house.fill" /> |
| Label | <Label>Home</Label> | <NativeTabs.Trigger.Label>Home</NativeTabs.Trigger.Label> |
| Badge | <Badge>9+</Badge> | <NativeTabs.Trigger.Badge>9+</NativeTabs.Trigger.Badge> |
| Android icons | drawable prop | md prop (Material Symbols) |
All examples below use SDK 55 syntax. For SDK 54, replace NativeTabs.Trigger.Icon/Label/Badge with standalone Icon, Label, Badge imports.
Basic Usage
import { NativeTabs } from "expo-router/unstable-native-tabs";
export default function TabLayout() {
return (
<NativeTabs minimizeBehavior="onScrollDown">
<NativeTabs.Trigger name="index">
<NativeTabs.Trigger.Icon sf="house.fill" md="home" />
<NativeTabs.Trigger.Label>Home</NativeTabs.Trigger.Label>
<NativeTabs.Trigger.Badge>9+</NativeTabs.Trigger.Badge>
</NativeTabs.Trigger>
<NativeTabs.Trigger name="settings">
<NativeTabs.Trigger.Icon sf="gear" md="settings" />
<NativeTabs.Trigger.Label>Settings</NativeTabs.Trigger.Label>
</NativeTabs.Trigger>
<NativeTabs.Trigger name="(search)" role="search">
<NativeTabs.Trigger.Label>Search</NativeTabs.Trigger.Label>
</NativeTabs.Trigger>
</NativeTabs>
);
}Rules
You must include a trigger for each tab
The
NativeTabs.Trigger'name' must match the route name, including parentheses (e.g.<NativeTabs.Trigger name="(search)">)Prefer search tab to be last in the list so it can combine with the search bar
Use the 'role' prop for common tab types
Tabs must be static — no dynamic addition/removal at runtime (remounts navigator, loses state)
Platform Features
Native Tabs use platform-specific tab bar implementations:
iOS 26+: Liquid glass effects with system-native appearance
Android: Material 3 bottom navigation
Better performance and native feel
Icon Component
// SF Symbol (iOS) + Material Symbol (Android)
<NativeTabs.Trigger.Icon sf="house.fill" md="home" />
// State variants
<NativeTabs.Trigger.Icon sf={{ default: "house", selected: "house.fill" }} md="home" />
// Custom image
<NativeTabs.Trigger.Icon src={require('./icon.png')} />
// Xcode asset catalog — iOS only (SDK 55+)
<NativeTabs.Trigger.Icon xcasset="home-icon" />
<NativeTabs.Trigger.Icon xcasset={{ default: "home-outline", selected: "home-filled" }} />
// Rendering mode — iOS only (SDK 55+)
<NativeTabs.Trigger.Icon src={require('./icon.png')} renderingMode="template" />
<NativeTabs.Trigger.Icon src={require('./gradient.png')} renderingMode="original" />renderingMode: "template" applies tint color (single-color icons), "original" preserves source colors (gradients). Android always uses original.
Label & Badge
// Label
<NativeTabs.Trigger.Label>Home</NativeTabs.Trigger.Label>
<NativeTabs.Trigger.Label hidden>Home</NativeTabs.Trigger.Label> {/* icon-only tab */}
// Badge
<NativeTabs.Trigger.Badge>9+</NativeTabs.Trigger.Badge>
<NativeTabs.Trigger.Badge /> {/* dot indicator */}iOS 26 Features
Liquid Glass Tab Bar
The tab bar automatically adopts liquid glass appearance on iOS 26+.
Minimize on Scroll
<NativeTabs minimizeBehavior="onScrollDown">Search Tab
<NativeTabs.Trigger name="(search)" role="search">
<NativeTabs.Trigger.Label>Search</NativeTabs.Trigger.Label>
</NativeTabs.Trigger>Note: Place search tab last for best UX.
Role Prop
Use semantic roles for special tab types:
<NativeTabs.Trigger name="search" role="search" />
<NativeTabs.Trigger name="favorites" role="favorites" />
<NativeTabs.Trigger name="more" role="more" />Available roles: search | more | favorites | bookmarks | contacts | downloads | featured | history | mostRecent | mostViewed | recents | topRated
Customization
Tint Color
<NativeTabs tintColor="#007AFF">Dynamic Colors (iOS)
Use DynamicColorIOS for colors that adapt to liquid glass:
import { DynamicColorIOS, Platform } from 'react-native';
const adaptiveBlue = Platform.select({
ios: DynamicColorIOS({ light: '#007AFF', dark: '#0A84FF' }),
default: '#007AFF',
});
<NativeTabs tintColor={adaptiveBlue}>Conditional Tabs
<NativeTabs.Trigger name="admin" hidden={!isAdmin}>
<NativeTabs.Trigger.Label>Admin</NativeTabs.Trigger.Label>
<NativeTabs.Trigger.Icon sf="shield.fill" md="shield" />
</NativeTabs.Trigger>Don't hide the tabs when they are visible - toggling visibility remounts the navigator; Do it only during the initial render.
Note: Hidden tabs cannot be navigated to!
Behavior Options
<NativeTabs.Trigger
name="home"
disablePopToTop // Don't pop stack when tapping active tab
disableScrollToTop // Don't scroll to top when tapping active tab
disableAutomaticContentInsets // Opt out of automatic safe area insets (SDK 55+)
>Hidden Tab Bar (SDK 55+)
Use hidden prop on NativeTabs to hide the entire tab bar dynamically:
<NativeTabs hidden={isTabBarHidden}>{/* triggers */}</NativeTabs>Bottom Accessory (SDK 55+)
NativeTabs.BottomAccessory renders content above the tab bar (iOS 26+). Uses usePlacement() to adapt between 'regular' and 'inline' layouts.
Important: Two instances render simultaneously — store state outside the component (props, context, or external store).
import { NativeTabs } from "expo-router/unstable-native-tabs";
import { useState } from "react";
import { Pressable, Text, View } from "react-native";
function MiniPlayer({
isPlaying,
onToggle,
}: {
isPlaying: boolean;
onToggle: () => void;
}) {
const placement = NativeTabs.BottomAccessory.usePlacement();
if (placement === "inline") {
return (
<Pressable onPress={onToggle}>
<SymbolView name={isPlaying ? "pause.fill" : "play.fill"} />
</Pressable>
);
}
return <View>{/* full player UI */}</View>;
}
export default function TabLayout() {
const [isPlaying, setIsPlaying] = useState(false);
return (
<NativeTabs>
<NativeTabs.BottomAccessory>
<MiniPlayer
isPlaying={isPlaying}
onToggle={() => setIsPlaying(!isPlaying)}
/>
</NativeTabs.BottomAccessory>
<NativeTabs.Trigger name="index">
<NativeTabs.Trigger.Icon sf="house.fill" md="home" />
<NativeTabs.Trigger.Label>Home</NativeTabs.Trigger.Label>
</NativeTabs.Trigger>
</NativeTabs>
);
}Safe Area Handling (SDK 55+)
SDK 55 handles safe areas automatically:
Android: Content wrapped in SafeAreaView (bottom inset)
iOS: First ScrollView gets automatic
contentInsetAdjustmentBehavior
To opt out per-tab, use disableAutomaticContentInsets and manage manually:
<NativeTabs.Trigger name="index" disableAutomaticContentInsets>
<NativeTabs.Trigger.Label>Home</NativeTabs.Trigger.Label>
</NativeTabs.Trigger>// In the screen
import { SafeAreaView } from "react-native-screens/experimental";
export default function HomeScreen() {
return (
<SafeAreaView edges={{ bottom: true }} style={{ flex: 1 }}>
{/* content */}
</SafeAreaView>
);
}Using Vector Icons
If you must use @expo/vector-icons instead of SF Symbols:
import { NativeTabs } from "expo-router/unstable-native-tabs";
import Ionicons from "@expo/vector-icons/Ionicons";
<NativeTabs.Trigger name="home">
<NativeTabs.Trigger.VectorIcon vector={Ionicons} name="home" />
<NativeTabs.Trigger.Label>Home</NativeTabs.Trigger.Label>
</NativeTabs.Trigger>Prefer SF Symbols + md prop over vector icons for native feel.
If you are using SDK 55 and later use the md prop to specify Material Symbols used on Android.
Structure with Stacks
Native tabs don't render headers. Nest Stacks inside each tab for navigation headers:
// app/(tabs)/_layout.tsx
import { NativeTabs } from "expo-router/unstable-native-tabs";
export default function TabLayout() {
return (
<NativeTabs>
<NativeTabs.Trigger name="(home)">
<NativeTabs.Trigger.Label>Home</NativeTabs.Trigger.Label>
<NativeTabs.Trigger.Icon sf="house.fill" md="home" />
</NativeTabs.Trigger>
</NativeTabs>
);
}
// app/(tabs)/(home)/_layout.tsx
import Stack from "expo-router/stack";
export default function HomeStack() {
return (
<Stack>
<Stack.Screen
name="index"
options={{ title: "Home", headerLargeTitle: true }}
/>
<Stack.Screen name="details" options={{ title: "Details" }} />
</Stack>
);
}Custom Web Layout
Use platform-specific files for separate native and web tab layouts:
app/
_layout.tsx # NativeTabs for iOS/Android
_layout.web.tsx # Headless tabs for web (expo-router/ui)Or extract to a component: components/app-tabs.tsx + components/app-tabs.web.tsx.
Migration from JS Tabs
Before (JS Tabs)
import { Tabs } from "expo-router";
<Tabs>
<Tabs.Screen
name="index"
options={{
title: "Home",
tabBarIcon: ({ color }) => <IconSymbol name="house.fill" color={color} />,
tabBarBadge: 3,
}}
/>
</Tabs>;After (Native Tabs)
import { NativeTabs } from "expo-router/unstable-native-tabs";
<NativeTabs>
<NativeTabs.Trigger name="index">
<NativeTabs.Trigger.Label>Home</NativeTabs.Trigger.Label>
<NativeTabs.Trigger.Icon sf="house.fill" md="home" />
<NativeTabs.Trigger.Badge>3</NativeTabs.Trigger.Badge>
</NativeTabs.Trigger>
</NativeTabs>;Key Differences
| JS Tabs | Native Tabs |
|---|---|
<Tabs.Screen> | <NativeTabs.Trigger> |
options={{ title }} | <NativeTabs.Trigger.Label> |
options={{ tabBarIcon }} | <NativeTabs.Trigger.Icon> |
tabBarBadge option | <NativeTabs.Trigger.Badge> |
| Props-based API | Component-based API |
| Headers built-in | Nest <Stack> for headers |
Limitations
Android: Maximum 5 tabs (Material Design constraint)
Nesting: Native tabs cannot nest inside other native tabs
Tab bar height: Cannot be measured programmatically
FlatList transparency: Use
disableTransparentOnScrollEdgeto fix issuesDynamic tabs: Tabs must be static; changes remount navigator and lose state
Keyboard Handling (Android)
Configure in app.json:
{
"expo": {
"android": {
"softwareKeyboardLayoutMode": "resize"
}
}
}Common Issues
Icons not showing on Android: Add
mdprop (SDK 55) or use VectorIconHeaders missing: Nest a Stack inside each tab group
Trigger name mismatch:
namemust match exact route name including parenthesesBadge not visible: Badge must be a child of Trigger, not a prop
Tab bar transparent on iOS 18 and earlier: If the screen uses a
ScrollVieworFlatList, make sure it is the first opaque child of the screen component. If it needs to be wrapped in anotherView, ensure the wrapper usescollapsable={false}. If the screen does not use aScrollVieworFlatList, setdisableTransparentOnScrollEdgetotruein theNativeTabs.Triggeroptions, to make the tab bar opaque.Scroll to top not working: Ensure
disableScrollToTopis not set on the active tab's Trigger andScrollViewis the first child of the screen component.Header buttons flicker when navigating between tabs: Make sure the app is wrapped in a
ThemeProvider
import {
ThemeProvider,
DarkTheme,
DefaultTheme,
} from "@react-navigation/native";
import { useColorScheme } from "react-native";
import { Stack } from "expo-router";
export default function Layout() {
const colorScheme = useColorScheme();
return (
<ThemeProvider theme={colorScheme === "dark" ? DarkTheme : DefaultTheme}>
<Stack />
</ThemeProvider>
);
}If the app only uses a light or dark theme, you can directly pass DarkTheme or DefaultTheme to ThemeProvider without checking the color scheme.
import { ThemeProvider, DarkTheme } from "@react-navigation/native";
import { Stack } from "expo-router";
export default function Layout() {
return (
<ThemeProvider theme={DarkTheme}>
<Stack />
</ThemeProvider>
);
}Requirements and compatibility
Written for: OpenAI Codex
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs Node.js
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Created by
- Expo Team
- Publisher
- OpenAI (Official)
- Distributed by
- OpenAI
- Source
- openai/plugins · plugins/expo/skills/building-native-ui · commit d420d13
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from plugins/expo/LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Expo Team or OpenAI.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download building-native-ui.zip