Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[solid-query] Add solid-router + query examples #3891

Draft
wants to merge 9 commits into
base: main
Choose a base branch
from
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import {
createRootRouteWithContext,
} from '@tanstack/solid-router'
import { SolidQueryDevtools } from '@tanstack/solid-query-devtools'
import type { QueryClient } from '@tanstack/solid-query'
import { TanStackRouterDevtools } from '@tanstack/solid-router-devtools'
import type { QueryClient } from '@tanstack/solid-query'

export const Route = createRootRouteWithContext<{
queryClient: QueryClient
Expand Down
5 changes: 5 additions & 0 deletions examples/solid/kitchen-sink-solid-query-file-based/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules
.DS_Store
dist
dist-ssr
*.local
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"files.watcherExclude": {
"**/routeTree.gen.ts": true
},
"search.exclude": {
"**/routeTree.gen.ts": true
},
"files.readonlyInclude": {
"**/routeTree.gen.ts": true
}
}
6 changes: 6 additions & 0 deletions examples/solid/kitchen-sink-solid-query-file-based/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Example

To run this example:

- `npm install` or `yarn`
- `npm start` or `yarn start`
12 changes: 12 additions & 0 deletions examples/solid/kitchen-sink-solid-query-file-based/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
30 changes: 30 additions & 0 deletions examples/solid/kitchen-sink-solid-query-file-based/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "tanstack-router-solid-example-kitchen-sink-solid-query-file-based",
"private": true,
"type": "module",
"scripts": {
"dev": "vite --port 3000",
"build": "vite build",
"serve": "vite preview",
"start": "vite"
},
"dependencies": {
"@tanstack/solid-query": "^5.72.0",
"@tanstack/solid-query-devtools": "^5.72.0",
"@tanstack/solid-router": "^1.114.29",
"@tanstack/solid-router-devtools": "^1.114.29",
"@tanstack/router-plugin": "^1.114.29",
"immer": "^10.1.1",
"solid-js": "^1.9.5",
"redaxios": "^0.5.1",
"postcss": "^8.5.1",
"autoprefixer": "^10.4.20",
"tailwindcss": "^3.4.17",
"zod": "^3.24.2"
},
"devDependencies": {
"vite-plugin-solid": "^2.11.6",
"typescript": "^5.7.2",
"vite": "^6.1.0"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import type { Invoice } from '../utils/mockTodos'

export function InvoiceFields({
invoice,
disabled,
}: {
invoice?: Invoice
disabled?: boolean
}) {
return (
<div class="space-y-2">
<h2 class="font-bold text-lg">
<input
name="title"
value={invoice?.title ?? ''}
placeholder="Invoice Title"
class="border border-opacity-50 rounded p-2 w-full"
disabled={disabled}
/>
</h2>
<div>
<textarea
name="body"
value={invoice?.body ?? ''}
rows={6}
placeholder="Invoice Body..."
class="border border-opacity-50 p-2 rounded w-full"
disabled={disabled}
/>
</div>
</div>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export function Spinner({
show,
wait,
}: {
show?: boolean
wait?: `delay-${number}`
}) {
return (
<div
class={`inline-block animate-spin px-3 transition ${
(show ?? true)
? `opacity-1 duration-500 ${wait ?? 'delay-300'}`
: 'duration-500 opacity-0 delay-0'
}`}
>
</div>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import * as Solid from 'solid-js'

export function useSessionStorage<T>(key: string, initialValue: T) {
const stored = sessionStorage.getItem(key)
const [state, setState] = Solid.createSignal<T>(
stored ? JSON.parse(stored) : initialValue,
)

Solid.createEffect(() => {
sessionStorage.setItem(key, JSON.stringify(state()))
}, [state()])

return [state, setState]
}
153 changes: 153 additions & 0 deletions examples/solid/kitchen-sink-solid-query-file-based/src/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import { render } from 'solid-js/web'
import {
ErrorComponent,
RouterProvider,
createRouter,
} from '@tanstack/solid-router'
import { QueryClient, QueryClientProvider } from '@tanstack/solid-query'
import { auth } from './utils/auth'
import { Spinner } from './components/Spinner'
import { routeTree } from './routeTree.gen'
import { useSessionStorage } from './hooks/useSessionStorage'
import './styles.css'

//

export const queryClient = new QueryClient()

const router = createRouter({
routeTree,
defaultPendingComponent: () => (
<div class={`p-2 text-2xl`}>
<Spinner />
</div>
),
defaultErrorComponent: ({ error }) => <ErrorComponent error={error} />,
context: {
auth: undefined!, // We'll inject this when we render
queryClient: queryClient, // Type assertion to fix the type mismatch
},
defaultPreload: 'intent',
// Since we're using Solid 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,
})

declare module '@tanstack/solid-router' {
interface Register {
router: typeof router
}
}

function App() {
// This stuff is just to tweak our sandbox setup in real-time
const [loaderDelay, setLoaderDelay] = useSessionStorage('loaderDelay', 500)
const [pendingMs, setPendingMs] = useSessionStorage('pendingMs', 1000)
const [pendingMinMs, setPendingMinMs] = useSessionStorage('pendingMinMs', 500)

return (
<>
<div class="text-xs fixed w-52 shadow-md shadow-black/20 rounded bottom-2 left-2 bg-white dark:bg-gray-800 bg-opacity-75 border-b flex flex-col gap-1 flex-wrap items-left divide-y">
<div class="p-2 space-y-2">
<div class="flex gap-2">
<button
class="bg-blue-500 text-white rounded p-1 px-2"
onClick={() => {
setLoaderDelay(150)
}}
>
Fast
</button>
<button
class="bg-blue-500 text-white rounded p-1 px-2"
onClick={() => {
setLoaderDelay(500)
}}
>
Fast 3G
</button>
<button
class="bg-blue-500 text-white rounded p-1 px-2"
onClick={() => {
setLoaderDelay(2000)
}}
>
Slow 3G
</button>
</div>
<div>
<div>Loader Delay: {loaderDelay()}ms</div>
<input
type="range"
min="0"
max="5000"
step="100"
value={loaderDelay()}
onChange={(e) => setLoaderDelay(e.target.valueAsNumber)}
class="w-full"
/>
</div>
</div>
<div class="p-2 space-y-2">
<div class="flex gap-2">
<button
class="bg-blue-500 text-white rounded p-1 px-2"
onClick={() => {
setPendingMs(1000)
setPendingMinMs(500)
}}
>
Reset to Default
</button>
</div>
<div>
<div>defaultPendingMs: {pendingMs()}ms</div>
<input
type="range"
min="0"
max="5000"
step="100"
value={pendingMs()}
onChange={(e) => setPendingMs(e.target.valueAsNumber)}
class="w-full"
/>
</div>
<div>
<div>defaultPendingMinMs: {pendingMinMs()}ms</div>
<input
type="range"
min="0"
max="5000"
step="100"
value={pendingMinMs()}
onChange={(e) => setPendingMinMs(e.target.valueAsNumber)}
class="w-full"
/>
</div>
</div>
</div>
<RouterProvider
router={router}
defaultPreload="intent"
defaultPendingMs={pendingMs()}
defaultPendingMinMs={pendingMinMs()}
context={{
auth,
}}
/>
</>
)
}

const rootElement = document.getElementById('app')!
if (!rootElement.innerHTML) {
render(
() => (
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
),
rootElement,
)
}
Loading