
Expo Liquid Glass
- 343 installs
- 10 repo stars
- Updated February 6, 2026
- devanshudesai/agent-skills
Helps with ai & agent building tasks.
About
expo-liquid-glass is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- expo-liquid-glass
- AI & Agent Building
- AI-coding skill
Expo Liquid Glass by the numbers
- 343 all-time installs (skills.sh)
- Ranked #2,157 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/devanshudesai/agent-skills --skill expo-liquid-glassAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 343 |
|---|---|
| repo stars | ★ 10 |
| Last updated | February 6, 2026 |
| Repository | devanshudesai/agent-skills ↗ |
What it does
Helps with ai & agent building tasks.
Files
Expo Liquid Glass
Ship Liquid Glass UI that feels native, stays legible, and degrades safely across iOS/Android.
Execution Order
1. Confirm platform/runtime constraints. 2. Check design alignment against HIG buckets (recommended for design-heavy tasks). 3. Pick one primary implementation path (add a second path only if needed). 4. Apply Apple-aligned visual rules before writing code. 5. Implement guarded glass components with explicit fallbacks. 6. Run accessibility and visual QA in both light/dark and clear/tinted appearances.
1) Preflight Constraints
- Use Liquid Glass only for controls/navigation chrome, not primary content surfaces.
- Treat these APIs as fast-moving: check current Expo SDK docs before finalizing syntax.
- Expect a development build for advanced iOS-native features:
expo-glass-effect and @expo/ui are not reliable in Expo Go on iOS.
- Keep scope on Liquid Glass in Expo: use HIG rules to guide implementation, not to redesign unrelated product behavior.
2) Design Alignment (Recommended for design-heavy tasks)
For tasks that involve significant visual design decisions, evaluate against these HIG buckets:
1. Foundations Check materials, color, layout, motion, and accessibility implications. 2. Patterns Check navigation/search/flow behavior for consistency with system expectations. 3. Components Check bars, buttons, menus, fields, sidebars, and overlays used by the screen. 4. Inputs Check touch, gesture, keyboard, and pointer behavior for parity and discoverability.
See references/apple-liquid-glass-design.md for practical design guidance. If a proposed style conflicts with HIG intent, prefer the HIG-consistent option.
3) Choose the Primary Path
| Path | Use It For | Tradeoffs |
|---|---|---|
expo-glass-effect | Most RN screens that need glass chips, floating buttons, toolbars, grouped controls | Best default in Expo; must guard runtime availability |
@expo/ui (Host + SwiftUI modifiers) | Native SwiftUI composition, advanced glass transitions, coordinated IDs/namespaces | iOS-family only, dev-build workflow, SwiftUI mental model |
expo-router/unstable-native-tabs | System-native Liquid Glass tab bars and iOS 26 nav behavior | Unstable API; syntax differs between SDK 54 and 55 |
@callstack/liquid-glass | Non-Expo RN or teams standardizing on Callstack package | iOS/tvOS focus; also requires fallbacks and runtime checks |
Combine paths when appropriate:
- Use native tabs for navigation chrome.
- Use
expo-glass-effectfor floating controls inside screens. - Use
@expo/uionly where SwiftUI-specific behavior is required.
4) Apple-Style Design Rules (Critical)
Apply these rules before implementing visuals:
1. Keep hierarchy in layout and spacing, not decorative layers. 2. Group related controls into shared glass clusters; separate unrelated groups with space. 3. Let content run edge-to-edge behind controls so glass has something to refract. 4. Use system controls/material first; customize minimally. 5. Move strong brand color into content/background, not navigation bars. 6. Keep icons/labels high contrast in light, dark, clear, and tinted modes. 7. Avoid full-screen glass sheets; reserve glass for top-level interaction surfaces.
5) Implementation Patterns
Pattern A: Guarded Adaptive Glass Wrapper
import { Platform, View } from 'react-native';
import { BlurView } from 'expo-blur';
import { GlassView, isGlassEffectAPIAvailable } from 'expo-glass-effect';
export function AdaptiveGlass({ style, children }) {
if (isGlassEffectAPIAvailable()) {
return (
<GlassView style={style} glassEffectStyle="regular" tintColor="#FFFFFF10">
{children}
</GlassView>
);
}
if (Platform.OS === 'ios') {
return (
<BlurView style={style} intensity={40} tint="dark">
{children}
</BlurView>
);
}
return <View style={[style, { backgroundColor: 'rgba(60,60,67,0.30)' }]}>{children}</View>;
}Pattern B: Safe expo-glass-effect Usage
- Prefer
glassEffectStyle:'regular' | 'clear' | 'identity'as needed. - Never set
opacity < 1onGlassViewor parents. - Treat
isInteractiveas mount-time only. Remount using akeyif it must change. - Avoid scrollable content inside
GlassView. - Check availability with
isGlassEffectAPIAvailable()before rendering.
Pattern C: Native Tabs (SDK-Specific Syntax)
SDK 55+ compound API:
<NativeTabs.Trigger name="index">
<NativeTabs.Trigger.TabBarIcon
ios={{ default: 'house', selected: 'house.fill' }}
androidIconName="home"
/>
<NativeTabs.Trigger.TabBarLabel>Home</NativeTabs.Trigger.TabBarLabel>
</NativeTabs.Trigger>SDK 54 API:
<NativeTabs.Trigger name="index">
<NativeTabs.Trigger.Icon sf="house.fill" md="home" />
<NativeTabs.Trigger.Label>Home</NativeTabs.Trigger.Label>
</NativeTabs.Trigger>Known issue: transparent NativeTabs can flash white while pushing screens in some stacks. Mitigate by setting a background color via ThemeProvider (see native-tabs reference).
Pattern D: SwiftUI Glass with Namespace IDs
Use @expo/ui when coordinated glass transitions are needed:
import { Host, Namespace, Text } from '@expo/ui/swift-ui';
import { glassEffect, glassEffectID, padding } from '@expo/ui/swift-ui/modifiers';
const ns = new Namespace('glass');
<Host style={{ width: 220, height: 56 }}>
<Text
modifiers={[
padding({ all: 16 }),
glassEffect({ glass: { variant: 'regular' } }),
glassEffectID({ id: 'primary-chip', in: ns }),
]}
>
Explore
</Text>
</Host>;6) Accessibility and Quality Gates
Treat this as required before completion:
- Check
AccessibilityInfo.isReduceTransparencyEnabled()and provide non-glass fallback. - Verify legibility over bright, dark, and high-saturation backgrounds.
- Validate both clear and tinted system appearances on iOS 26.
- Keep hit targets and spacing stable during interactive animations.
- Measure scroll performance with and without glass on low-end test devices.
7) Common Failure Modes and Fixes
- Double blur in headers:
Native header blur + custom glass child causes muddy layering. Use a plain translucent View in header accessories.
- Flat-looking glass:
Solid backgrounds remove refraction cues. Add tonal variation, gradients, or imagery behind the surface.
- Over-customized controls:
Heavy tint/border/shadow stacks reduce native feel. Start from system defaults, then tune lightly.
- Missing runtime guards:
Rendering glass APIs unguarded can crash or silently degrade on unsupported builds.
- Version drift:
Native-tabs and SwiftUI wrappers evolve quickly; check SDK-specific docs before coding.
8) Reference Loading Strategy
Load only what is needed for the task:
references/expo-ui-swiftui.md: SwiftUI component mapping, Host layout, modifier patterns.references/native-tabs.md: Native tab behaviors, migration notes, known issues.references/callstack-liquid-glass.md: Callstack setup and compatibility tradeoffs.references/apple-liquid-glass-design.md: Apple-aligned composition, hierarchy, motion, and accessibility rules.
If a request is design-heavy (not API-heavy), prioritize Apple visual rules in this file first, then pull API syntax from the relevant reference.
{
"version": "1.0.0",
"organization": "devanshuDesai",
"date": "February 2026",
"abstract": "Agent skill for building Liquid Glass interfaces in Expo React Native apps. Covers four implementation paths: expo-glass-effect (UIKit), @expo/ui SwiftUI integration, expo-router native tabs, and @callstack/liquid-glass. Includes runtime guards, platform fallbacks, SDK 54/55 migration, Apple HIG-aligned design rules, accessibility checks, and common failure mode fixes.",
"references": [
"https://docs.expo.dev/versions/latest/sdk/glass-effect/",
"https://docs.expo.dev/versions/latest/sdk/ui/",
"https://docs.expo.dev/router/advanced/native-tabs/",
"https://developer.apple.com/design/human-interface-guidelines/materials",
"https://developer.apple.com/videos/play/wwdc2025/280/",
"https://github.com/nicklockwood/LiquidGlass"
]
}
Apple Liquid Glass Design Notes
Design-focused guidance for making Liquid Glass interfaces feel native, intentional, and legible.
Use this file alongside the design rules in SKILL.md section 4.
Primary Sources
- WWDC session: Design with Liquid Glass
- Apple examples: New Design Gallery
Core Principles
1. Use glass for interaction chrome, not content. 2. Build hierarchy with spacing and grouping before adding visual treatment. 3. Keep content edge-to-edge so glass has meaningful background variation. 4. Start from system controls/materials and customize sparingly. 5. Put brand color into content layers, not persistent navigation bars. 6. Verify contrast in light, dark, clear, and tinted appearances. 7. Prefer one dominant glass layer per region; avoid stacked blur stacks.
Composition Patterns
Grouped Action Cluster
- Place related actions in one rounded glass group.
- Keep unrelated actions in separate clusters with clear spacing.
- Use one visual emphasis level inside each cluster.
Floating Search + Filter
- Keep search in system-native placement (tab role or header search).
- Use a compact glass filter pill row beneath, not another full-width bar.
- Collapse controls on scroll to prioritize content.
Media-Style Bottom Accessory
- Reserve tab-bar accessory for persistent context (now playing, active order, timer).
- Keep accessory concise and scannable; route detail interactions into sheet/detail screen.
Motion Rules
- Use native interactive glass behavior when possible instead of custom opacity animations.
- Keep transitions short and purposeful; avoid decorative continuous motion.
- Maintain stable layout during animation to preserve touch confidence.
Color and Contrast
- Test with high-saturation imagery and low-contrast photography behind glass.
- Ensure icon/text contrast remains readable when users switch between clear and tinted styles.
- Avoid pure black backgrounds behind glass where refraction becomes visually flat.
Accessibility Checklist
- Respect Reduce Transparency with non-glass fallback.
- Preserve touch target size and spacing.
- Avoid encoding meaning only via translucency or tint.
- Re-check readability with larger text and dynamic type scaling.
Common Anti-Patterns
- Full-screen glass overlays for content.
- Multiple competing glass layers in one region.
- Heavy custom shadows/borders that fight native material behavior.
- Glass applied without runtime guards/fallbacks on unsupported platforms.
@callstack/liquid-glass
Third-party Liquid Glass library from Callstack. Works in both vanilla React Native and Expo.
Setup
npm install @callstack/liquid-glassFor Expo projects, prefer:
npx expo install @callstack/liquid-glassCompatibility
- React Native
>= 0.80.0 - iOS/tvOS (Liquid Glass itself is iOS-family only)
- Liquid Glass effects require iOS/tvOS 26+
- In Expo workflows, prefer a development build for reliable testing
Components
LiquidGlassView
Drop-in glass panel component. Wrap any content to render it on glass.
import { LiquidGlassView, isLiquidGlassAvailable } from '@callstack/liquid-glass';
import { Text, View } from 'react-native';
function GlassCard() {
return (
<LiquidGlassView
style={[
{ width: 200, height: 100, borderRadius: 20 },
!isLiquidGlassAvailable() && { backgroundColor: 'rgba(255,255,255,0.5)' },
]}
interactive
effect="clear"
>
<Text style={{ fontWeight: '600', color: 'white' }}>Hello</Text>
</LiquidGlassView>
);
}Props:
interactive(boolean): Touch-response animations (scale, bounce, shimmer)effect:'clear'(high transparency) |'regular'(medium transparency)tintColor(ColorValue): Overlay tintcolorScheme:'light'|'dark'|'system'- Inherits all
ViewProps
LiquidGlassContainerView
Groups glass elements for morphing/merging when close together.
import { LiquidGlassContainerView, LiquidGlassView } from '@callstack/liquid-glass';
function MergingExample() {
return (
<LiquidGlassContainerView spacing={20}>
<LiquidGlassView style={{ width: 100, height: 100, borderRadius: 50 }}>
<Text>A</Text>
</LiquidGlassView>
<LiquidGlassView style={{ width: 100, height: 100, borderRadius: 50 }}>
<Text>B</Text>
</LiquidGlassView>
</LiquidGlassContainerView>
);
}Props:
spacing: Distance (points) at which glass elements begin merging
Platform Detection
import { isLiquidGlassAvailable } from '@callstack/liquid-glass';
if (isLiquidGlassAvailable()) {
// Use LiquidGlassView
} else {
// Fallback to BlurView or semi-transparent View
}Falls back to normal View rendering when Liquid Glass APIs are unavailable.
When to Choose This Over expo-glass-effect
- Non-Expo React Native projects
- Need
colorSchemeprop for explicit light/dark control - Prefer Callstack's API surface
- Already using other Callstack libraries
For Expo projects, expo-glass-effect is the official recommendation.
@expo/ui SwiftUI Integration
Full guide for using SwiftUI components with Liquid Glass in Expo apps.
Table of Contents
- Setup
- Host Component
- Available Components
- Glass Modifier
- Layout with HStack/VStack
- Modifiers System
- Patterns
Setup
npx expo install @expo/uiRequirements:
- SDK 54+
- Xcode 26+ (for glass modifiers)
- Development build required (not available in Expo Go)
- iOS, macOS, tvOS only (Android/web not yet supported)
Status: Beta. API surface can change between SDK releases.
Host Component
Host is the bridge from React Native (UIKit) to SwiftUI. It uses UIHostingController under the hood. Think of it like <svg> in the DOM or <Canvas> in react-native-skia.
import { Host, Button } from '@expo/ui/swift-ui';
function Example() {
return (
<Host style={{ width: 200, height: 50 }}>
<Button onPress={() => console.log('tap')}>Action</Button>
</Host>
);
}Host props:
style: Standard React Native styles (flex, dimensions, positioning)matchContents: Boolean - sizes Host to fit child SwiftUI content
Layout rule: Flexbox styles apply only to the Host container itself. Inside the Host, use SwiftUI layout primitives (HStack, VStack, Spacer) instead of Yoga/flexbox. Do NOT set layout props directly on wrapped SwiftUI views - this conflicts with SwiftUI and causes undefined behavior.
Available Components
Import from @expo/ui/swift-ui:
| Component | Key Props | Notes |
|---|---|---|
Button | variant ("default", "borderless"), onPress | |
Text | children (string) | SwiftUI Text, not RN Text |
HStack | children | Horizontal layout |
VStack | children | Vertical layout |
Spacer | Flexible space | |
Form | children | iOS settings-style form |
Section | children | Group within Form |
Image | SwiftUI Image | |
Toggle | checked, onValueChange, label, variant ("switch", "checkbox") | |
Picker | options, selectedIndex, variant ("segmented", "wheel", "menu") | |
Slider | value, onValueChange | |
TextField | defaultValue, onChangeText, autocorrection | |
DateTimePicker | displayedComponents, initialDate, variant, onDateSelected | |
ColorPicker | label, selection, onValueChanged | |
ContextMenu | Nested Items/Trigger | |
BottomSheet | isOpened, onIsOpenedChange | |
List | scrollEnabled, editModeEnabled, etc. | Reorderable, deletable |
CircularProgress | progress, color | |
LinearProgress | progress, color | |
Gauge | min, max, current, color, type |
Glass Modifier
Apply liquid glass to any SwiftUI component via the glassEffect modifier.
import { Host, Text, VStack } from '@expo/ui/swift-ui';
import { glassEffect, padding, frame } from '@expo/ui/swift-ui/modifiers';
function GlassCard() {
return (
<Host style={{ width: 300, height: 200 }}>
<VStack
modifiers={[
padding({ all: 20 }),
frame({ maxWidth: 280 }),
glassEffect({ glass: { variant: 'regular' } }),
]}
>
<Text>Glass Surface</Text>
</VStack>
</Host>
);
}Glass variants
// Regular - medium transparency, standard controls
glassEffect({ glass: { variant: 'regular' } })
// Clear - high transparency, media-rich backgrounds
glassEffect({ glass: { variant: 'clear' } })
// With tint color
glassEffect({ glass: { variant: 'regular', tint: '#007AFF' } })
// Interactive - enables touch animations (scale, bounce, shimmer)
glassEffect({ glass: { variant: 'regular', interactive: true } })Coordinated Glass Transitions
Use Namespace + glassEffectID to coordinate glass identity across transitions:
import { Host, Namespace, Text } from '@expo/ui/swift-ui';
import { glassEffect, glassEffectID } from '@expo/ui/swift-ui/modifiers';
const glassNamespace = new Namespace('main');
<Host style={{ width: 220, height: 56 }}>
<Text
modifiers={[
glassEffect({ glass: { variant: 'regular' } }),
glassEffectID({ id: 'search-chip', in: glassNamespace }),
]}
>
Search
</Text>
</Host>;Glass + Mesh Gradient (Liquid Glass Text)
Combine expo-mesh-gradient with glassEffect for liquid glass text:
import { Host, Text } from '@expo/ui/swift-ui';
import { glassEffect, padding } from '@expo/ui/swift-ui/modifiers';
import { MeshGradient } from 'expo-mesh-gradient';
function GlassTitle() {
return (
<View style={{ flex: 1 }}>
<MeshGradient
style={StyleSheet.absoluteFill}
points={[/* mesh gradient points */]}
colors={[/* gradient colors */]}
/>
<Host style={{ position: 'absolute', top: 100, alignSelf: 'center' }}>
<Text
modifiers={[
padding({ all: 16 }),
glassEffect({ glass: { variant: 'clear' } }),
]}
>
Liquid Glass Text
</Text>
</Host>
</View>
);
}Layout
SwiftUI layout inside Host uses stacks, not flexbox:
import { Host, HStack, VStack, Text, Spacer } from '@expo/ui/swift-ui';
import { padding, glassEffect } from '@expo/ui/swift-ui/modifiers';
function GlassToolbar() {
return (
<Host style={{ width: '100%', height: 60 }}>
<HStack modifiers={[padding({ horizontal: 16 }), glassEffect({ glass: { variant: 'regular' } })]}>
<Text>Left</Text>
<Spacer />
<Text>Right</Text>
</HStack>
</Host>
);
}Modifiers System
Import from @expo/ui/swift-ui/modifiers. Pass as arrays to the modifiers prop:
modifiers={[
padding({ all: 16 }),
frame({ maxWidth: 300, minHeight: 50 }),
glassEffect({ glass: { variant: 'regular' } }),
]}Modifiers are applied in order (like SwiftUI modifier chains).
Patterns
Mixing RN and SwiftUI
React Native components can be nested as JSX children within Host:
<Host style={{ flex: 1 }}>
<VStack>
<Text>SwiftUI Text</Text>
{/* RN View as child - use with care */}
</VStack>
</Host>Conditional Glass
import { isGlassEffectAPIAvailable } from 'expo-glass-effect';
function AdaptiveCard() {
const mods = [padding({ all: 16 })];
if (isGlassEffectAPIAvailable()) {
mods.push(glassEffect({ glass: { variant: 'regular' } }));
}
return (
<Host style={{ width: 200, height: 100 }}>
<VStack modifiers={mods}>
<Text>Content</Text>
</VStack>
</Host>
);
}Native Tabs with Liquid Glass
Expo Router v6 native tabs that automatically render with iOS 26 liquid glass.
Table of Contents
- Setup
- Basic Usage
- Tab Bar Items
- SDK 54 to 55 Migration
- Liquid Glass Colors
- iOS 26 Features
- Advanced Configuration
- Known Issues
- Limitations
Setup
Requires Expo Router v6 with unstable native tabs (alpha API). Syntax differs by SDK.
# current unstable channel
npx expo install expo-router@nextFile structure:
app/
_layout.tsx # NativeTabs layout
(tabs)/
index.tsx # Home tab
search.tsx # Search tab
account.tsx # Account tabBasic Usage
import { NativeTabs } from 'expo-router/unstable-native-tabs';
export default function TabLayout() {
return (
<NativeTabs>
<NativeTabs.Trigger name="index">
<NativeTabs.Trigger.TabBarIcon
ios={{ default: 'house', selected: 'house.fill' }}
androidIconName="home"
/>
<NativeTabs.Trigger.TabBarLabel>Home</NativeTabs.Trigger.TabBarLabel>
</NativeTabs.Trigger>
<NativeTabs.Trigger name="search">
<NativeTabs.Trigger.TabBarIcon
ios={{ default: 'magnifyingglass', selected: 'magnifyingglass' }}
androidIconName="search"
/>
<NativeTabs.Trigger.TabBarLabel>Search</NativeTabs.Trigger.TabBarLabel>
</NativeTabs.Trigger>
<NativeTabs.Trigger name="account">
<NativeTabs.Trigger.TabBarIcon
ios={{ default: 'person', selected: 'person.fill' }}
androidIconName="person"
/>
<NativeTabs.Trigger.TabBarLabel>Account</NativeTabs.Trigger.TabBarLabel>
</NativeTabs.Trigger>
</NativeTabs>
);
}On iOS 26, the tab bar automatically uses liquid glass material.
Tab Bar Items
Icon (SDK 55+ compound API)
// SF Symbols (iOS) + Material Symbols (Android)
<NativeTabs.Trigger.TabBarIcon
ios={{ default: 'house', selected: 'house.fill' }}
androidIconName="home"
/>
// Custom image
<NativeTabs.Trigger.TabBarIcon src={require('./assets/icon.png')} />
// State variants (different icons for selected/default)
<NativeTabs.Trigger.TabBarIcon
ios={{ default: 'house', selected: 'house.fill' }}
androidIconName="home"
/>
// Rendering mode (iOS)
<NativeTabs.Trigger.TabBarIcon
ios={{ default: 'star.fill', selected: 'star.fill' }}
renderingMode="template"
/>Label
<NativeTabs.Trigger.TabBarLabel>Home</NativeTabs.Trigger.TabBarLabel>
// Hidden label (tab still works, just no text)
<NativeTabs.Trigger.TabBarLabel hidden>Home</NativeTabs.Trigger.TabBarLabel>Badge
// Numeric badge
<NativeTabs.Trigger.TabBarBadge>3</NativeTabs.Trigger.TabBarBadge>
// Presence indicator (empty badge)
<NativeTabs.Trigger.TabBarBadge />Legacy aliases on SDK 54:
Trigger.IconTrigger.LabelTrigger.Badge
SDK 54 to 55 Migration
Update trigger subcomponents:
// SDK 54
<NativeTabs.Trigger.Icon sf="house.fill" md="home" />
<NativeTabs.Trigger.Label>Home</NativeTabs.Trigger.Label>
// SDK 55
<NativeTabs.Trigger.TabBarIcon
ios={{ default: 'house', selected: 'house.fill' }}
androidIconName="home"
/>
<NativeTabs.Trigger.TabBarLabel>Home</NativeTabs.Trigger.TabBarLabel>Icon still works as an alias in SDK 55 for compatibility, but prefer TabBarIcon.
Liquid Glass Colors
Liquid glass automatically adapts colors based on light/dark background. Use DynamicColorIOS for proper adaptation:
import { DynamicColorIOS } from 'react-native';
<NativeTabs
labelStyle={{
color: DynamicColorIOS({
dark: 'white',
light: 'black',
}),
}}
tintColor={DynamicColorIOS({
dark: 'white',
light: 'black',
})}
>iOS 26 Features
Separate Search Tab
<NativeTabs.Trigger name="search" role="search">
<NativeTabs.Trigger.TabBarLabel>Search</NativeTabs.Trigger.TabBarLabel>
<NativeTabs.Trigger.TabBarIcon
ios={{ default: 'magnifyingglass', selected: 'magnifyingglass' }}
androidIconName="search"
/>
</NativeTabs.Trigger>Search Bar in Tab Header
Wrap tab content in a Stack navigator with search bar options:
// app/(tabs)/search/_layout.tsx
import { Stack } from 'expo-router';
export default function SearchLayout() {
return (
<Stack>
<Stack.Screen
name="index"
options={{
headerSearchBarOptions: {
placeholder: 'Search...',
onChangeText: (e) => { /* handle search */ },
},
}}
/>
</Stack>
);
}Minimize Tab Bar on Scroll
<NativeTabs minimizeBehavior="onScrollDown">
{/* tabs */}
</NativeTabs>Bottom Accessory (Mini Player)
Floating view above the tab bar for persistent controls:
<NativeTabs
bottomAccessory={
<View style={{ height: 60, padding: 8 }}>
<Text>Now Playing: Song Title</Text>
</View>
}
>
{/* tabs */}
</NativeTabs>Advanced Configuration
Per-Tab Headers with Stack Navigators
Each tab can have its own Stack navigator for per-screen header configuration:
// app/(tabs)/home/_layout.tsx
import { Stack } from 'expo-router';
export default function HomeLayout() {
return (
<Stack
screenOptions={{
headerLargeTitle: true,
headerBlurEffect: 'systemChromeMaterialDark',
headerTransparent: true,
}}
>
<Stack.Screen name="index" options={{ title: 'Home' }} />
</Stack>
);
}Safe Area Handling (SDK 55+)
- Android: Screens auto-wrapped in
SafeAreaView - iOS: Content inset adjustment on first
ScrollView - Override:
disableAutomaticContentInsetsprop
Android-Specific
- Maximum 5 tabs (Material Design constraint)
disablePopToTop: Prevents stack reset on active tab tapdisableScrollToTop: Disables scroll-to-top behavior- Keyboard avoidance enabled by default
Web Fallback
Native tabs render a basic iPad-like tab bar on web. For custom web layouts:
// app/_layout.web.tsx
import { Tabs } from 'expo-router/ui';
export default function WebTabLayout() {
return <Tabs>{/* custom web tab UI */}</Tabs>;
}Known Issues
White flash while pushing screens
Transparent NativeTabs can flash white in some stack transitions. Mitigate with a ThemeProvider background:
import { DarkTheme, ThemeProvider } from '@react-navigation/native';
import { NativeTabs } from 'expo-router/unstable-native-tabs';
export default function Layout() {
return (
<ThemeProvider value={DarkTheme}>
<NativeTabs transparentBackground />
</ThemeProvider>
);
}Limitations
- 5-tab maximum on Android
- Cannot measure tab bar height programmatically
- No nested native tabs
- FlatList integration is limited
- Dynamically adding/removing tabs causes remounting and state loss
- Limited scroll-to-top support with FlatList