@codyswann/lisa 2.111.0 → 2.112.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/package.json +1 -1
  2. package/plugins/lisa/.claude-plugin/plugin.json +1 -1
  3. package/plugins/lisa/.codex-plugin/plugin.json +1 -1
  4. package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
  5. package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
  6. package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
  7. package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
  8. package/plugins/lisa-expo/.mcp.json +3 -3
  9. package/plugins/lisa-expo/THIRD-PARTY-NOTICES.md +57 -0
  10. package/plugins/lisa-expo/skills/add-app-clip/SKILL.md +280 -0
  11. package/plugins/lisa-expo/skills/add-app-clip/agents/openai.yaml +4 -0
  12. package/plugins/lisa-expo/skills/add-app-clip/references/native-module.md +96 -0
  13. package/plugins/lisa-expo/skills/building-native-ui/SKILL.md +321 -0
  14. package/plugins/lisa-expo/skills/building-native-ui/agents/openai.yaml +4 -0
  15. package/plugins/lisa-expo/skills/building-native-ui/references/animations.md +220 -0
  16. package/plugins/lisa-expo/skills/building-native-ui/references/controls.md +272 -0
  17. package/plugins/lisa-expo/skills/building-native-ui/references/form-sheet.md +253 -0
  18. package/plugins/lisa-expo/skills/building-native-ui/references/gradients.md +106 -0
  19. package/plugins/lisa-expo/skills/building-native-ui/references/icons.md +213 -0
  20. package/plugins/lisa-expo/skills/building-native-ui/references/media.md +198 -0
  21. package/plugins/lisa-expo/skills/building-native-ui/references/route-structure.md +229 -0
  22. package/plugins/lisa-expo/skills/building-native-ui/references/search.md +248 -0
  23. package/plugins/lisa-expo/skills/building-native-ui/references/storage.md +121 -0
  24. package/plugins/lisa-expo/skills/building-native-ui/references/tabs.md +433 -0
  25. package/plugins/lisa-expo/skills/building-native-ui/references/toolbar-and-headers.md +284 -0
  26. package/plugins/lisa-expo/skills/building-native-ui/references/visual-effects.md +197 -0
  27. package/plugins/lisa-expo/skills/building-native-ui/references/webgpu-three.md +605 -0
  28. package/plugins/lisa-expo/skills/building-native-ui/references/zoom-transitions.md +158 -0
  29. package/plugins/lisa-expo/skills/eas-update-insights/SKILL.md +228 -0
  30. package/plugins/lisa-expo/skills/eas-update-insights/agents/openai.yaml +4 -0
  31. package/plugins/lisa-expo/skills/eas-update-insights/references/channel-insights-schema.md +47 -0
  32. package/plugins/lisa-expo/skills/eas-update-insights/references/update-insights-schema.md +69 -0
  33. package/plugins/lisa-expo/skills/expo-api-routes/SKILL.md +369 -0
  34. package/plugins/lisa-expo/skills/expo-api-routes/agents/openai.yaml +4 -0
  35. package/plugins/lisa-expo/skills/expo-brownfield/SKILL.md +54 -0
  36. package/plugins/lisa-expo/skills/expo-brownfield/agents/openai.yaml +4 -0
  37. package/plugins/lisa-expo/skills/expo-brownfield/references/brownfield-integrated.md +526 -0
  38. package/plugins/lisa-expo/skills/expo-brownfield/references/brownfield-isolated.md +402 -0
  39. package/plugins/lisa-expo/skills/expo-brownfield/references/comparison.md +63 -0
  40. package/plugins/lisa-expo/skills/expo-brownfield/references/troubleshooting.md +88 -0
  41. package/plugins/lisa-expo/skills/expo-cicd-workflows/SKILL.md +92 -0
  42. package/plugins/lisa-expo/skills/expo-cicd-workflows/agents/openai.yaml +4 -0
  43. package/plugins/lisa-expo/skills/expo-cicd-workflows/scripts/fetch.js +113 -0
  44. package/plugins/lisa-expo/skills/expo-cicd-workflows/scripts/package.json +11 -0
  45. package/plugins/lisa-expo/skills/expo-cicd-workflows/scripts/validate.js +85 -0
  46. package/plugins/lisa-expo/skills/expo-deployment/SKILL.md +190 -0
  47. package/plugins/lisa-expo/skills/expo-deployment/agents/openai.yaml +4 -0
  48. package/plugins/lisa-expo/skills/expo-deployment/references/app-store-metadata.md +479 -0
  49. package/plugins/lisa-expo/skills/expo-deployment/references/ios-app-store.md +355 -0
  50. package/plugins/lisa-expo/skills/expo-deployment/references/play-store.md +246 -0
  51. package/plugins/lisa-expo/skills/expo-deployment/references/testflight.md +58 -0
  52. package/plugins/lisa-expo/skills/expo-deployment/references/workflows.md +200 -0
  53. package/plugins/lisa-expo/skills/expo-dev-client/SKILL.md +164 -0
  54. package/plugins/lisa-expo/skills/expo-dev-client/agents/openai.yaml +4 -0
  55. package/plugins/lisa-expo/skills/expo-module/SKILL.md +141 -0
  56. package/plugins/lisa-expo/skills/expo-module/agents/openai.yaml +4 -0
  57. package/plugins/lisa-expo/skills/expo-module/references/config-plugin.md +90 -0
  58. package/plugins/lisa-expo/skills/expo-module/references/create-expo-module.md +206 -0
  59. package/plugins/lisa-expo/skills/expo-module/references/lifecycle.md +127 -0
  60. package/plugins/lisa-expo/skills/expo-module/references/module-config.md +48 -0
  61. package/plugins/lisa-expo/skills/expo-module/references/native-module.md +286 -0
  62. package/plugins/lisa-expo/skills/expo-module/references/native-view.md +171 -0
  63. package/plugins/lisa-expo/skills/expo-tailwind-setup/SKILL.md +480 -0
  64. package/plugins/lisa-expo/skills/expo-tailwind-setup/agents/openai.yaml +4 -0
  65. package/plugins/lisa-expo/skills/expo-ui-jetpack-compose/SKILL.md +40 -0
  66. package/plugins/lisa-expo/skills/expo-ui-jetpack-compose/agents/openai.yaml +4 -0
  67. package/plugins/lisa-expo/skills/expo-ui-swift-ui/SKILL.md +39 -0
  68. package/plugins/lisa-expo/skills/expo-ui-swift-ui/agents/openai.yaml +4 -0
  69. package/plugins/lisa-expo/skills/native-data-fetching/SKILL.md +507 -0
  70. package/plugins/lisa-expo/skills/native-data-fetching/agents/openai.yaml +4 -0
  71. package/plugins/lisa-expo/skills/native-data-fetching/references/expo-router-loaders.md +344 -0
  72. package/plugins/lisa-expo/skills/upgrading-expo/SKILL.md +134 -0
  73. package/plugins/lisa-expo/skills/upgrading-expo/agents/openai.yaml +4 -0
  74. package/plugins/lisa-expo/skills/upgrading-expo/references/expo-av-to-audio.md +132 -0
  75. package/plugins/lisa-expo/skills/upgrading-expo/references/expo-av-to-video.md +160 -0
  76. package/plugins/lisa-expo/skills/upgrading-expo/references/native-tabs.md +124 -0
  77. package/plugins/lisa-expo/skills/upgrading-expo/references/new-architecture.md +79 -0
  78. package/plugins/lisa-expo/skills/upgrading-expo/references/react-19.md +79 -0
  79. package/plugins/lisa-expo/skills/upgrading-expo/references/react-compiler.md +59 -0
  80. package/plugins/lisa-expo/skills/upgrading-expo/references/react-navigation-to-expo-router.md +61 -0
  81. package/plugins/lisa-expo/skills/use-dom/SKILL.md +417 -0
  82. package/plugins/lisa-expo/skills/use-dom/agents/openai.yaml +4 -0
  83. package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
  84. package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
  85. package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
  86. package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
  87. package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
  88. package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
  89. package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
  90. package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
  91. package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
  92. package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
  93. package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
  94. package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
  95. package/plugins/src/expo/.mcp.json +3 -3
  96. package/plugins/src/expo/THIRD-PARTY-NOTICES.md +57 -0
  97. package/plugins/src/expo/skills/add-app-clip/SKILL.md +280 -0
  98. package/plugins/src/expo/skills/add-app-clip/references/native-module.md +96 -0
  99. package/plugins/src/expo/skills/building-native-ui/SKILL.md +321 -0
  100. package/plugins/src/expo/skills/building-native-ui/references/animations.md +220 -0
  101. package/plugins/src/expo/skills/building-native-ui/references/controls.md +272 -0
  102. package/plugins/src/expo/skills/building-native-ui/references/form-sheet.md +253 -0
  103. package/plugins/src/expo/skills/building-native-ui/references/gradients.md +106 -0
  104. package/plugins/src/expo/skills/building-native-ui/references/icons.md +213 -0
  105. package/plugins/src/expo/skills/building-native-ui/references/media.md +198 -0
  106. package/plugins/src/expo/skills/building-native-ui/references/route-structure.md +229 -0
  107. package/plugins/src/expo/skills/building-native-ui/references/search.md +248 -0
  108. package/plugins/src/expo/skills/building-native-ui/references/storage.md +121 -0
  109. package/plugins/src/expo/skills/building-native-ui/references/tabs.md +433 -0
  110. package/plugins/src/expo/skills/building-native-ui/references/toolbar-and-headers.md +284 -0
  111. package/plugins/src/expo/skills/building-native-ui/references/visual-effects.md +197 -0
  112. package/plugins/src/expo/skills/building-native-ui/references/webgpu-three.md +605 -0
  113. package/plugins/src/expo/skills/building-native-ui/references/zoom-transitions.md +158 -0
  114. package/plugins/src/expo/skills/eas-update-insights/SKILL.md +228 -0
  115. package/plugins/src/expo/skills/eas-update-insights/references/channel-insights-schema.md +47 -0
  116. package/plugins/src/expo/skills/eas-update-insights/references/update-insights-schema.md +69 -0
  117. package/plugins/src/expo/skills/expo-api-routes/SKILL.md +369 -0
  118. package/plugins/src/expo/skills/expo-brownfield/SKILL.md +54 -0
  119. package/plugins/src/expo/skills/expo-brownfield/references/brownfield-integrated.md +526 -0
  120. package/plugins/src/expo/skills/expo-brownfield/references/brownfield-isolated.md +402 -0
  121. package/plugins/src/expo/skills/expo-brownfield/references/comparison.md +63 -0
  122. package/plugins/src/expo/skills/expo-brownfield/references/troubleshooting.md +88 -0
  123. package/plugins/src/expo/skills/expo-cicd-workflows/SKILL.md +92 -0
  124. package/plugins/src/expo/skills/expo-cicd-workflows/scripts/fetch.js +113 -0
  125. package/plugins/src/expo/skills/expo-cicd-workflows/scripts/package.json +11 -0
  126. package/plugins/src/expo/skills/expo-cicd-workflows/scripts/validate.js +85 -0
  127. package/plugins/src/expo/skills/expo-deployment/SKILL.md +190 -0
  128. package/plugins/src/expo/skills/expo-deployment/references/app-store-metadata.md +479 -0
  129. package/plugins/src/expo/skills/expo-deployment/references/ios-app-store.md +355 -0
  130. package/plugins/src/expo/skills/expo-deployment/references/play-store.md +246 -0
  131. package/plugins/src/expo/skills/expo-deployment/references/testflight.md +58 -0
  132. package/plugins/src/expo/skills/expo-deployment/references/workflows.md +200 -0
  133. package/plugins/src/expo/skills/expo-dev-client/SKILL.md +164 -0
  134. package/plugins/src/expo/skills/expo-module/SKILL.md +141 -0
  135. package/plugins/src/expo/skills/expo-module/references/config-plugin.md +90 -0
  136. package/plugins/src/expo/skills/expo-module/references/create-expo-module.md +206 -0
  137. package/plugins/src/expo/skills/expo-module/references/lifecycle.md +127 -0
  138. package/plugins/src/expo/skills/expo-module/references/module-config.md +48 -0
  139. package/plugins/src/expo/skills/expo-module/references/native-module.md +286 -0
  140. package/plugins/src/expo/skills/expo-module/references/native-view.md +171 -0
  141. package/plugins/src/expo/skills/expo-tailwind-setup/SKILL.md +480 -0
  142. package/plugins/src/expo/skills/expo-ui-jetpack-compose/SKILL.md +40 -0
  143. package/plugins/src/expo/skills/expo-ui-swift-ui/SKILL.md +39 -0
  144. package/plugins/src/expo/skills/native-data-fetching/SKILL.md +507 -0
  145. package/plugins/src/expo/skills/native-data-fetching/references/expo-router-loaders.md +344 -0
  146. package/plugins/src/expo/skills/upgrading-expo/SKILL.md +134 -0
  147. package/plugins/src/expo/skills/upgrading-expo/references/expo-av-to-audio.md +132 -0
  148. package/plugins/src/expo/skills/upgrading-expo/references/expo-av-to-video.md +160 -0
  149. package/plugins/src/expo/skills/upgrading-expo/references/native-tabs.md +124 -0
  150. package/plugins/src/expo/skills/upgrading-expo/references/new-architecture.md +79 -0
  151. package/plugins/src/expo/skills/upgrading-expo/references/react-19.md +79 -0
  152. package/plugins/src/expo/skills/upgrading-expo/references/react-compiler.md +59 -0
  153. package/plugins/src/expo/skills/upgrading-expo/references/react-navigation-to-expo-router.md +61 -0
  154. package/plugins/src/expo/skills/use-dom/SKILL.md +417 -0
  155. package/scripts/generate-codex-plugin-artifacts.mjs +7 -2
