Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,4 @@
**/build
**/coverage
**/dist
**/docs
pnpm-lock.yaml
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ Enjoy this library? Try the entire [TanStack](https://tanstack.com)! [React Quer
- Easy Integration w/ external caches and storage (eg. React Query, Apollo, SWR, RTKQuery)

## Example Usage

To run example React projects with Tanstack Router, see [CONTRIBUTING.md](./CONTRIBUTING.md)

<!-- Use the force, Luke! -->
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ The `createRootRouteWithContext` function is a helper function that can be used
### Examples

```tsx
import { createRootRouteWithContext, createRouter } from '@tanstack/react-router'
import {
createRootRouteWithContext,
createRouter,
} from '@tanstack/react-router'
import { QueryClient } from '@tanstack/react-query'

const rootRoute = createRootRouteWithContext<{ queryClient: QueryClient }>()({
Expand Down
2 changes: 1 addition & 1 deletion docs/framework/react/api/router/useParamsHook.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ function Component() {

// OR

const routeParams = routeApi.useParams();
const routeParams = routeApi.useParams()

// OR

Expand Down
63 changes: 26 additions & 37 deletions docs/framework/react/decisions-on-dx.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ But Tanstack Router is different. It's not your average routing library. It's no

## Tanstack Router's origin story

It's important remember that Tanstack Router's origins stem from [Nozzle.io](https://nozzle.io)'s need for a client-side routing solution that offered a first-in-class *URL Search Parameters* experience without compromising on the ***type-safety*** that was required to power its complex dashboards.
It's important remember that Tanstack Router's origins stem from [Nozzle.io](https://nozzle.io)'s need for a client-side routing solution that offered a first-in-class _URL Search Parameters_ experience without compromising on the **_type-safety_** that was required to power its complex dashboards.

And so, from Tanstack Router's very inception, every facet of it's design was meticulously thought out to ensure that its type-safety and developer experience were second to none.

Expand All @@ -36,7 +36,7 @@ But to achieve this, we had to make some decisions that deviate from the norms i

## 1. Why is the Router's configuration done this way?

When you want to leverage the Typescript's inference features to its fullest, you'll quickly realize that *Generics* are your best friend. And so, Tanstack Router uses Generics everywhere to ensure that the types of your routes are inferred as much as possible.
When you want to leverage the Typescript's inference features to its fullest, you'll quickly realize that _Generics_ are your best friend. And so, Tanstack Router uses Generics everywhere to ensure that the types of your routes are inferred as much as possible.

This means that you have to define your routes in a way that allows Typescript to infer the types of your routes as much as possible.

Expand All @@ -54,7 +54,7 @@ function App() {
{/* ... */}
</Router>
// ^? Typescript cannot infer the routes in this configuration
);
)
}
```

Expand All @@ -67,22 +67,22 @@ And since this would mean that you'd have to manually type the `to` prop of the
const router = createRouter({
routes: {
posts: {
component: PostsPage, // /posts
component: PostsPage, // /posts
children: {
"$postId": {
component: PostIdPage // /posts/$postId
}
}
$postId: {
component: PostIdPage, // /posts/$postId
},
},
},
// ...
}
},
})
```

At first glance, this seems like a good idea. It's easy to visualize the entire route hierarchy in one go. But this approach has a couple big downsides that make it not ideal for large applications:

* **It's not very scalable**: As your application grows, the tree will grow and become harder to manage. And since its all defined in one file, it can become very hard to maintain.
* **It's not great for code-splitting**: You'd have to manually code-split each component and then pass it into the `component` property of the route, further complicating the route configuration with an ever-growing route configuration file.
- **It's not very scalable**: As your application grows, the tree will grow and become harder to manage. And since its all defined in one file, it can become very hard to maintain.
- **It's not great for code-splitting**: You'd have to manually code-split each component and then pass it into the `component` property of the route, further complicating the route configuration with an ever-growing route configuration file.

This only get worse as your begin to use more features of the router, such as nested context, loaders, search param validation, etc.

Expand Down Expand Up @@ -110,10 +110,7 @@ There were two approaches we considered for this:
import { router } from '@/src/app'
export const PostsIdLink = () => {
return (
<Link<typeof router>
to='/posts/$postId'
params={{ postId: '123' }}
>
<Link<typeof router> to="/posts/$postId" params={{ postId: '123' }}>
Go to post 123
</Link>
)
Expand Down Expand Up @@ -141,7 +138,7 @@ And then you can benefit from its auto-complete anywhere in your app without hav
export const PostsIdLink = () => {
return (
<Link
to='/posts/$postId'
to="/posts/$postId"
// ^? Typescript will auto-complete this for you
params={{ postId: '123' }} // and this too!
>
Expand All @@ -165,11 +162,11 @@ Something you'll notice (quite soon) in the Tanstack Router documentation is tha

As mentioned in the beginning, Tanstack Router was designed for complex applications that require a high degree of type-safety and maintainability. And to achieve this, the configuration of the router has be done in an precise way that allows Typescript to infer the types of your routes as much as possible.

A key difference in the set-up of a *basic* application with Tanstack Router, is that your route configurations require a function to be provided to `getParentRoute`, that returns the parent route of the current route.
A key difference in the set-up of a _basic_ application with Tanstack Router, is that your route configurations require a function to be provided to `getParentRoute`, that returns the parent route of the current route.

```tsx
import { createRoute } from '@tanstack/react-router';
import { postsRoute } from './postsRoute';
import { createRoute } from '@tanstack/react-router'
import { postsRoute } from './postsRoute'

