<Tabs />

This component should only be rendered inside a _layout.tsx file, where it will serve as the location that children will render for routes below the layout.

Tabs is a React Navigation Bottom Tabs view and accepts the same props as React Navigation. On iOS and Android it uses React Navigation 8’s native tab implementation. On web it uses One’s headless implementation.

The screen name matches the route file name. One derives the link from the route tree.

import { Tabs } from 'one'
export default function Layout() {
return (
<Tabs>
<Tabs.Screen
name="explore"
options={{
title: 'Explore',
}}
/>
</Tabs>
)
}
Adding a new tab route has a bug currently that can cause it to not show up until you re-run your app with --clean.

Native tabs

React Navigation owns the native tab bar on iOS and Android. Use its native tab options directly, including tabBarIcon, tabBarSystemItem, tabBarMinimizeBehavior, and tabBarSelectionEnabled. An action tab listens for tabPress and disables route selection:

<Tabs.Screen
name="new"
listeners={{
tabPress() {
openComposer()
},
}}
options={{
title: 'New',
tabBarSelectionEnabled: false,
tabBarIcon: ({ focused }) => ({
type: 'sfSymbol',
name: focused ? 'plus.circle.fill' : 'plus.circle',
}),
}}
/>

The href screen option belongs to One’s web headless implementation. Native layouts reject it so a hidden or redirected tab cannot silently become an ordinary native tab. Put screens that should not appear as tabs in a parent Stack. If a web tab needs a custom target, define that option in the route layout’s .web.tsx sibling.

On web

new

On web, <Tabs> renders headless: only the focused tab’s screen, no tab bar. Your site’s own navigation is your tab bar.

To build one, place a component as a child of <Tabs> - it replaces the headless default on web and is ignored on native. Use useTabs to get the tab list and the focused tab:

// app/(tabs)/_layout.web.tsx
import { Link, Tabs, useTabs } from 'one'
function WebTabBar() {
const { screens, focused } = useTabs()
return (
<>
<nav>
{screens.map((tab) => (
<Link
key={tab.name}
href={tab.href}
className={tab.isFocused ? 'active' : undefined}
>
{tab.options.title}
</Link>
))}
</nav>
{focused.element}
</>
)
}
export default function Layout() {
return (
<Tabs>
<Tabs.Screen name="explore" options={{ title: 'Explore', href: '/explore' }} />
<Tabs.Screen name="profile" options={{ title: 'Profile', href: '/profile' }} />
<WebTabBar />
</Tabs>
)
}

See the headless web navigators guide for more, including how native and web can share the same layout file.

Edit this page on GitHub.