The app
directory is where One handles universal file system routing. Every file you create in it will be turned into a route, except for files named _layout
or _middleware
(or files that match patterns in your ignoredRouteFiles
configuration, if you have that). One only supports file system routes at this time, but we have a discussion for those who want to help us figure out a configuration-based approach.
Here's an example:
_layout.tsx
Wraps all files in this directory and below
index.tsx
Matches "/"
blog
index.tsx
Matches "/blog"
[slug].tsx
Matches a single sub-path of "/blog", like "/blog/hello"
[...rest].tsx
Matches all sub-paths, like "/blog/hello/world"
One routes support the popular feature of nested layouts, via _layout.tsx
files, and parallel render-as-you-fetch data loading via export loader
.
All .tsx
files in the app
dir are used to match your routes, besides layouts. Each individual file is referred to as a "page" to disambiguate them from the term "route," which we use to refer to the actual URL state in a browser on web (or React Navigation state on native).
All pages must export a React component to be rendered. You can choose between two ways to export it: export default
or plain export
. If you don't default
export, we look for the first export
that is capitalized. The reason we support this is simple - the React Refresh library doesn't support hot reloading default exports. We're looking into modifying it to support them for route files, but until then you may want to simply use export
to maintain a nice hot reloading experience.
A simple page that matches the /
route and will render on native and web:
app/index.tsx
import { Text } from 'react-native'
export default function HomePage() {
return (
<Text>Hello world</Text>
)
}
If you are targeting web-only, you may use HTML elements:
app/index.tsx
export default function HomePage() {
return (
<div>Hello world</div>
)
}
All index
files match against /
, so app/index.tsx
matches to your root /
route. You can give your page a name as well, for example app/about.tsx
will the /about
route.
The segments of a route, for example /hello/world
, are referred to as "route parameters" or just "params", where "hello" and "word" are each a single parameter. Pages can match against parameters dynamically in two ways: matching a single parameter, or matching all parameters below them.
One uses brackets to match against a single dynamic parameter: ./blog/[slug].tsx
matches /blog/post-one
and /blog/post-two
:
blog.tsx
index.tsx
Matches "/blog"
[slug].tsx
Matches a single sub-path of "/blog", like "/blog/hello"
One will match ./blog/[slug].tsx
to any /blog/*
route, and pass in the parameters to the route as follows:
import { useParams } from 'one'
export function loader({ params }) {
// params.slug will be a string matching the URL parameter
}
export default function BlogPostPage() {
const params = useParams()
// params.slug will be a string matching the URL parameter
}
Much like JavaScript supports rest parameters, One supports rest parameter routes which are defined using the [...rest].tsx
syntax.
catalog.tsx
index.tsx
Matches "/catalog"
[...rest].tsx
Matches all sub-paths of "/catalog", like "/catalog/a/b/c"
In the case where a user navigates to /catalog/a/b/c
, the [...rest].tsx
route would receive a params prop as follows:
import { useParams } from 'one'
export function loader({ params }) {
// params.rest is an array ['a', 'b', 'c']
}
export default function BlogPostPage() {
const params = useParams()
// params.rest is an array ['a', 'b', 'c']
}
To have a custom 404 page, you can create a +not-found.tsx
route in any folder. It will act as the same as a [...rest].tsx
route in that it will catch all sub-routes for that folder.
Simply export a React component (default or named) from your not found page and it will render with a 404 status code.
catalog.tsx
index.tsx
Matches "/catalog"
+not-found.tsx
Matches all non-matching sub-paths of "/catalog" with a 404 status code
You can choose a rendering strategy on a per-page basis using a filename suffix. For more on these modes, see the next documentation page, Routing Modes.
route+ssg.tsx
- Matches "/route", but will render the page as a SSG route.route+spa.tsx
- Matches "/route", but will render the page as a SPA route.route+ssr.tsx
- Matches "/route", but will render the page as a SSR route.route+api.tsx
- Matches "/route", but will render the page as an API route.You can target a specific platform using the same specific extension convention as React Native - ie, using .web.tsx
, .native.tsx
, .ios.tsx
, or .android.tsx
as the last part of the filename.
This lets you diverge the routing based on the platform. For example:
index.tsx
Matches "/" on native"
index.web.tsx
Matches "/" on web
blog.web.tsx
Matches "/blog" on web, on native there will be no route
You can define a group by naming a folder with surrounding parenthesis:
_layout.tsx
(blog)
_layout.tsx
This layout will nest inside the above layout
blog.tsx
Matches "/blog"
[slug].tsx
Matches a single sub-path of "/blog", like "/blog/hello"
Groups are useful for a couple reasons:
One generates types for your routes, so that you get type checking on a variety of things like the Link component.
The types are generated into your root directory, in a routes.d.ts
file. You must make sure your tsconfig.json picks up this file.
Edit this page on GitHub.