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
5 changes: 5 additions & 0 deletions examples/solid/search-validator-adapters/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
node_modules
.DS_Store
dist
dist-ssr
*.local
11 changes: 11 additions & 0 deletions examples/solid/search-validator-adapters/.vscode/settings.json
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/search-validator-adapters/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/search-validator-adapters/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>
35 changes: 35 additions & 0 deletions examples/solid/search-validator-adapters/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"name": "tanstack-solid-router-search-validator-adapters",
"private": true,
"type": "module",
"scripts": {
"dev": "vite --port 3000",
"build": "vite build && tsc --noEmit",
"serve": "vite preview",
"start": "vite",
"test:unit": "vitest"
},
"dependencies": {
"@tailwindcss/postcss": "^4.1.15",
"@tanstack/arktype-adapter": "^1.135.2",
"@tanstack/solid-query": "^5.90.0",
"@tanstack/solid-router": "^1.135.2",
"@tanstack/solid-router-devtools": "^1.135.2",
"@tanstack/router-plugin": "^1.135.2",
"@tanstack/valibot-adapter": "^1.135.2",
"@tanstack/zod-adapter": "^1.135.2",
Comment on lines +14 to +20
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Use workspace:* protocol for internal dependencies.

All @tanstack/* packages are internal dependencies and should use the workspace:* protocol instead of semver ranges.

As per coding guidelines.

Apply this diff:

-    "@tanstack/arktype-adapter": "^1.135.2",
+    "@tanstack/arktype-adapter": "workspace:*",
     "@tanstack/solid-query": "^5.90.0",
-    "@tanstack/solid-router": "^1.135.2",
-    "@tanstack/solid-router-devtools": "^1.135.2",
-    "@tanstack/router-plugin": "^1.135.2",
-    "@tanstack/valibot-adapter": "^1.135.2",
-    "@tanstack/zod-adapter": "^1.135.2",
+    "@tanstack/solid-router": "workspace:*",
+    "@tanstack/solid-router-devtools": "workspace:*",
+    "@tanstack/router-plugin": "workspace:*",
+    "@tanstack/valibot-adapter": "workspace:*",
+    "@tanstack/zod-adapter": "workspace:*",

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In examples/solid/search-validator-adapters/package.json around lines 14 to 20,
the @tanstack/* dependencies use semver ranges; update each internal @tanstack/*
dependency to use the workspace:* protocol (e.g., replace "^1.135.2" or
"^5.90.0" with "workspace:*") so the package.json references local workspace
packages per the coding guidelines; ensure you change all listed @tanstack
entries consistently and run npm/yarn/pnpm install to validate the lockfile.

"arktype": "^2.1.7",
"postcss": "^8.5.1",
"solid-js": "^1.9.10",
"tailwindcss": "^4.1.15",
"valibot": "1.0.0-beta.15",
"zod": "^3.24.2"
},
"devDependencies": {
"@testing-library/jest-dom": "^6.6.3",
"@solidjs/testing-library": "^0.8.10",
"vite-plugin-solid": "^2.11.10",
"typescript": "^5.7.2",
"vite": "^7.1.7"
}
}
5 changes: 5 additions & 0 deletions examples/solid/search-validator-adapters/postcss.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export default {
plugins: {
'@tailwindcss/postcss': {},
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { JSX } from 'solid-js'

export interface ContentProps {
readonly children: JSX.Element
}

export const Content = ({ children }: ContentProps) => {
return <section class="my-2 flex flex-col gap-2">{children}</section>
}
22 changes: 22 additions & 0 deletions examples/solid/search-validator-adapters/src/components/Header.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Link } from '@tanstack/solid-router'

const HeaderLink: typeof Link = (props) => {
return <Link class="text-lg text-blue-700" {...props} />
}

export interface HeaderProps {
readonly title: string
}

export const Header = ({ title }: HeaderProps) => {
return (
<header class="flex items-end gap-8 border-b-2 border-slate-150 py-4">
<h1 class="text-4xl">{title}</h1>
<nav class="flex gap-4">
<HeaderLink to="/users/zod">Zod</HeaderLink>
<HeaderLink to="/users/valibot">Valibot</HeaderLink>
<HeaderLink to="/users/arktype">Arktype</HeaderLink>
</nav>
</header>
)
}
18 changes: 18 additions & 0 deletions examples/solid/search-validator-adapters/src/components/Search.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
export interface SearchProps {
search: string
onChange: (search: string) => void
}

export const Search = ({ search, onChange }: SearchProps) => {
return (
<div class="flex gap-2 align-center items-center">
<label>Search</label>
<input
type="search"
class="border p-1 px-2 rounded-sm"
value={search}
onInput={(e) => onChange(e.currentTarget.value)}
></input>
Comment on lines +6 to +15
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Give the search input an accessible name

Without associating the <label> to the <input>, assistive tech sees the control as unnamed. Please wire them together so the field remains usable for screen-reader users.

+import { createUniqueId } from 'solid-js'
+
 export interface SearchProps {
   search: string
   onChange: (search: string) => void
 }
 
 export const Search = ({ search, onChange }: SearchProps) => {
+  const inputId = createUniqueId()
+
   return (
     <div class="flex gap-2 align-center items-center">
-      <label>Search</label>
+      <label for={inputId}>Search</label>
       <input
         type="search"
         class="border p-1 px-2 rounded-sm"
+        id={inputId}
         value={search}
         onInput={(e) => onChange(e.currentTarget.value)}
       ></input>
     </div>
   )
 }
🤖 Prompt for AI Agents
In examples/solid/search-validator-adapters/src/components/Search.tsx around
lines 6 to 15, the <label> is not associated with the <input>, so screen readers
can't identify the control; fix this by giving the input a stable id (e.g.,
"search-input") and set the label's htmlFor attribute to that id (or
alternatively wrap the input inside the <label>), ensuring the input retains its
value and onInput handler so the field is properly named for assistive
technology.

</div>
)
}
84 changes: 84 additions & 0 deletions examples/solid/search-validator-adapters/src/components/Users.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { queryOptions, useQuery } from '@tanstack/solid-query'

export interface Geo {
lat: string
lng: string
}

export interface Address {
street: string
suite: string
city: string
zipcode: string
geo: Geo
}

export interface Company {
name: string
catchPhrase: string
bs: string
}

export interface User {
id: number
name: string
username: string
email: string
address: Address
phone: string
website: string
company: Company
}

export const getUsers = async (): Promise<Array<User>> => {
const response = await fetch('https://jsonplaceholder.typicode.com/users')
const users: Array<User> = await response.json()
return users
}

export const searchUsers = async (search: string): Promise<Array<User>> => {
const users = await getUsers()
const normalizedSearch = search.toLowerCase()
return users.filter(
(user) =>
user.name.toLowerCase().includes(normalizedSearch) ||
user.username.toLowerCase().includes(normalizedSearch),
)
}

export const usersQueryOptions = (search: string) =>
queryOptions({
queryKey: ['users', search],
queryFn: async () => {
return await searchUsers(search)
},
})

export interface UsersProps {
search: string
}

export const Users = ({ search }: UsersProps) => {
const users = useQuery(() => usersQueryOptions(search))

return (
<table class="table-auto">
<thead>
<tr class="border text-left">
<th class="p-4">Username</th>
<th class="p-4">Name</th>
<th class="p-4">Email</th>
</tr>
</thead>
<tbody>
{users.data?.map((user) => (
<tr class="border">
<td class="p-4">{user.username}</td>
<td class="p-4">{user.name}</td>
<td class="p-4">{user.email}</td>
</tr>
))}
</tbody>
</table>
)
}
Comment on lines +61 to +84
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Keep the search prop reactive.

Solid components execute once; destructuring props in the parameter list snapshots their initial values and severs reactivity. As a result, useQuery(() => usersQueryOptions(search)) never sees updates when the route search param changes, so the users list keeps filtering with the initial search string. Keep the props object intact (or wrap in an accessor) so the query tracks changes.

-export const Users = ({ search }: UsersProps) => {
-  const users = useQuery(() => usersQueryOptions(search))
+export const Users = (props: UsersProps) => {
+  const users = useQuery(() => usersQueryOptions(props.search))
🤖 Prompt for AI Agents
In examples/solid/search-validator-adapters/src/components/Users.tsx around
lines 61 to 84, the component currently destructures the search prop in the
parameter list which snapshots its initial value and breaks Solid reactivity;
change the function signature to accept the props object (e.g., props:
UsersProps) or define a search accessor that reads from props (e.g., const
search = () => props.search) and update the useQuery call to reference that
reactive accessor (useQuery(() => usersQueryOptions(search())) or useQuery(() =>
usersQueryOptions(() => props.search))). Ensure all usages inside the component
read from the props object/accessor so updates to the route search param trigger
the query and re-render.

39 changes: 39 additions & 0 deletions examples/solid/search-validator-adapters/src/main.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { render } from 'solid-js/web'
import { RouterProvider, createRouter } from '@tanstack/solid-router'
import { QueryClient, QueryClientProvider } from '@tanstack/solid-query'
import { routeTree } from './routeTree.gen'
import './styles.css'

export const queryClient = new QueryClient()

export const router = createRouter({
routeTree,
context: {
queryClient,
},
scrollRestoration: true,
})

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

export function App() {
return <RouterProvider router={router} />
}

const rootElement = document.getElementById('app')!

if (!rootElement.innerHTML) {
render(
() => (
<QueryClientProvider client={queryClient}>
<App />
</QueryClientProvider>
),

rootElement,
)
}
Loading
Loading