Layout files can be placed in any folder inside the ./app
directory. They serve as the frame for the routes contained within that folder, and they can nest inside of each other if you place them in sub-directories.
Layouts must render one of the following to show the matched pages that exist in their directory:
This looks something like this at the simplest:
app/_layout.tsx
As of now, layouts don't support loaders. If you need to access the current route params, you can use the useParams hook.
The root layout on web controls the entire page, from <html>
on down. You can optionally return html
, body
, and head
elements in your app/_layout.tsx
and they will "just work" - on web, it will be output, and on native it won't so as to avoid rendering errors:
app/_layout.tsx
The root _layout.tsx
can use a special hook that allows it to insert tags into your <head />
tag after server rendering finishes. This is an edge-case that is mostly only useful for CSS-in-JS libraries. You can use it like so:
If you'd like to logically group together some pages without creating a sub-route, you can use a group folder by naming it like so:
Groups let you add extra layouts. If done right, this gives you a lot of control over how your app feels.
A common pattern that apps have is something like Twitter/X, where you have bottom tabs for your "top level" views, but then on some of the bottom tab sections, you want to have a Stack that remembers its state inside just that tab.
This pattern can be incredibly verbose to link together with React Navigation, and takes a bit of tinkering to figure out with One. Since it is common and a useful example, lets walk through how you'd build on using One's file system routing.
Here's our file structure:
The top level Layout will define our tabs:
app/_layout.tsx
This will set us up with three bottom tabs: Feed, Notifications, and Profile. The Notifications and Profile tabs for now will just show their content directly, but inside of the Feed tab, we want to show a stack.
We set up the stack in (feed)/_layout.tsx
:
(feed)/_layout.tsx
One thing we're showing here is that the layout is diverging between web and native. On web, we are showing a Slot, while on Native we show a Stack. This is because browsers feel better without stacks - the native back/forward button serves as our stack controller.
On Native we are defining the configuration for each sub-screen with the Stack.Screen
component. The Stack component is a React Navigation native stack navigator and nothing more, it accepts all the props you'd expect.
You'll notice we are matching the name
to the file names names of the sub-routes, without the .tsx
extension.
This will get you a nice Stack-inside-Tabs pattern that is common on native apps, all with just two layouts and a few routes.
As of today layouts don't support loaders. This is on our radar, please chime in if this is helpful for you.
Also note that useParams
won't work in layouts, as they are never nested inside a route. Instead, you can use useActiveParams
.
Edit this page on GitHub.