Documentation
Framework
Version

React Example: Kitchen Sink File Based

import { createFileRoute } from '@tanstack/react-router' import * as React from 'react' import { Link, MatchRoute, Outlet, retainSearchParams, useNavigate, } from '@tanstack/react-router' import { z } from 'zod' import { Spinner } from '../components/Spinner' import { fetchUsers } from '../utils/mockTodos' type UsersViewSortBy = 'name' | 'id' | 'email' export const Route = createFileRoute('/dashboard/users')({ validateSearch: z.object({ usersView: z .object({ sortBy: z.enum(['name', 'id', 'email']).optional(), filterBy: z.string().optional(), }) .optional(), }).parse, search: { // Retain the usersView search param while navigating // within or to this route (or it's children!) middlewares: [retainSearchParams(['usersView'])], }, loaderDeps: ({ search }) => ({ filterBy: search.usersView?.filterBy, sortBy: search.usersView?.sortBy, }), loader: async ({ deps }) => { const users = await fetchUsers(deps) return { users, crumb: 'Users' } }, component: UsersComponent, }) function UsersComponent() { const navigate = useNavigate({ from: Route.fullPath }) const { usersView } = Route.useSearch() const { users } = Route.useLoaderData() const sortBy = usersView?.sortBy ?? 'name' const filterBy = usersView?.filterBy const [filterDraft, setFilterDraft] = React.useState(filterBy ?? '') React.useEffect(() => { setFilterDraft(filterBy ?? '') }, [filterBy]) const setSortBy = (sortBy: UsersViewSortBy) => navigate({ search: (old) => { return { ...old, usersView: { ...(old.usersView ?? {}), sortBy, }, } }, replace: true, }) React.useEffect(() => { navigate({ search: (old) => { return { ...old, usersView: { ...old.usersView, filterBy: filterDraft || undefined, }, } }, replace: true, }) }, [filterDraft]) return ( <div className="flex-1 flex"> <div className="divide-y"> <div className="py-2 px-3 flex gap-2 items-center bg-gray-100 dark:bg-gray-800"> <div>Sort By:</div> <select value={sortBy} onChange={(e) => setSortBy(e.target.value as UsersViewSortBy)} className="flex-1 border p-1 px-2 rounded" > {['name', 'id', 'email'].map((d) => { return <option key={d} value={d} children={d} /> })} </select> </div> <div className="py-2 px-3 flex gap-2 items-center bg-gray-100 dark:bg-gray-800"> <div>Filter By:</div> <input value={filterDraft} onChange={(e) => setFilterDraft(e.target.value)} placeholder="Search Names..." className="min-w-0 flex-1 border p-1 px-2 rounded" /> </div> {users.map((user) => { return ( <div key={user.id}> <Link to="/dashboard/users/user" search={{ userId: user.id, }} className="block py-2 px-3 text-blue-700" activeProps={{ className: `font-bold` }} > <pre className="text-sm"> {user.name}{' '} <MatchRoute to="/dashboard/users/user" search={{ userId: user.id, }} pending > {(match) => <Spinner show={!!match} wait="delay-50" />} </MatchRoute> </pre> </Link> </div> ) })} </div> <div className="flex-initial border-l"> <Outlet /> </div> </div> ) } 
import { createFileRoute } from '@tanstack/react-router' import * as React from 'react' import { Link, MatchRoute, Outlet, retainSearchParams, useNavigate, } from '@tanstack/react-router' import { z } from 'zod' import { Spinner } from '../components/Spinner' import { fetchUsers } from '../utils/mockTodos' type UsersViewSortBy = 'name' | 'id' | 'email' export const Route = createFileRoute('/dashboard/users')({ validateSearch: z.object({ usersView: z .object({ sortBy: z.enum(['name', 'id', 'email']).optional(), filterBy: z.string().optional(), }) .optional(), }).parse, search: { // Retain the usersView search param while navigating // within or to this route (or it's children!) middlewares: [retainSearchParams(['usersView'])], }, loaderDeps: ({ search }) => ({ filterBy: search.usersView?.filterBy, sortBy: search.usersView?.sortBy, }), loader: async ({ deps }) => { const users = await fetchUsers(deps) return { users, crumb: 'Users' } }, component: UsersComponent, }) function UsersComponent() { const navigate = useNavigate({ from: Route.fullPath }) const { usersView } = Route.useSearch() const { users } = Route.useLoaderData() const sortBy = usersView?.sortBy ?? 'name' const filterBy = usersView?.filterBy const [filterDraft, setFilterDraft] = React.useState(filterBy ?? '') React.useEffect(() => { setFilterDraft(filterBy ?? '') }, [filterBy]) const setSortBy = (sortBy: UsersViewSortBy) => navigate({ search: (old) => { return { ...old, usersView: { ...(old.usersView ?? {}), sortBy, }, } }, replace: true, }) React.useEffect(() => { navigate({ search: (old) => { return { ...old, usersView: { ...old.usersView, filterBy: filterDraft || undefined, }, } }, replace: true, }) }, [filterDraft]) return ( <div className="flex-1 flex"> <div className="divide-y"> <div className="py-2 px-3 flex gap-2 items-center bg-gray-100 dark:bg-gray-800"> <div>Sort By:</div> <select value={sortBy} onChange={(e) => setSortBy(e.target.value as UsersViewSortBy)} className="flex-1 border p-1 px-2 rounded" > {['name', 'id', 'email'].map((d) => { return <option key={d} value={d} children={d} /> })} </select> </div> <div className="py-2 px-3 flex gap-2 items-center bg-gray-100 dark:bg-gray-800"> <div>Filter By:</div> <input value={filterDraft} onChange={(e) => setFilterDraft(e.target.value)} placeholder="Search Names..." className="min-w-0 flex-1 border p-1 px-2 rounded" /> </div> {users.map((user) => { return ( <div key={user.id}> <Link to="/dashboard/users/user" search={{ userId: user.id, }} className="block py-2 px-3 text-blue-700" activeProps={{ className: `font-bold` }} > <pre className="text-sm"> {user.name}{' '} <MatchRoute to="/dashboard/users/user" search={{ userId: user.id, }} pending > {(match) => <Spinner show={!!match} wait="delay-50" />} </MatchRoute> </pre> </Link> </div> ) })} </div> <div className="flex-initial border-l"> <Outlet /> </div> </div> ) } 
Subscribe to Bytes

Your weekly dose of JavaScript news. Delivered every Monday to over 100,000 devs, for free.

Subscribe to Bytes

Your weekly dose of JavaScript news. Delivered every Monday to over 100,000 devs, for free.