import {
ErrorComponent,
HeadContent,
Link,
Outlet,
RouterProvider,
createRootRouteWithContext,
createRoute,
createRouter,
useRouter,
} from '@tanstack/solid-router'
import {
QueryClient,
QueryClientProvider,
useQuery,
} from '@tanstack/solid-query'
import './styles.css'
import { render } from 'solid-js/web'
import { SolidQueryDevtools } from '@tanstack/solid-query-devtools'
import { createEffect, createMemo } from 'solid-js'
import { TanStackRouterDevtools } from '@tanstack/solid-router-devtools'
import { NotFoundError, postQueryOptions, postsQueryOptions } from './posts'
import type { ErrorComponentProps } from '@tanstack/solid-router'
const rootRoute = createRootRouteWithContext<{
queryClient: QueryClient
}>()({
component: RootComponent,
notFoundComponent: () => {
return (
<div>
<p>This is the notFoundComponent configured on root route</p>
<Link to="/">Start Over</Link>
</div>
)
},
})
function RootComponent() {
return (
<>
<HeadContent />
<div class="p-2 flex gap-2 text-lg">
<Link
to="/"
activeProps={{
class: 'font-bold',
}}
activeOptions={{ exact: true }}
>
Home
</Link>{' '}
<Link
to="/posts"
activeProps={{
class: 'font-bold',
}}
>
Posts
</Link>{' '}
<Link
to="/route-a"
activeProps={{
class: 'font-bold',
}}
>
Pathless Layout
</Link>{' '}
<Link
// @ts-expect-error
to="/this-route-does-not-exist"
activeProps={{
class: 'font-bold',
}}
>
This Route Does Not Exist
</Link>
</div>
<hr />
<Outlet />
<SolidQueryDevtools buttonPosition="top-right" />
<TanStackRouterDevtools position="bottom-right" />
</>
)
}
const indexRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/',
component: IndexRouteComponent,
})
function IndexRouteComponent() {
return (
<div class="p-2">
<h3>Welcome Home!</h3>
</div>
)
}
const postsLayoutRoute = createRoute({
getParentRoute: () => rootRoute,
path: 'posts',
loader: ({ context: { queryClient } }) =>
queryClient.ensureQueryData(postsQueryOptions),
}).lazy(() => import('./posts.lazy').then((d) => d.Route))
const postsIndexRoute = createRoute({
getParentRoute: () => postsLayoutRoute,
path: '/',
component: PostsIndexRouteComponent,
})
function PostsIndexRouteComponent() {
return <div>Select a post.</div>
}
const postRoute = createRoute({
getParentRoute: () => postsLayoutRoute,
path: '$postId',
errorComponent: PostErrorComponent,
loader: ({ context: { queryClient }, params: { postId } }) =>
queryClient.ensureQueryData(postQueryOptions(postId)),
component: PostRouteComponent,
})
function PostErrorComponent({ error, reset }: ErrorComponentProps) {
const router = useRouter()
if (error instanceof NotFoundError) {
return <div>{error.message}</div>
}
createEffect(() => {
reset()
queryClient.resetQueries()
})
return (
<div>
<button
onClick={() => {
router.invalidate()
}}
>
retry
</button>
<ErrorComponent error={error} />
</div>
)
}
function PostRouteComponent() {
const params = postRoute.useParams()
const postQuery = useQuery(() => postQueryOptions(params().postId))
const post = createMemo(() => postQuery.data)
return (
<div class="space-y-2">
<h4 class="text-xl font-bold underline">{post()?.title}</h4>
<div class="text-sm">{post()?.body}</div>
</div>
)
}
const pathlessLayoutRoute = createRoute({
getParentRoute: () => rootRoute,
id: '_pathlessLayout',
component: PathlessLayoutComponent,
})
function PathlessLayoutComponent() {
return (
<div class="p-2">
<div class="border-b">I'm a pathless layout</div>
<div>
<Outlet />
</div>
</div>
)
}
const nestedPathlessLayoutRoute = createRoute({
getParentRoute: () => pathlessLayoutRoute,
id: '_nestedPathlessLayout',
component: NestedPathlessLayoutComponent,
})
function NestedPathlessLayoutComponent() {
return (
<div>
<div>I'm a nested pathless layout</div>
<div class="flex gap-2 border-b">
<Link
to="/route-a"
activeProps={{
class: 'font-bold',
}}
>
Go to route A
</Link>
<Link
to="/route-b"
activeProps={{
class: 'font-bold',
}}
>
Go to route B
</Link>
</div>
<div>
<Outlet />
</div>
</div>
)
}
const pathlessLayoutARoute = createRoute({
getParentRoute: () => nestedPathlessLayoutRoute,
path: '/route-a',
component: PathlessLayoutAComponent,
})
function PathlessLayoutAComponent() {
return <div>I'm A!</div>
}
const pathlessLayoutBRoute = createRoute({
getParentRoute: () => nestedPathlessLayoutRoute,
path: '/route-b',
component: PathlessLayoutBComponent,
})
function PathlessLayoutBComponent() {
return <div>I'm B!</div>
}
const routeTree = rootRoute.addChildren([
postsLayoutRoute.addChildren([postRoute, postsIndexRoute]),
pathlessLayoutRoute.addChildren([
nestedPathlessLayoutRoute.addChildren([
pathlessLayoutARoute,
pathlessLayoutBRoute,
]),
]),
indexRoute,
])
const queryClient = new QueryClient()
// Set up a Router instance
const router = createRouter({
routeTree,
defaultPreload: 'intent',
// Since we're using React Query, we don't want loader calls to ever be stale
// This will ensure that the loader is always called when the route is preloaded or visited
defaultPreloadStaleTime: 0,
scrollRestoration: true,
context: {
queryClient,
},
})
// Register things for typesafety
declare module '@tanstack/solid-router' {
interface Register {
router: typeof router
}
}
const rootElement = document.getElementById('app')!
if (!rootElement.innerHTML) {
render(
() => (
<QueryClientProvider client={queryClient}>
<RouterProvider router={router} />
</QueryClientProvider>
),
rootElement,
)
}
import {
ErrorComponent,
HeadContent,
Link,
Outlet,
RouterProvider,
createRootRouteWithContext,
createRoute,
createRouter,
useRouter,
} from '@tanstack/solid-router'
import {
QueryClient,
QueryClientProvider,
useQuery,
} from '@tanstack/solid-query'
import './styles.css'
import { render } from 'solid-js/web'
import { SolidQueryDevtools } from '@tanstack/solid-query-devtools'
import { createEffect, createMemo } from 'solid-js'
import { TanStackRouterDevtools } from '@tanstack/solid-router-devtools'
import { NotFoundError, postQueryOptions, postsQueryOptions } from './posts'
import type { ErrorComponentProps } from '@tanstack/solid-router'
const rootRoute = createRootRouteWithContext<{
queryClient: QueryClient
}>()({
component: RootComponent,
notFoundComponent: () => {
return (
<div>
<p>This is the notFoundComponent configured on root route</p>
<Link to="/">Start Over</Link>
</div>
)
},
})
function RootComponent() {
return (
<>
<HeadContent />
<div class="p-2 flex gap-2 text-lg">
<Link
to="/"
activeProps={{
class: 'font-bold',
}}
activeOptions={{ exact: true }}
>
Home
</Link>{' '}
<Link
to="/posts"
activeProps={{
class: 'font-bold',
}}
>
Posts
</Link>{' '}
<Link
to="/route-a"
activeProps={{
class: 'font-bold',
}}
>
Pathless Layout
</Link>{' '}
<Link
// @ts-expect-error
to="/this-route-does-not-exist"
activeProps={{
class: 'font-bold',
}}
>
This Route Does Not Exist
</Link>
</div>
<hr />
<Outlet />
<SolidQueryDevtools buttonPosition="top-right" />
<TanStackRouterDevtools position="bottom-right" />
</>
)
}
const indexRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/',
component: IndexRouteComponent,
})
function IndexRouteComponent() {
return (
<div class="p-2">
<h3>Welcome Home!</h3>
</div>
)
}
const postsLayoutRoute = createRoute({
getParentRoute: () => rootRoute,
path: 'posts',
loader: ({ context: { queryClient } }) =>
queryClient.ensureQueryData(postsQueryOptions),
}).lazy(() => import('./posts.lazy').then((d) => d.Route))
const postsIndexRoute = createRoute({
getParentRoute: () => postsLayoutRoute,
path: '/',
component: PostsIndexRouteComponent,
})
function PostsIndexRouteComponent() {
return <div>Select a post.</div>
}
const postRoute = createRoute({
getParentRoute: () => postsLayoutRoute,
path: '$postId',
errorComponent: PostErrorComponent,
loader: ({ context: { queryClient }, params: { postId } }) =>
queryClient.ensureQueryData(postQueryOptions(postId)),
component: PostRouteComponent,
})
function PostErrorComponent({ error, reset }: ErrorComponentProps) {
const router = useRouter()
if (error instanceof NotFoundError) {
return <div>{error.message}</div>
}
createEffect(() => {
reset()
queryClient.resetQueries()
})
return (
<div>
<button
onClick={() => {
router.invalidate()
}}
>
retry
</button>
<ErrorComponent error={error} />
</div>
)
}
function PostRouteComponent() {
const params = postRoute.useParams()
const postQuery = useQuery(() => postQueryOptions(params().postId))
const post = createMemo(() => postQuery.data)
return (
<div class="space-y-2">
<h4 class="text-xl font-bold underline">{post()?.title}</h4>
<div class="text-sm">{post()?.body}</div>
</div>
)
}
const pathlessLayoutRoute = createRoute({
getParentRoute: () => rootRoute,
id: '_pathlessLayout',
component: PathlessLayoutComponent,
})
function PathlessLayoutComponent() {
return (
<div class="p-2">
<div class="border-b">I'm a pathless layout</div>
<div>
<Outlet />
</div>
</div>
)
}
const nestedPathlessLayoutRoute = createRoute({
getParentRoute: () => pathlessLayoutRoute,
id: '_nestedPathlessLayout',
component: NestedPathlessLayoutComponent,
})
function NestedPathlessLayoutComponent() {
return (
<div>
<div>I'm a nested pathless layout</div>
<div class="flex gap-2 border-b">
<Link
to="/route-a"
activeProps={{
class: 'font-bold',
}}
>
Go to route A
</Link>
<Link
to="/route-b"
activeProps={{
class: 'font-bold',
}}
>
Go to route B
</Link>
</div>
<div>
<Outlet />
</div>
</div>
)
}
const pathlessLayoutARoute = createRoute({
getParentRoute: () => nestedPathlessLayoutRoute,
path: '/route-a',
component: PathlessLayoutAComponent,
})
function PathlessLayoutAComponent() {
return <div>I'm A!</div>
}
const pathlessLayoutBRoute = createRoute({
getParentRoute: () => nestedPathlessLayoutRoute,
path: '/route-b',
component: PathlessLayoutBComponent,
})
function PathlessLayoutBComponent() {
return <div>I'm B!</div>
}
const routeTree = rootRoute.addChildren([
postsLayoutRoute.addChildren([postRoute, postsIndexRoute]),
pathlessLayoutRoute.addChildren([
nestedPathlessLayoutRoute.addChildren([
pathlessLayoutARoute,
pathlessLayoutBRoute,
]),
]),
indexRoute,
])
const queryClient = new QueryClient()
// Set up a Router instance
const router = createRouter({
routeTree,
defaultPreload: 'intent',
// Since we're using React Query, we don't want loader calls to ever be stale
// This will ensure that the loader is always called when the route is preloaded or visited
defaultPreloadStaleTime: 0,
scrollRestoration: true,
context: {
queryClient,
},
})
// Register things for typesafety
declare module '@tanstack/solid-router' {
interface Register {
router: typeof router
}
}
const rootElement = document.getElementById('app')!
if (!rootElement.innerHTML) {
render(
() => (
<QueryClientProvider client={queryClient}>
<RouterProvider router={router} />
</QueryClientProvider>
),
rootElement,
)
}
Your weekly dose of JavaScript news. Delivered every Monday to over 100,000 devs, for free.