export const postsIndexRoute = createRoute({
getParentRoute: () => postsRoute,
Expand All @@ -181,36 +178,28 @@ At this stage, this is done so the definition of `postsIndexRoute` can be aware

As such, this is a critical part of the route configuration and a point of failure if not done correctly.

But this is only one part of setting up a basic application. Tanstack Router requires the all the routes (including the root route) to be stitched into a ***route-tree*** so that it may be passed into the `createRouter` function before declaring the `Router` instance on the module for type inference. This is another critical part of the route configuration and a point of failure if not done correctly.
But this is only one part of setting up a basic application. Tanstack Router requires the all the routes (including the root route) to be stitched into a **_route-tree_** so that it may be passed into the `createRouter` function before declaring the `Router` instance on the module for type inference. This is another critical part of the route configuration and a point of failure if not done correctly.

> 🤯 If this route-tree were in its own file for an application with ~40-50 routes, it can easily grow up to 700+ lines.

```tsx
const routeTree = rootRoute.addChildren([
postsRoute.addChildren([
postsIndexRoute,
postsIdRoute
])
postsRoute.addChildren([postsIndexRoute, postsIdRoute]),
])
```

This complexity only increases as you begin to use more features of the router, such as nested context, loaders, search param validation, etc. As such, it no longer becomes feasible to define your routes in a single file. And so, users end up building their own *semi consistent* way of defining their routes across multiple files. This can lead to inconsistencies and errors in the route configuration.
This complexity only increases as you begin to use more features of the router, such as nested context, loaders, search param validation, etc. As such, it no longer becomes feasible to define your routes in a single file. And so, users end up building their own _semi consistent_ way of defining their routes across multiple files. This can lead to inconsistencies and errors in the route configuration.

Finally, comes the issue of code-splitting. As your application grows, you'll want to code-split your components to reduce the initial bundle size of your application. This can be a bit of a headache to manage when you're defining your routes in a single file or even across multiple files.

```tsx
import {
createRoute,
lazyRouteComponent
} from '@tanstack/react-router';
import { postsRoute } from './postsRoute';
import { createRoute, lazyRouteComponent } from '@tanstack/react-router'
import { postsRoute } from './postsRoute'

export const postsIndexRoute = createRoute({
getParentRoute: () => postsRoute,
path: '/',
component: lazyRouteComponent(
() => import('../page-components/posts/index')
)
component: lazyRouteComponent(() => import('../page-components/posts/index')),
})
```

Expand All @@ -225,22 +214,22 @@ Tanstack Router's file-based routing is designed to solve all of these issues. I
The file-based routing approach is powered by the Tanstack Router CLI. It performs 3 essential tasks that solve the pain points in route configuration when using code-based routing:

1. **Route configuration boilerplate**: It generates the boilerplate for your route configurations.
2. **Route tree stitching**: It stitches together your route configurations into a single cohesive route-tree. Also in the background, it correctly updates the route configurations to define the `getParentRoute` function match the routes with their parent routes.
2. **Route tree stitching**: It stitches together your route configurations into a single cohesive route-tree. Also in the background, it correctly updates the route configurations to define the `getParentRoute` function match the routes with their parent routes.
3. **Code-splitting**: It automatically code-splits your components and handles updating your route configurations with the correct lazy imports.

Let's take a look at how the route configuration for the previous example would look like with file-based routing.

```tsx
// src/routes/posts/index.lazy.ts
import { createLazyFileRoute } from '@tanstack/react-router';
import { createLazyFileRoute } from '@tanstack/react-router'

export const Route = createLazyFileRoute('/posts/')({
component: () => "Posts index component goes here!!!"
component: () => 'Posts index component goes here!!!',
})
```

That's it! No need to worry about defining the `getParentRoute` function, stitching together the route-tree, or code-splitting your components. The CLI handles all of this for you.

At no point does the Tanstack Router CLI take away your control over your route configurations. It's designed to be as flexible as possible, allowing you to define your routes in a way that suits your application whilst reducing the boilerplate and complexity of the route configuration.

> 🧠 Check out the guides for [file-based routing](./guide/file-based-routing) and [code-splitting](./guide/code-splitting) for a more in-depth explanation of how they work in Tanstack Router.
> 🧠 Check out the guides for [file-based routing](./guide/file-based-routing) and [code-splitting](./guide/code-splitting) for a more in-depth explanation of how they work in Tanstack Router.
4 changes: 2 additions & 2 deletions docs/framework/react/devtools.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ const TanStackRouterDevtools =
)
```

Then wrap the `TanStackRouterDevtools` component in suspense.
Then wrap the `TanStackRouterDevtools` component in suspense.

```tsx
<Suspense>
<TanStackRouterDevtools />
<TanStackRouterDevtools />
</Suspense>
```

Expand Down
1 change: 1 addition & 0 deletions docs/framework/react/guide/code-based-routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,7 @@ function PostComponent() {
return <div>Post ID: {postId}</div>
}
```

> 🧠 Quick tip: If your component is code-split, you can use the [getRouteApi function](./guide/code-splitting#manually-accessing-route-apis-in-other-files-with-the-routeapi-class) to avoid having to import the `postIdRoute` configuration to get access to the typed `useParams()` hook.

## Splat / Catch-All Routes
Expand Down
2 changes: 1 addition & 1 deletion docs/framework/react/guide/data-mutations.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ Without notifying your mutation management library about the route change, it's
Hopefully and hypothetically, the easiest way is for your mutation library to support a keying mechanism that will allow your mutations's state to be reset when the key changes:

```tsx
const routeApi = getRouteApi("/posts/$postId/edit")
const routeApi = getRouteApi('/posts/$postId/edit')

function EditPost() {
const { roomId } = routeApi.useParams()
Expand Down
1 change: 1 addition & 0 deletions docs/framework/react/guide/deferred-data-loading.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ function PostIdComponent() {
)
}
```

> 🧠 Quick tip: If your component is code-split, you can use the [getRouteApi function](./guide/code-splitting#manually-accessing-route-apis-in-other-files-with-the-routeapi-class) to avoid having to import the `Route` configuration to get access to the typed `useLoaderData()` hook.

The `Await` component resolves the promise by triggering the nearest suspense boundary until it is resolved, after which it renders the component's `children` as a function with the resolved data.
Expand Down
2 changes: 1 addition & 1 deletion docs/framework/react/guide/file-based-routing.md
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ File-based routing requires that you follow a few simple file naming conventions
- **`.route.tsx` File Type**
- When using directories to organize your routes, the `route` suffix can be used to create a route file at the directory's path. For example, `blog/post/route.tsx` will be used at the route file for the `/blog/post` route.
- **`.lazy.tsx` File Type**
- The `lazy` suffix can be used to code-split components for a route. For example, `blog.post.lazy.tsx` will be used as the component for the `blog.post` route.
- The `lazy` suffix can be used to code-split components for a route. For example, `blog.post.lazy.tsx` will be used as the component for the `blog.post` route.
- **`.component.tsx` File Type (⚠️ deprecated)**
- **`.errorComponent.tsx` File Type (⚠️ deprecated)**
- **`.pendingComponent.tsx` File Type (⚠️ deprecated)**
Expand Down
15 changes: 3 additions & 12 deletions docs/framework/react/guide/history-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@ If you don't create a history instance, a browser-oriented instance of this API
Once you have a history instance, you can pass it to the `Router` constructor:

```ts
import {
createMemoryHistory,
createRouter,
} from '@tanstack/react-router'
import { createMemoryHistory, createRouter } from '@tanstack/react-router'

const memoryHistory = createMemoryHistory({
initialEntries: ['/'], // Pass your initial url
Expand All @@ -34,10 +31,7 @@ The `createBrowserHistory` is the default history type. It uses the browser's hi
Hash routing can be helpful if your server doesn't support rewrites to index.html for HTTP requests (among other environments that don't have a server).

```ts
import {
createHashHistory,
createRouter,
} from '@tanstack/react-router'
import { createHashHistory, createRouter } from '@tanstack/react-router'

const hashHistory = createHashHistory()

Expand All @@ -49,10 +43,7 @@ const router = createRouter({ routeTree, history: hashHistory })
Memory routing is useful in environments that are not a browser or when you do not want components to interact with the URL.

```ts
import {
createMemoryHistory,
createRouter,
} from '@tanstack/react-router'
import { createMemoryHistory, createRouter } from '@tanstack/react-router'

const memoryHistory = createMemoryHistory({
initialEntries: ['/'], // Pass your initial url
Expand Down
Loading