Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
8b20969
feat(react-router): add types for routeContext
chorobin Aug 7, 2024
701e356
perf(react-router): optimise new types for `routeContext`
chorobin Aug 7, 2024
9ca86b7
chore(react-router): add type tests for routeContext and beforeLoad
chorobin Aug 8, 2024
9372a8d
checkpoint
tannerlinsley Aug 9, 2024
4ef23bd
ci: apply automated fixes
autofix-ci[bot] Aug 9, 2024
6dc5a40
updates docs
tannerlinsley Aug 9, 2024
b6b5e71
fix: tests
tannerlinsley Aug 9, 2024
7d673f9
fix: context
tannerlinsley Aug 9, 2024
9907d8f
chore: fix the lockfile
SeanCassiere Aug 10, 2024
9f442e0
ci: apply automated fixes
autofix-ci[bot] Aug 10, 2024
ee6c757
style(react-router): fixup the eslint errors
SeanCassiere Aug 10, 2024
ddc15f1
fix: hydration mismatch during SSR, refactor matchRoutes to take loca…
tannerlinsley Aug 10, 2024
7c7d873
fix: serialize beforeLoadContext during SSR
tannerlinsley Aug 11, 2024
d78275a
examples: add start-basic-auth
tannerlinsley Aug 14, 2024
0cb18aa
fix: history export parseHref
tannerlinsley Aug 14, 2024
9c5d0d7
feat: routeOptions.preload to allow disabling route-level preloading
tannerlinsley Aug 14, 2024
7f5dcc8
fix: full support for href redirects
tannerlinsley Aug 14, 2024
3353a4d
feat: useServerFn hook to allow handling server side effects like red…
tannerlinsley Aug 14, 2024
1c32a1b
fix: server redirect and notFound now default to 200 status code for …
tannerlinsley Aug 14, 2024
5f4b160
fix: update pnpm
tannerlinsley Aug 14, 2024
be8acb4
ci: apply automated fixes
autofix-ci[bot] Aug 14, 2024
6d8abc5
Merge remote-tracking branch 'origin/main' into route-context
chorobin Aug 14, 2024
c958b9d
examples: fix start-basic-auth
tannerlinsley Aug 15, 2024
922b504
tests: fix playwright
tannerlinsley Aug 16, 2024
fc6b55e
tests: fix playwright
tannerlinsley Aug 16, 2024
51bbf8f
tests: fix playwright
tannerlinsley Aug 16, 2024
b79ca4b
Merge branch 'main' into route-context
tannerlinsley Aug 16, 2024
9337dc2
sean's changes to the route context branch (#2141)
SeanCassiere Aug 17, 2024
b6a128e
test(react-router): add runtime unit tests for the `context` function
SeanCassiere Aug 20, 2024
b339841
examples: init start-clerk-basic
tannerlinsley Aug 20, 2024
630c6ad
Merge branch 'main' into route-context
tannerlinsley Aug 20, 2024
a894157
fix: lockfile
tannerlinsley Aug 20, 2024
6f7a669
ci: apply automated fixes
autofix-ci[bot] Aug 20, 2024
e16410d
test: remove failing test for WIP clerk example
tannerlinsley Aug 20, 2024
6377115
tests: fix more clerk wip tests
tannerlinsley Aug 20, 2024
cbd8ba9
fix: lockfile
tannerlinsley Aug 20, 2024
454105b
tests: more clerk fixes
tannerlinsley Aug 20, 2024
82e7a90
tests: skip WIP clerk example
tannerlinsley Aug 20, 2024
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 docs/framework/react/api/router/RouteMatchType.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ interface RouteMatch {
updatedAt: number
loadPromise?: Promise<void>
loaderData?: Route['loaderData']
routeContext: Route['routeContext']
context: Route['allContext']
search: Route['fullSearchSchema']
fetchedAt: number
Expand Down
10 changes: 5 additions & 5 deletions docs/framework/react/guide/router-context.md
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ export const Route = createFileRoute('/todos')({

## Processing Accumulated Route Context

Context, especially the isolated `routeContext` objects, make it trivial to accumulate and process the route context objects for all matched routes. Here's an example where we use all of the matched route contexts to generate a breadcrumb trail:
Context, especially the isolated route `context` objects, make it trivial to accumulate and process the route context objects for all matched routes. Here's an example where we use all of the matched route contexts to generate a breadcrumb trail:

```tsx
// src/routes/__root.tsx
Expand All @@ -306,9 +306,9 @@ export const Route = createRootRoute({
const router = useRouter()

const breadcrumbs = router.state.matches.map((match) => {
const { routeContext } = match
const { context } = match
return {
title: routeContext.getTitle(),
title: context.getTitle(),
path: match.path,
}
})
Expand All @@ -328,9 +328,9 @@ export const Route = createRootRoute({

const matchWithTitle = [...router.state.matches]
.reverse()
.find((d) => d.routeContext.getTitle)
.find((d) => d.context.getTitle)

const title = matchWithTitle?.routeContext.getTitle() || 'My App'
const title = matchWithTitle?.context.getTitle() || 'My App'

return (
<html>
Expand Down
2 changes: 1 addition & 1 deletion docs/framework/react/overview.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ TanStack Router provides a light-weight built-in caching layer that works seamle

### Flexible & Powerful Data Lifecycle APIs

TanStack Router is designed with a flexible and powerful data loading API that more easily integrates with existing data fetching libraries like TanStack Query, SWR, Apollo, Relay, or even your own custom data fetching solution. Configurable APIs like `routeContext`, `beforeLoad`, `loaderDeps` and `loader` work in unison to make it easy to define declarative data dependencies, prefetch data, and manage the lifecycle of an external data source with ease.
TanStack Router is designed with a flexible and powerful data loading API that more easily integrates with existing data fetching libraries like TanStack Query, SWR, Apollo, Relay, or even your own custom data fetching solution. Configurable APIs like `context`, `beforeLoad`, `loaderDeps` and `loader` work in unison to make it easy to define declarative data dependencies, prefetch data, and manage the lifecycle of an external data source with ease.

## Inherited Route Context

Expand Down
7 changes: 7 additions & 0 deletions examples/react/start-basic-auth/.env
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Environment variables declared in this file are automatically made available to Prisma.
# See the documentation for more detail: https://pris.ly/d/prisma-schema#accessing-environment-variables-from-the-schema

# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings

DATABASE_URL="file:./dev.db"
5 changes: 5 additions & 0 deletions examples/react/start-basic-auth/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"extends": ["react-app"],
"parser": "@typescript-eslint/parser",
"plugins": ["react-hooks"]
}
22 changes: 22 additions & 0 deletions examples/react/start-basic-auth/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
node_modules
package-lock.json
yarn.lock

!.env
.DS_Store
.cache
.vercel
.output
.vinxi

/build/
/api/
/server/build
/public/build
.vinxi
# Sentry Config File
.env.sentry-build-plugin
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
5 changes: 5 additions & 0 deletions examples/react/start-basic-auth/.prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
**/api
**/build
**/public
pnpm-lock.yaml
routeTree.gen.ts
5 changes: 5 additions & 0 deletions examples/react/start-basic-auth/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"singleQuote": true,
"semi": false,
"trailingComma": "all"
}
72 changes: 72 additions & 0 deletions examples/react/start-basic-auth/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Welcome to TanStack.com!

This site is built with TanStack Router!

- [TanStack Router Docs](https://tanstack.com/router)

It's deployed automagically with Vercel!

- [Vercel](https://vercel.com/)

## Development

From your terminal:

```sh
pnpm install
pnpm dev
```

This starts your app in development mode, rebuilding assets on file changes.

## Editing and previewing the docs of TanStack projects locally

The documentations for all TanStack projects except for `React Charts` are hosted on [https://tanstack.com](https://tanstack.com), powered by this TanStack Router app.
In production, the markdown doc pages are fetched from the GitHub repos of the projects, but in development they are read from the local file system.

Follow these steps if you want to edit the doc pages of a project (in these steps we'll assume it's [`TanStack/form`](https://github.com/tanstack/form)) and preview them locally :

1. Create a new directory called `tanstack`.

```sh
mkdir tanstack
```

2. Enter the directory and clone this repo and the repo of the project there.

```sh
cd tanstack
git clone git@github.com:TanStack/tanstack.com.git
git clone git@github.com:TanStack/form.git
```

> [!NOTE]
> Your `tanstack` directory should look like this:
>
> ```
> tanstack/
> |
> +-- form/
> |
> +-- tanstack.com/
> ```

> [!WARNING]
> Make sure the name of the directory in your local file system matches the name of the project's repo. For example, `tanstack/form` must be cloned into `form` (this is the default) instead of `some-other-name`, because that way, the doc pages won't be found.

3. Enter the `tanstack/tanstack.com` directory, install the dependencies and run the app in dev mode:

```sh
cd tanstack.com
pnpm i
# The app will run on https://localhost:3000 by default
pnpm dev
```

4. Now you can visit http://localhost:3000/form/latest/docs/overview in the browser and see the changes you make in `tanstack/form/docs`.

> [!NOTE]
> The updated pages need to be manually reloaded in the browser.

> [!WARNING]
> You will need to update the `docs/config.json` file (in the project's repo) if you add a new doc page!
12 changes: 12 additions & 0 deletions examples/react/start-basic-auth/app.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { defineConfig } from '@tanstack/start/config'
import tsConfigPaths from 'vite-tsconfig-paths'

export default defineConfig({
vite: {
plugins: () => [
tsConfigPaths({
projects: ['./tsconfig.json'],
}),
],
},
})
7 changes: 7 additions & 0 deletions examples/react/start-basic-auth/app/client.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { hydrateRoot } from 'react-dom/client'
import { StartClient } from '@tanstack/start'
import { createRouter } from './router'

const router = createRouter()

hydrateRoot(document.getElementById('root')!, <StartClient router={router} />)
57 changes: 57 additions & 0 deletions examples/react/start-basic-auth/app/components/Auth.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
export function Auth({
actionText,
onSubmit,
status,
afterSubmit,
}: {
actionText: string
onSubmit: (e: React.FormEvent<HTMLFormElement>) => void
status: 'pending' | 'idle' | 'success' | 'error'
afterSubmit?: React.ReactNode
}) {
return (
<div className="fixed inset-0 bg-white dark:bg-black flex items-start justify-center p-8">
<div className="bg-white dark:bg-gray-900 p-8 rounded-lg shadow-lg">
<h1 className="text-2xl font-bold mb-4">{actionText}</h1>
<form
onSubmit={(e) => {
e.preventDefault()
onSubmit(e)
}}
className="space-y-4"
>
<div>
<label htmlFor="email" className="block text-xs">
Username
</label>
<input
type="email"
name="email"
id="email"
className="px-2 py-1 w-full rounded border border-gray-500/20 bg-white dark:bg-gray-800"
/>
</div>
<div>
<label htmlFor="password" className="block text-xs">
Password
</label>
<input
type="password"
name="password"
id="password"
className="px-2 py-1 w-full rounded border border-gray-500/20 bg-white dark:bg-gray-800"
/>
</div>
<button
type="submit"
className="w-full bg-cyan-600 text-white rounded py-2 font-black uppercase"
disabled={status === 'pending'}
>
{status === 'pending' ? '...' : actionText}
</button>
{afterSubmit ? afterSubmit : null}
</form>
</div>
</div>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import {
ErrorComponent,
ErrorComponentProps,
Link,
rootRouteId,
useMatch,
useRouter,
} from '@tanstack/react-router'

export function DefaultCatchBoundary({ error }: ErrorComponentProps) {
const router = useRouter()
const isRoot = useMatch({
strict: false,
select: (state) => state.id === rootRouteId,
})

console.error(error)

return (
<div className="min-w-0 flex-1 p-4 flex flex-col items-center justify-center gap-6">
<ErrorComponent error={error} />
<div className="flex gap-2 items-center flex-wrap">
<button
onClick={() => {
router.invalidate()
}}
className={`px-2 py-1 bg-gray-600 dark:bg-gray-700 rounded text-white uppercase font-extrabold`}
>
Try Again
</button>
{isRoot ? (
<Link
to="/"
className={`px-2 py-1 bg-gray-600 dark:bg-gray-700 rounded text-white uppercase font-extrabold`}
>
Home
</Link>
) : (
<Link
to="/"
className={`px-2 py-1 bg-gray-600 dark:bg-gray-700 rounded text-white uppercase font-extrabold`}
onClick={(e) => {
e.preventDefault()
window.history.back()
}}
>
Go Back
</Link>
)}
</div>
</div>
)
}
69 changes: 69 additions & 0 deletions examples/react/start-basic-auth/app/components/Login.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { Link, useRouter } from '@tanstack/react-router'
import { useServerFn } from '@tanstack/start'
import { useMutation } from '../hooks/useMutation'
import { loginFn } from '../routes/_authed'
import { Auth } from './Auth'
import { signupFn } from '~/routes/signup'

export function Login() {
const router = useRouter()

const loginMutation = useMutation({
fn: loginFn,
onSuccess: (ctx) => {
if (!ctx.data?.error) {
router.invalidate()
router.navigate({
to: '/',
})
return
}
},
})

const signupMutation = useMutation({
fn: useServerFn(signupFn),
})

return (
<Auth
actionText="Login"
status={loginMutation.status}
onSubmit={(e) => {
const formData = new FormData(e.target as HTMLFormElement)

loginMutation.mutate({
email: formData.get('email') as string,
password: formData.get('password') as string,
})
}}
afterSubmit={
loginMutation.data ? (
<>
<div className="text-red-400">{loginMutation.data.message}</div>
{loginMutation.data.userNotFound ? (
<div>
<button
className="text-blue-500"
onClick={(e) => {
const formData = new FormData(
(e.target as HTMLButtonElement).form!,
)

signupMutation.mutate({
email: formData.get('email') as string,
password: formData.get('password') as string,
})
}}
type="button"
>
Sign up instead?
</button>
</div>
) : null}
</>
) : null
}
/>
)
}
Loading