Awesome-omni-skill building-ui
Complete guide for building beautiful apps with Expo Router. Covers fundamentals, styling, components, navigation, animations, patterns, and native tabs.
install
source · Clone the upstream repo
git clone https://github.com/diegosouzapw/awesome-omni-skill
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/diegosouzapw/awesome-omni-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/tools/building-ui-majiayu000" ~/.claude/skills/diegosouzapw-awesome-omni-skill-building-ui && rm -rf "$T"
manifest:
skills/tools/building-ui-majiayu000/SKILL.mdsource content
Expo UI Guidelines
References
Consult these resources as needed:
- ./references/route-structure.md -- Route file conventions, dynamic routes, query parameters, groups, and folder organization
- ./references/tabs.md -- Native tab bar with NativeTabs, migration from JS tabs, iOS 26 features
- ./references/icons.md -- SF Symbols with expo-symbols, common icon names, animations, and weights
- ./references/controls.md -- Native iOS controls: Switch, Slider, SegmentedControl, DateTimePicker, Picker
- ./references/visual-effects.md -- Blur effects with expo-blur and liquid glass with expo-glass-effect
- ./references/animations.md -- Reanimated animations: entering, exiting, layout, scroll-driven, and gestures
- ./references/search.md -- Search bar integration with headers, useSearch hook, and filtering patterns
- ./references/gradients.md -- CSS gradients using experimental_backgroundImage (New Architecture only)
- ./references/media.md -- Media handling for Expo Router including camera, audio, video, and file saving
- ./references/storage.md -- Data storage patterns including SQLite, AsyncStorage, and SecureStore
- ./references/webgpu-three.md -- 3D graphics, games, and GPU-powered visualizations with WebGPU and Three.js
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
and scan the QR code with Expo Gonpx expo start - Check if features work: Test your app thoroughly in Expo Go
- Only create custom builds when required - see below
When Custom Builds Are Required
- Local Expo modules (custom native code in
directory)modules/ - Apple targets (widgets, app clips, extensions)
- Third-party native modules not included in Expo Go
- Push notifications with custom configuration
When Expo Go Works
- Standard Expo SDK modules
- Expo Router navigation
- React Native Reanimated animations
- Most UI components and styling
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
directory.app - 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
notexpo-audioexpo-av
notexpo-videoexpo-av
notexpo-symbols@expo/vector-icons
not react-native SafeAreaViewreact-native-safe-area-context
notprocess.env.EXPO_OSPlatform.OS
notReact.useReact.useContext
Image component instead of intrinsic elementexpo-imageimg
for liquid glass backdropsexpo-glass-effect
Responsiveness
- Always wrap root component in a scroll view for responsiveness
- Use
instead of<ScrollView contentInsetAdjustmentBehavior="automatic" />
for smarter safe area insets<SafeAreaView>
should be applied to FlatList and SectionList as wellcontentInsetAdjustmentBehavior="automatic"- Use flexbox instead of Dimensions API
- ALWAYS prefer
overuseWindowDimensions
to measure screen sizeDimensions.get()
Behavior
- Use expo-haptics conditionally on iOS to make more delightful experiences
- Use views with built-in haptics like
from React Native and<Switch />@react-native-community/datetimepicker - When a route belongs to a Stack, its first child should almost always be a ScrollView with
setcontentInsetAdjustmentBehavior="automatic" - Prefer
in Stack.Screen options to add a search barheaderSearchBarOptions - Use the
prop on text containing data that could be copied<Text selectable /> - 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
General Styling Rules
- Use StyleSheet.create() for all styles
- Prefer named colors from a theme/design system
- Group related styles together
- Use semantic naming for style objects
Text Styling
import { Text, StyleSheet } from 'react-native'; const styles = StyleSheet.create({ title: { fontSize: 24, fontWeight: '700', color: '#1a1a1a', }, body: { fontSize: 16, lineHeight: 24, color: '#4a4a4a', }, });
Shadows
iOS and Android handle shadows differently:
const styles = StyleSheet.create({ card: { // iOS shadows shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 8, // Android shadows elevation: 4, // Common backgroundColor: '#fff', borderRadius: 12, }, });
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
files to define stacks_layout.tsx - Use Stack from 'expo-router/stack' for native navigation stacks
Page Title
Set the page title using Stack.Screen options:
import { Stack } from 'expo-router'; export default function Layout() { return ( <Stack> <Stack.Screen name="index" options={{ title: 'Home' }} /> <Stack.Screen name="details" options={{ title: 'Details' }} /> </Stack> ); }
Context Menus
Add context menus for enhanced interactions:
import { ContextMenu } from 'zeego/context-menu'; <ContextMenu.Root> <ContextMenu.Trigger> <Pressable>{/* content */}</Pressable> </ContextMenu.Trigger> <ContextMenu.Content> <ContextMenu.Item key="share" onSelect={handleShare}> <ContextMenu.ItemTitle>Share</ContextMenu.ItemTitle> </ContextMenu.Item> </ContextMenu.Content> </ContextMenu.Root>
Modal
Present modals using the
presentation option:
// In _layout.tsx <Stack.Screen name="modal" options={{ presentation: 'modal', headerShown: false, }} />
Sheet
For bottom sheets, use:
<Stack.Screen name="sheet" options={{ presentation: 'formSheet', sheetGrabberVisible: true, sheetInitialDetentIndex: 0, sheetAllowedDetents: [0.5, 1], }} />
Common route structure
app/ ├── _layout.tsx # Root layout (Stack or Tabs) ├── index.tsx # Home screen "/" ├── (tabs)/ │ ├── _layout.tsx # Tab navigator │ ├── index.tsx # First tab │ └── settings.tsx # Settings tab ├── [id].tsx # Dynamic route "/123" └── modal.tsx # Modal screen