@@ -0,0 +1,121 @@
1
+ # Storage
2
+
3
+ ## Key-Value Storage
4
+
5
+ Use the localStorage polyfill for key-value storage. **Never use AsyncStorage**
6
+
7
+ ```tsx
8
+ import "expo-sqlite/localStorage/install";
9
+
10
+ // Simple get/set
11
+ localStorage.setItem("key", "value");
12
+ localStorage.getItem("key");
13
+
14
+ // Store objects as JSON
15
+ localStorage.setItem("user", JSON.stringify({ name: "John", id: 1 }));
16
+ const user = JSON.parse(localStorage.getItem("user") ?? "{}");
17
+ ```
18
+
19
+ ## When to Use What
20
+
21
+ | Use Case | Solution |
22
+ | ---------------------------------------------------- | ----------------------- |
23
+ | Simple key-value (settings, preferences, small data) | `localStorage` polyfill |
24
+ | Large datasets, complex queries, relational data | Full `expo-sqlite` |
25
+ | Sensitive data (tokens, passwords) | `expo-secure-store` |
26
+
27
+ ## Storage with React State
28
+
29
+ Create a storage utility with subscriptions for reactive updates:
30
+
31
+ ```tsx
32
+ // utils/storage.ts
33
+ import "expo-sqlite/localStorage/install";
34
+
35
+ type Listener = () => void;
36
+ const listeners = new Map<string, Set<Listener>>();
37
+
38
+ export const storage = {
39
+ get<T>(key: string, defaultValue: T): T {
40
+ const value = localStorage.getItem(key);
41
+ return value ? JSON.parse(value) : defaultValue;
42
+ },
43
+
44
+ set<T>(key: string, value: T): void {
45
+ localStorage.setItem(key, JSON.stringify(value));
46
+ listeners.get(key)?.forEach((fn) => fn());
47
+ },
48
+
49
+ subscribe(key: string, listener: Listener): () => void {
50
+ if (!listeners.has(key)) listeners.set(key, new Set());
51
+ listeners.get(key)!.add(listener);
52
+ return () => listeners.get(key)?.delete(listener);
53
+ },
54
+ };
55
+ ```
56
+
57
+ ## React Hook for Storage
58
+
59
+ ```tsx
60
+ // hooks/use-storage.ts
61
+ import { useSyncExternalStore } from "react";
62
+ import { storage } from "@/utils/storage";
63
+
64
+ export function useStorage<T>(
65
+ key: string,
66
+ defaultValue: T
67
+ ): [T, (value: T) => void] {
68
+ const value = useSyncExternalStore(
69
+ (cb) => storage.subscribe(key, cb),
70
+ () => storage.get(key, defaultValue)
71
+ );
72
+
73
+ return [value, (newValue: T) => storage.set(key, newValue)];
74
+ }
75
+ ```
76
+
77
+ Usage:
78
+
79
+ ```tsx
80
+ function Settings() {
81
+ const [theme, setTheme] = useStorage("theme", "light");
82
+
83
+ return (
84
+ <Switch
85
+ value={theme === "dark"}
86
+ onValueChange={(dark) => setTheme(dark ? "dark" : "light")}
87
+ />
88
+ );
89
+ }
90
+ ```
91
+
92
+ ## Full SQLite for Complex Data
93
+
94
+ For larger datasets or complex queries, use expo-sqlite directly:
95
+
96
+ ```tsx
97
+ import * as SQLite from "expo-sqlite";
98
+
99
+ const db = await SQLite.openDatabaseAsync("app.db");
100
+
101
+ // Create table
102
+ await db.execAsync(`
103
+ CREATE TABLE IF NOT EXISTS events (
104
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
105
+ title TEXT NOT NULL,
106
+ date TEXT NOT NULL,
107
+ location TEXT
108
+ )
109
+ `);
110
+
111
+ // Insert
112
+ await db.runAsync("INSERT INTO events (title, date) VALUES (?, ?)", [
113
+ "Meeting",
114
+ "2024-01-15",
115
+ ]);
116
+
117
+ // Query
118
+ const events = await db.getAllAsync("SELECT * FROM events WHERE date > ?", [
119
+ "2024-01-01",
120
+ ]);
121
+ ```
@@ -0,0 +1,433 @@
1
+ # Native Tabs
2
+
3
+ Always prefer NativeTabs from 'expo-router/unstable-native-tabs' for the best iOS experience.
4
+
5
+ **SDK 54+. SDK 55 recommended.**
6
+
7
+ ## SDK Compatibility
8
+
9
+ | Aspect | SDK 54 | SDK 55+ |
10
+ | ------------- | ------------------------------------------------------- | ----------------------------------------------------------- |
11
+ | Import | `import { NativeTabs, Icon, Label, Badge, VectorIcon }` | `import { NativeTabs }` only |
12
+ | Icon | `<Icon sf="house.fill" />` | `<NativeTabs.Trigger.Icon sf="house.fill" />` |
13
+ | Label | `<Label>Home</Label>` | `<NativeTabs.Trigger.Label>Home</NativeTabs.Trigger.Label>` |
14
+ | Badge | `<Badge>9+</Badge>` | `<NativeTabs.Trigger.Badge>9+</NativeTabs.Trigger.Badge>` |
15
+ | Android icons | `drawable` prop | `md` prop (Material Symbols) |
16
+
17
+ All examples below use SDK 55 syntax. For SDK 54, replace `NativeTabs.Trigger.Icon/Label/Badge` with standalone `Icon`, `Label`, `Badge` imports.
18
+
19
+ ## Basic Usage
20
+
21
+ ```tsx
22
+ import { NativeTabs } from "expo-router/unstable-native-tabs";
23
+
24
+ export default function TabLayout() {
25
+ return (
26
+ <NativeTabs minimizeBehavior="onScrollDown">
27
+ <NativeTabs.Trigger name="index">
28
+ <NativeTabs.Trigger.Icon sf="house.fill" md="home" />
29
+ <NativeTabs.Trigger.Label>Home</NativeTabs.Trigger.Label>
30
+ <NativeTabs.Trigger.Badge>9+</NativeTabs.Trigger.Badge>
31
+ </NativeTabs.Trigger>
32
+ <NativeTabs.Trigger name="settings">
33
+ <NativeTabs.Trigger.Icon sf="gear" md="settings" />
34
+ <NativeTabs.Trigger.Label>Settings</NativeTabs.Trigger.Label>
35
+ </NativeTabs.Trigger>
36
+ <NativeTabs.Trigger name="(search)" role="search">
37
+ <NativeTabs.Trigger.Label>Search</NativeTabs.Trigger.Label>
38
+ </NativeTabs.Trigger>
39
+ </NativeTabs>
40
+ );
41
+ }
42
+ ```
43
+
44
+ ## Rules
45
+
46
+ - You must include a trigger for each tab
47
+ - The `NativeTabs.Trigger` 'name' must match the route name, including parentheses (e.g. `<NativeTabs.Trigger name="(search)">`)
48
+ - Prefer search tab to be last in the list so it can combine with the search bar
49
+ - Use the 'role' prop for common tab types
50
+ - Tabs must be static — no dynamic addition/removal at runtime (remounts navigator, loses state)
51
+
52
+ ## Platform Features
53
+
54
+ Native Tabs use platform-specific tab bar implementations:
55
+
56
+ - **iOS 26+**: Liquid glass effects with system-native appearance
57
+ - **Android**: Material 3 bottom navigation
58
+ - Better performance and native feel
59
+
60
+ ## Icon Component
61
+
62
+ ```tsx
63
+ // SF Symbol (iOS) + Material Symbol (Android)
64
+ <NativeTabs.Trigger.Icon sf="house.fill" md="home" />
65
+
66
+ // State variants
67
+ <NativeTabs.Trigger.Icon sf={{ default: "house", selected: "house.fill" }} md="home" />
68
+
69
+ // Custom image
70
+ <NativeTabs.Trigger.Icon src={require('./icon.png')} />
71
+
72
+ // Xcode asset catalog — iOS only (SDK 55+)
73
+ <NativeTabs.Trigger.Icon xcasset="home-icon" />
74
+ <NativeTabs.Trigger.Icon xcasset={{ default: "home-outline", selected: "home-filled" }} />
75
+
76
+ // Rendering mode — iOS only (SDK 55+)
77
+ <NativeTabs.Trigger.Icon src={require('./icon.png')} renderingMode="template" />
78
+ <NativeTabs.Trigger.Icon src={require('./gradient.png')} renderingMode="original" />
79
+ ```
80
+
81
+ `renderingMode`: `"template"` applies tint color (single-color icons), `"original"` preserves source colors (gradients). Android always uses original.
82
+
83
+ ## Label & Badge
84
+
85
+ ```tsx
86
+ // Label
87
+ <NativeTabs.Trigger.Label>Home</NativeTabs.Trigger.Label>
88
+ <NativeTabs.Trigger.Label hidden>Home</NativeTabs.Trigger.Label> {/* icon-only tab */}
89
+
90
+ // Badge
91
+ <NativeTabs.Trigger.Badge>9+</NativeTabs.Trigger.Badge>
92
+ <NativeTabs.Trigger.Badge /> {/* dot indicator */}
93
+ ```
94
+
95
+ ## iOS 26 Features
96
+
97
+ ### Liquid Glass Tab Bar
98
+
99
+ The tab bar automatically adopts liquid glass appearance on iOS 26+.
100
+
101
+ ### Minimize on Scroll
102
+
103
+ ```tsx
104
+ <NativeTabs minimizeBehavior="onScrollDown">
105
+ ```
106
+
107
+ ### Search Tab
108
+
109
+ ```tsx
110
+ <NativeTabs.Trigger name="(search)" role="search">
111
+ <NativeTabs.Trigger.Label>Search</NativeTabs.Trigger.Label>
112
+ </NativeTabs.Trigger>
113
+ ```
114
+
115
+ **Note**: Place search tab last for best UX.
116
+
117
+ ### Role Prop
118
+
119
+ Use semantic roles for special tab types:
120
+
121
+ ```tsx
122
+ <NativeTabs.Trigger name="search" role="search" />
123
+ <NativeTabs.Trigger name="favorites" role="favorites" />
124
+ <NativeTabs.Trigger name="more" role="more" />
125
+ ```
126
+
127
+ Available roles: `search` | `more` | `favorites` | `bookmarks` | `contacts` | `downloads` | `featured` | `history` | `mostRecent` | `mostViewed` | `recents` | `topRated`
128
+
129
+ ## Customization
130
+
131
+ ### Tint Color
132
+
133
+ ```tsx
134
+ <NativeTabs tintColor="#007AFF">
135
+ ```
136
+
137
+ ### Dynamic Colors (iOS)
138
+
139
+ Use DynamicColorIOS for colors that adapt to liquid glass:
140
+
141
+ ```tsx
142
+ import { DynamicColorIOS, Platform } from 'react-native';
143
+
144
+ const adaptiveBlue = Platform.select({
145
+ ios: DynamicColorIOS({ light: '#007AFF', dark: '#0A84FF' }),
146
+ default: '#007AFF',
147
+ });
148
+
149
+ <NativeTabs tintColor={adaptiveBlue}>
150
+ ```
151
+
152
+ ## Conditional Tabs
153
+
154
+ ```tsx
155
+ <NativeTabs.Trigger name="admin" hidden={!isAdmin}>
156
+ <NativeTabs.Trigger.Label>Admin</NativeTabs.Trigger.Label>
157
+ <NativeTabs.Trigger.Icon sf="shield.fill" md="shield" />
158
+ </NativeTabs.Trigger>
159
+ ```
160
+
161
+ **Don't hide the tabs when they are visible - toggling visibility remounts the navigator; Do it only during the initial render.**
162
+
163
+ **Note**: Hidden tabs cannot be navigated to!
164
+
165
+ ## Behavior Options
166
+
167
+ ```tsx
168
+ <NativeTabs.Trigger
169
+ name="home"
170
+ disablePopToTop // Don't pop stack when tapping active tab
171
+ disableScrollToTop // Don't scroll to top when tapping active tab
172
+ disableAutomaticContentInsets // Opt out of automatic safe area insets (SDK 55+)
173
+ >
174
+ ```
175
+
176
+ ## Hidden Tab Bar (SDK 55+)
177
+
178
+ Use `hidden` prop on `NativeTabs` to hide the entire tab bar dynamically:
179
+
180
+ ```tsx
181
+ <NativeTabs hidden={isTabBarHidden}>{/* triggers */}</NativeTabs>
182
+ ```
183
+
184
+ ## Bottom Accessory (SDK 55+)
185
+
186
+ `NativeTabs.BottomAccessory` renders content above the tab bar (iOS 26+). Uses `usePlacement()` to adapt between `'regular'` and `'inline'` layouts.
187
+
188
+ **Important**: Two instances render simultaneously — store state outside the component (props, context, or external store).
189
+
190
+ ```tsx
191
+ import { NativeTabs } from "expo-router/unstable-native-tabs";
192
+ import { useState } from "react";
193
+ import { Pressable, Text, View } from "react-native";
194
+
195
+ function MiniPlayer({
196
+ isPlaying,
197
+ onToggle,
198
+ }: {
199
+ isPlaying: boolean;
200
+ onToggle: () => void;
201
+ }) {
202
+ const placement = NativeTabs.BottomAccessory.usePlacement();
203
+ if (placement === "inline") {
204
+ return (
205
+ <Pressable onPress={onToggle}>
206
+ <SymbolView name={isPlaying ? "pause.fill" : "play.fill"} />
207
+ </Pressable>
208
+ );
209
+ }
210
+ return <View>{/* full player UI */}</View>;
211
+ }
212
+
213
+ export default function TabLayout() {
214
+ const [isPlaying, setIsPlaying] = useState(false);
215
+ return (
216
+ <NativeTabs>
217
+ <NativeTabs.BottomAccessory>
218
+ <MiniPlayer
219
+ isPlaying={isPlaying}
220
+ onToggle={() => setIsPlaying(!isPlaying)}
221
+ />
222
+ </NativeTabs.BottomAccessory>
223
+ <NativeTabs.Trigger name="index">
224
+ <NativeTabs.Trigger.Icon sf="house.fill" md="home" />
225
+ <NativeTabs.Trigger.Label>Home</NativeTabs.Trigger.Label>
226
+ </NativeTabs.Trigger>
227
+ </NativeTabs>
228
+ );
229
+ }
230
+ ```
231
+
232
+ ## Safe Area Handling (SDK 55+)
233
+
234
+ SDK 55 handles safe areas automatically:
235
+
236
+ - **Android**: Content wrapped in SafeAreaView (bottom inset)
237
+ - **iOS**: First ScrollView gets automatic `contentInsetAdjustmentBehavior`
238
+
239
+ To opt out per-tab, use `disableAutomaticContentInsets` and manage manually:
240
+
241
+ ```tsx
242
+ <NativeTabs.Trigger name="index" disableAutomaticContentInsets>
243
+ <NativeTabs.Trigger.Label>Home</NativeTabs.Trigger.Label>
244
+ </NativeTabs.Trigger>
245
+ ```
246
+
247
+ ```tsx
248
+ // In the screen
249
+ import { SafeAreaView } from "react-native-screens/experimental";
250
+
251
+ export default function HomeScreen() {
252
+ return (
253
+ <SafeAreaView edges={{ bottom: true }} style={{ flex: 1 }}>
254
+ {/* content */}
255
+ </SafeAreaView>
256
+ );
257
+ }
258
+ ```
259
+
260
+ ## Using Vector Icons
261
+
262
+ If you must use @expo/vector-icons instead of SF Symbols:
263
+
264
+ ```tsx
265
+ import { NativeTabs } from "expo-router/unstable-native-tabs";
266
+ import Ionicons from "@expo/vector-icons/Ionicons";
267
+
268
+ <NativeTabs.Trigger name="home">
269
+ <NativeTabs.Trigger.VectorIcon vector={Ionicons} name="home" />
270
+ <NativeTabs.Trigger.Label>Home</NativeTabs.Trigger.Label>
271
+ </NativeTabs.Trigger>
272
+ ```
273
+
274
+ **Prefer SF Symbols + `md` prop over vector icons for native feel.**
275
+
276
+ If you are using SDK 55 and later **use the md prop to specify Material Symbols used on Android**.
277
+
278
+ ## Structure with Stacks
279
+
280
+ Native tabs don't render headers. Nest Stacks inside each tab for navigation headers:
281
+
282
+ ```tsx
283
+ // app/(tabs)/_layout.tsx
284
+ import { NativeTabs } from "expo-router/unstable-native-tabs";
285
+
286
+ export default function TabLayout() {
287
+ return (
288
+ <NativeTabs>
289
+ <NativeTabs.Trigger name="(home)">
290
+ <NativeTabs.Trigger.Label>Home</NativeTabs.Trigger.Label>
291
+ <NativeTabs.Trigger.Icon sf="house.fill" md="home" />
292
+ </NativeTabs.Trigger>
293
+ </NativeTabs>
294
+ );
295
+ }
296
+
297
+ // app/(tabs)/(home)/_layout.tsx
298
+ import Stack from "expo-router/stack";
299
+
300
+ export default function HomeStack() {
301
+ return (
302
+ <Stack>
303
+ <Stack.Screen
304
+ name="index"
305
+ options={{ title: "Home", headerLargeTitle: true }}
306
+ />
307
+ <Stack.Screen name="details" options={{ title: "Details" }} />
308
+ </Stack>
309
+ );
310
+ }
311
+ ```
312
+
313
+ ## Custom Web Layout
314
+
315
+ Use platform-specific files for separate native and web tab layouts:
316
+
317
+ ```
318
+ app/
319
+ _layout.tsx # NativeTabs for iOS/Android
320
+ _layout.web.tsx # Headless tabs for web (expo-router/ui)
321
+ ```
322
+
323
+ Or extract to a component: `components/app-tabs.tsx` + `components/app-tabs.web.tsx`.
324
+
325
+ ## Migration from JS Tabs
326
+
327
+ ### Before (JS Tabs)
328
+
329
+ ```tsx
330
+ import { Tabs } from "expo-router";
331
+
332
+ <Tabs>
333
+ <Tabs.Screen
334
+ name="index"
335
+ options={{
336
+ title: "Home",
337
+ tabBarIcon: ({ color }) => <IconSymbol name="house.fill" color={color} />,
338
+ tabBarBadge: 3,
339
+ }}
340
+ />
341
+ </Tabs>;
342
+ ```
343
+
344
+ ### After (Native Tabs)
345
+
346
+ ```tsx
347
+ import { NativeTabs } from "expo-router/unstable-native-tabs";
348
+
349
+ <NativeTabs>
350
+ <NativeTabs.Trigger name="index">
351
+ <NativeTabs.Trigger.Label>Home</NativeTabs.Trigger.Label>
352
+ <NativeTabs.Trigger.Icon sf="house.fill" md="home" />
353
+ <NativeTabs.Trigger.Badge>3</NativeTabs.Trigger.Badge>
354
+ </NativeTabs.Trigger>
355
+ </NativeTabs>;
356
+ ```
357
+
358
+ ### Key Differences
359
+
360
+ | JS Tabs | Native Tabs |
361
+ | -------------------------- | ---------------------------- |
362
+ | `<Tabs.Screen>` | `<NativeTabs.Trigger>` |
363
+ | `options={{ title }}` | `<NativeTabs.Trigger.Label>` |
364
+ | `options={{ tabBarIcon }}` | `<NativeTabs.Trigger.Icon>` |
365
+ | `tabBarBadge` option | `<NativeTabs.Trigger.Badge>` |
366
+ | Props-based API | Component-based API |
367
+ | Headers built-in | Nest `<Stack>` for headers |
368
+
369
+ ## Limitations
370
+
371
+ - **Android**: Maximum 5 tabs (Material Design constraint)
372
+ - **Nesting**: Native tabs cannot nest inside other native tabs
373
+ - **Tab bar height**: Cannot be measured programmatically
374
+ - **FlatList transparency**: Use `disableTransparentOnScrollEdge` to fix issues
375
+ - **Dynamic tabs**: Tabs must be static; changes remount navigator and lose state
376
+
377
+ ## Keyboard Handling (Android)
378
+
379
+ Configure in app.json:
380
+
381
+ ```json
382
+ {
383
+ "expo": {
384
+ "android": {
385
+ "softwareKeyboardLayoutMode": "resize"
386
+ }
387
+ }
388
+ }
389
+ ```
390
+
391
+ ## Common Issues
392
+
393
+ 1. **Icons not showing on Android**: Add `md` prop (SDK 55) or use VectorIcon
394
+ 2. **Headers missing**: Nest a Stack inside each tab group
395
+ 3. **Trigger name mismatch**: `name` must match exact route name including parentheses
396
+ 4. **Badge not visible**: Badge must be a child of Trigger, not a prop
397
+ 5. **Tab bar transparent on iOS 18 and earlier**: If the screen uses a `ScrollView` or `FlatList`, make sure it is the first opaque child of the screen component. If it needs to be wrapped in another `View`, ensure the wrapper uses `collapsable={false}`. If the screen does not use a `ScrollView` or `FlatList`, set `disableTransparentOnScrollEdge` to `true` in the `NativeTabs.Trigger` options, to make the tab bar opaque.
398
+ 6. **Scroll to top not working**: Ensure `disableScrollToTop` is not set on the active tab's Trigger and `ScrollView` is the first child of the screen component.
399
+ 7. **Header buttons flicker when navigating between tabs**: Make sure the app is wrapped in a `ThemeProvider`
400
+
401
+ ```tsx
402
+ import {
403
+ ThemeProvider,
404
+ DarkTheme,
405
+ DefaultTheme,
406
+ } from "@react-navigation/native";
407
+ import { useColorScheme } from "react-native";
408
+ import { Stack } from "expo-router";
409
+
410
+ export default function Layout() {
411
+ const colorScheme = useColorScheme();
412
+ return (
413
+ <ThemeProvider theme={colorScheme === "dark" ? DarkTheme : DefaultTheme}>
414
+ <Stack />
415
+ </ThemeProvider>
416
+ );
417
+ }
418
+ ```
419
+
420
+ If the app only uses a light or dark theme, you can directly pass `DarkTheme` or `DefaultTheme` to `ThemeProvider` without checking the color scheme.
421
+
422
+ ```tsx
423
+ import { ThemeProvider, DarkTheme } from "@react-navigation/native";
424
+ import { Stack } from "expo-router";
425
+
426
+ export default function Layout() {
427
+ return (
428
+ <ThemeProvider theme={DarkTheme}>
429
+ <Stack />
430
+ </ThemeProvider>
431
+ );
432
+ }
433
+ ```