Skip to content

docs(solid-query): convert guide docs to ref-based with Solid-style code#10146

Merged
TkDodo merged 9 commits intoTanStack:mainfrom
sukvvon:docs/solid-query-guides
Feb 26, 2026
Merged

docs(solid-query): convert guide docs to ref-based with Solid-style code#10146
TkDodo merged 9 commits intoTanStack:mainfrom
sukvvon:docs/solid-query-guides

Conversation

@sukvvon
Copy link
Contributor

@sukvvon sukvvon commented Feb 18, 2026

🎯 Changes

  • Add markers to 8 React guide source files for Solid marker overrides
  • Convert 25 Solid guide docs from copy-paste to ref-based with replace rules and marker overrides
  • Rewrite all code examples in Solid style (accessor pattern, createSignal, Show/For/Switch, no destructuring)
  • Add Solid offline example entry to docs config

✅ Checklist

  • I have followed the steps in the Contributing guide.
  • I have tested this code locally with pnpm run test:pr.

🚀 Release Impact

  • This change is docs/CI/dev-only (no release).

Summary by CodeRabbit

  • Documentation

    • Extensive updates across React and Solid guides: wording/format tweaks, new anchors/comments, and many new example-driven sections (caching, queries, mutations, cancellations, optimistic updates, pagination, prefetching, suspense, SSR/performance, scroll restoration).
  • New Examples

    • Added a runnable Solid "Offline Queries and Mutations" example (README, dev setup, mock API worker) demonstrating offline-persistent mutations, optimistic updates, pause/resume behavior, and UI fetch/mutation indicators.
  • Chore

    • Examples entry added to the Examples list for Solid.

@changeset-bot
Copy link

changeset-bot bot commented Feb 18, 2026

⚠️ No Changeset found

Latest commit: 77a6218

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@github-actions github-actions bot added the documentation Improvements or additions to documentation label Feb 18, 2026
@nx-cloud
Copy link

nx-cloud bot commented Feb 18, 2026

View your CI Pipeline Execution ↗ for commit 77a6218

Command Status Duration Result
nx run-many --target=build --exclude=examples/*... ✅ Succeeded <1s View ↗
nx affected --targets=test:sherif,test:knip,tes... ✅ Succeeded 40s View ↗

☁️ Nx Cloud last updated this comment at 2026-02-26 07:28:32 UTC

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 18, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ec4bb38 and 77a6218.

📒 Files selected for processing (1)
  • docs/framework/react/guides/caching.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/framework/react/guides/caching.md

📝 Walkthrough

Walkthrough

Adds a new Solid offline example (project files and README) and registers it in docs/config.json; extensively expands Solid framework guides with concrete Solid TSX examples and simplifies prior React→Solid replacement mappings; inserts numerous documentation anchors and minor wording/formatting edits across React guides. No runtime API changes.

Changes

Cohort / File(s) Summary
Docs config
docs/config.json
Added "Offline Queries and Mutations" entry under Solid examples.
React guides — anchors & wording
docs/framework/react/guides/caching.md, docs/framework/react/guides/important-defaults.md, docs/framework/react/guides/mutations.md, docs/framework/react/guides/parallel-queries.md, docs/framework/react/guides/queries.md, docs/framework/react/guides/query-cancellation.md, docs/framework/react/guides/query-options.md, docs/framework/react/guides/request-waterfalls.md
Inserted markdown comment anchors, minor wording/formatting tweaks; documentation-only edits.
Solid guides — examples & replace-map changes
docs/framework/solid/guides/... (background-fetching-indicators, caching, default-query-function, dependent-queries, disabling-queries, infinite-queries, initial-query-data, invalidations-from-mutations, mutations, optimistic-updates, paginated-queries, parallel-queries, placeholder-query-data, prefetching, queries, query-cancellation, query-functions, query-invalidation, query-keys, query-options, request-waterfalls, scroll-restoration, suspense, updates-from-mutation-responses, window-focus-refetching)
Removed or simplified prior React→Solid replacement mappings and added many explicit Solid TSX examples, front-matter mapping tweaks, and narrative/example expansions; documentation-only changes.
New Solid offline example project
examples/solid/offline/.eslintrc.cjs, examples/solid/offline/README.md, examples/solid/offline/index.html, examples/solid/offline/package.json, examples/solid/offline/src/App.tsx, examples/solid/offline/src/api.ts, examples/solid/offline/src/index.tsx, examples/solid/offline/src/movies.ts, examples/solid/offline/tsconfig.json, examples/solid/offline/vite.config.ts
Adds a standalone Solid example demonstrating offline-persistent mutations: App (default export), QueryClient + persister setup, optimistic updates, pause/resume behavior, MSW mock API (exported worker and API helpers), movieKeys/useMovie helpers, build config and README. Example code only.

Sequence Diagram(s)

sequenceDiagram
  participant User as "User"
  participant UI as "App UI (Solid)"
  participant QC as "QueryClient / Persistor"
  participant API as "MSW Worker / API"
  participant LS as "localStorage (persister)"

  User->>UI: submit mutation (update comment)
  UI->>QC: call mutate (optimistic update)
  QC->>API: POST /movies/:id
  API-->>QC: respond (success / error)
  QC->>UI: update mutation state (isPending / isSuccess / isError)
  QC->>LS: persist mutation state / queue
  Note over LS,QC: On reload, QC restores persisted state
  QC->>API: resume paused mutations
  QC->>UI: reflect final query data
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • TkDodo
  • manudeli

Poem

🐰
I hopped in with a tiny cheer,
Packed mutations for offline here,
Paused and persisted, then resumed the race,
Movies synced back to their rightful place —
Hop, fetch, sync — optimistic grace! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly describes the main change: converting Solid guide documentation to use ref-based approach with Solid-style code examples.
Description check ✅ Passed The PR description includes a detailed 🎯 Changes section covering all major updates and a partially completed ✅ Checklist with release impact marked as docs/CI/dev-only.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

@pkg-pr-new
Copy link

pkg-pr-new bot commented Feb 18, 2026

More templates

@tanstack/angular-query-experimental

npm i https://pkg.pr.new/@tanstack/angular-query-experimental@10146

@tanstack/eslint-plugin-query

npm i https://pkg.pr.new/@tanstack/eslint-plugin-query@10146

@tanstack/preact-query

npm i https://pkg.pr.new/@tanstack/preact-query@10146

@tanstack/query-async-storage-persister

npm i https://pkg.pr.new/@tanstack/query-async-storage-persister@10146

@tanstack/query-broadcast-client-experimental

npm i https://pkg.pr.new/@tanstack/query-broadcast-client-experimental@10146

@tanstack/query-core

npm i https://pkg.pr.new/@tanstack/query-core@10146

@tanstack/query-devtools

npm i https://pkg.pr.new/@tanstack/query-devtools@10146

@tanstack/query-persist-client-core

npm i https://pkg.pr.new/@tanstack/query-persist-client-core@10146

@tanstack/query-sync-storage-persister

npm i https://pkg.pr.new/@tanstack/query-sync-storage-persister@10146

@tanstack/react-query

npm i https://pkg.pr.new/@tanstack/react-query@10146

@tanstack/react-query-devtools

npm i https://pkg.pr.new/@tanstack/react-query-devtools@10146

@tanstack/react-query-next-experimental

npm i https://pkg.pr.new/@tanstack/react-query-next-experimental@10146

@tanstack/react-query-persist-client

npm i https://pkg.pr.new/@tanstack/react-query-persist-client@10146

@tanstack/solid-query

npm i https://pkg.pr.new/@tanstack/solid-query@10146

@tanstack/solid-query-devtools

npm i https://pkg.pr.new/@tanstack/solid-query-devtools@10146

@tanstack/solid-query-persist-client

npm i https://pkg.pr.new/@tanstack/solid-query-persist-client@10146

@tanstack/svelte-query

npm i https://pkg.pr.new/@tanstack/svelte-query@10146

@tanstack/svelte-query-devtools

npm i https://pkg.pr.new/@tanstack/svelte-query-devtools@10146

@tanstack/svelte-query-persist-client

npm i https://pkg.pr.new/@tanstack/svelte-query-persist-client@10146

@tanstack/vue-query

npm i https://pkg.pr.new/@tanstack/vue-query@10146

@tanstack/vue-query-devtools

npm i https://pkg.pr.new/@tanstack/vue-query-devtools@10146

commit: 77a6218

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 13

🧹 Nitpick comments (4)
docs/framework/solid/guides/placeholder-query-data.md (1)

22-22: Heading level skip is acceptable in ref-based docs.

The h3 heading appears without a preceding h2 in this file, but since this is a ref-based document (line 4), the parent h2 sections are inherited from the React guide. This is likely intentional structure, though it causes markdownlint to flag a violation when analyzing the file in isolation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/framework/solid/guides/placeholder-query-data.md` at line 22, Add an
explicit inline comment above the "Placeholder Data Memoization" h3 to indicate
the heading-level skip is intentional because this is a ref-based document whose
parent h2 sections are inherited from the React guide; this comment should
reference the h3 ("Placeholder Data Memoization") so reviewers and linters
understand the skip (or alternatively add a markdownlint disable directive at
the top of the file for the specific heading-level rule) to prevent the isolated
markdownlint violation.
docs/framework/solid/guides/disabling-queries.md (1)

23-38: Consider reordering Match conditions for clearer state handling.

The current order checks todosQuery.data before isError and isLoading. If data exists from a previous fetch but a refetch fails, the error state won't be displayed. This might be intentional for stale-while-revalidate UX, but typically error/loading states are checked first.

Suggested reordering for explicit state prioritization
       <Switch>
-        <Match when={todosQuery.data}>
-          <ul>
-            <For each={todosQuery.data}>{(todo) => <li>{todo.title}</li>}</For>
-          </ul>
-        </Match>
-        <Match when={todosQuery.isError}>
-          <span>Error: {todosQuery.error.message}</span>
-        </Match>
         <Match when={todosQuery.isLoading}>
           <span>Loading...</span>
         </Match>
+        <Match when={todosQuery.isError}>
+          <span>Error: {todosQuery.error.message}</span>
+        </Match>
+        <Match when={todosQuery.data}>
+          <ul>
+            <For each={todosQuery.data}>{(todo) => <li>{todo.title}</li>}</For>
+          </ul>
+        </Match>
         <Match when={true}>
           <span>Not ready ...</span>
         </Match>
       </Switch>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/framework/solid/guides/disabling-queries.md` around lines 23 - 38, The
Switch/Match block currently checks todosQuery.data before todosQuery.isError
and todosQuery.isLoading, which can hide error/loading states when stale data
exists; reorder the Match checks in the Switch so that todosQuery.isError and
todosQuery.isLoading are evaluated before todosQuery.data (i.e., check
todosQuery.isError, then todosQuery.isLoading, then todosQuery.data, finally the
fallback Match when={true}) to ensure explicit prioritization of error/loading
states in the rendering logic for the todosQuery component.
examples/solid/offline/src/api.ts (1)

17-42: Consider adding response status checks.

The fetch functions don't verify response.ok before parsing JSON. If the server returns an error status (e.g., 404 from line 59), calling response.json() on non-JSON error bodies could throw or produce unexpected results.

Example fix for fetchMovie
 export const fetchMovie = async (
   id: string,
 ): Promise<{
   ts: number
   movie: { comment: string; id: string; title: string }
 }> => {
   const response = await fetch(`/movies/${id}`)
+  if (!response.ok) {
+    throw new Error(`Failed to fetch movie: ${response.status}`)
+  }
   return await response.json()
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/solid/offline/src/api.ts` around lines 17 - 42, The three API
helpers (fetchMovie, fetchMovies, updateMovie) don't check response.ok before
calling response.json(), so update each function to verify response.ok after
fetch; if not ok, read the response body (preferably text or safe JSON parse),
and throw an Error that includes the HTTP status and returned message/body so
callers can handle failures. Specifically, in fetchMovie, fetchMovies, and
updateMovie add a conditional like "if (!response.ok) { const errBody = await
response.text(); throw new Error(`Fetch <functionName> failed
${response.status}: ${errBody}`) }" (replace <functionName> with the real
function names) before returning response.json().
examples/solid/offline/src/movies.ts (1)

43-45: Type the error handler parameters properly.

Using any types loses type safety. Consider using proper types or at minimum a more descriptive approach.

Proposed fix
-    onError: (_: any, __: any, context: any) => {
+    onError: (_error, _variables, context) => {
       queryClient.setQueryData(movieKeys.detail(movieId), context.previousData)
     },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/solid/offline/src/movies.ts` around lines 43 - 45, The onError
handler currently uses any for all params; change it to use meaningful types
(avoid any) such as (error: unknown, variables: { movieId: string } | undefined,
context: { previousData?: Movie | null } | undefined) => {
queryClient.setQueryData(movieKeys.detail(movieId), context?.previousData) };
import or reference the Movie type used by your fetcher and/or define a small
MutationContext type for context to improve type-safety while keeping the call
to queryClient.setQueryData(movieKeys.detail(movieId), context?.previousData)
intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/framework/react/guides/request-waterfalls.md`:
- Line 187: Summary: Hyphenate compound modifiers like "real world" to
"real-world" for grammar consistency. Fix: update the prose that mentions
"<Comments>" and "<Article>" to use "real-world applications" and any other
compound modifiers (e.g., "child might be nested far below the parent" if used
as a modifier) to be hyphenated; search for occurrences around the discussion of
hoisting the comments query to the parent and at the alternate locations noted
(also applies to lines referencing 368-369) and replace plain compounds with
hyphenated forms to maintain consistent compound-adjective styling.
- Around line 272-275: The fenced code block showing the waterfall steps (the
lines containing "getFeed()" and "getGraphDataById()") is missing a language
tag; update that triple-backtick block to include a language specifier (e.g.,
"text") so Markdownlint stops warning and the snippet is properly annotated.

In `@docs/framework/solid/guides/mutations.md`:
- Line 275: The heading "Persisting Offline mutations" is an h3 (###) that skips
from the document's h1; change it to h2 (## Persisting Offline mutations) so
heading levels increment correctly per markdownlint MD001 and maintain document
structure; update the heading token for "Persisting Offline mutations" to use
two hashes and ensure nearby headings remain properly nested.
- Around line 173-190: The onSuccess callbacks in the Example7 snippet use an
incorrect four-parameter signature (data, variables, onMutateResult, context);
update both the global useMutation onSuccess and the per-call mutate onSuccess
to the correct signature used by this library (e.g., onSuccess: (data,
variables, context) => { ... }) and adjust any references to the removed
parameter (onMutateResult) accordingly; specifically edit the useMutation block
and the mutate(...) call that reference addTodo, useMutation, and mutate to use
(data, variables, context) so the docs match the real API.
- Around line 143-168: The example callbacks for useMutation and mutate use an
incorrect fourth parameter name "onMutateResult"; update all
onSuccess/onError/onSettled callback signatures in the useMutation options and
in the mutate call to match the correct mutation callback shape (three params):
onSuccess(data, variables, context), onError(error, variables, context),
onSettled(dataOrUndefined, errorOrUndefined, variables, context) — or if your
API expects three params for settled, use (data, error, context); replace any
"onMutateResult" occurrences with "context" and remove the extra parameter so
the callbacks in useMutation and mutate reference the correct symbols and
ordering.
- Around line 221-255: The callbacks provided to queryClient.setMutationDefaults
use the wrong signatures—onMutate currently expects (variables, context) and
returns a context named onMutateResult, while onSuccess/onError are declared
with an extra parameter; fix by making onMutate async (variables) => { await
queryClient.cancelQueries(['todos']); const optimisticTodo = { id: uuid(),
title: variables.title }; queryClient.setQueryData(['todos'], old => [...old,
optimisticTodo]); return { optimisticTodo } } and update onSuccess and onError
to use the proper signature (data, variables, context) => {
queryClient.setQueryData(['todos'], old => old.map/todo filtering using
context.optimisticTodo.id) } so they read context.optimisticTodo (replace uses
of onMutateResult and context.client with the returned context and the outer
queryClient respectively).

In `@docs/framework/solid/guides/prefetching.md`:
- Around line 216-253: The example uses the old constructor-style API; update it
to the v1 functional API by replacing RouterContext/createRootRoute/new Route
with createRootRoute (or createRootRouteWithContext<MyContext>() for typed
context), createRoute for individual routes, build the tree with
rootRoute.addChildren([ ...childRoutes... ]), and instantiate the router via
createRouter({ routeTree: rootRoute }); also update any usages of RouterContext
methods to the new route context hooks (e.g., useRouteContext) and ensure
loader/beforeLoad signatures match the v1 createRoute API.

In `@docs/framework/solid/guides/queries.md`:
- Around line 7-95: The MD042 warnings are caused by the inline marker comments
like [//]: # 'SubscribeDescription' and [//]: # 'Example' in the markdown; wrap
the marker blocks with markdownlint disable/enable comments (e.g. insert <!--
markdownlint-disable MD042 --> before the sequence of [//]: # '...' markers and
<!-- markdownlint-enable MD042 --> after) or convert those marker lines to a
neutral HTML comment format so MD042 is suppressed for the markers (update the
marker blocks surrounding useQuery examples and the
SubscribeDescription/Example/Example2/Example3/Example4 markers accordingly).

In `@docs/framework/solid/guides/query-cancellation.md`:
- Around line 111-120: The queryFn passed to useQuery (in the todosQuery
example) does not return the Promise from client.request so the query resolves
to undefined; update the queryFn to return the client.request call (i.e., ensure
the function passed as queryFn returns the result of client.request({ document:
query, signal })) so useQuery receives the fetched data—look for GraphQLClient,
client.request, useQuery, queryFn and todosQuery in the snippet and add the
missing return.

In `@examples/solid/offline/README.md`:
- Around line 5-6: The README references running `npm run start` but
package.json only defines the `dev` script, causing the command to fail; update
the README.md instructions to use `npm run dev` (or add a `start` script to
package.json that maps to `dev`) so the command matches the actual script
name—look for the script entries in package.json and the command lines in
README.md and make them consistent.

In `@examples/solid/offline/src/App.tsx`:
- Around line 146-153: submitForm can pass an undefined comment into
updateMovie.mutate which triggers a TypeError in api.ts when
comment.toUpperCase() is called; change submitForm to ensure a defined string is
sent (e.g., compute const safeComment = comment() ??
movieQuery.data?.movie.comment ?? "" before calling updateMovie.mutate) or
validate and coerce comment() to a string and pass that into updateMovie.mutate
so api.ts always receives a string value; update the submitForm function (and
any hook return usage of comment()) to guarantee non-undefined input to
updateMovie.mutate.

In `@examples/solid/offline/src/index.tsx`:
- Around line 6-14: worker.start() is asynchronous and currently races with
initial renders; await the worker startup before calling render so MSW is ready
to intercept requests — change the flow so you await the Promise returned by
worker.start() (referencing worker.start()) and only then call render(...) to
mount <App /> (referencing render and App) to eliminate the race condition.

In `@examples/solid/offline/src/movies.ts`:
- Around line 22-42: In updateMovie's onMutate handler capture the current
comment value into a local variable before calling setComment(undefined) so the
optimistic update uses the preserved value; specifically, inside the onMutate
for updateMovie (which uses movieKeys.detail(movieId)) read comment() into e.g.
previousComment, then call setComment(undefined), and pass previousComment into
queryClient.setQueryData(...) when setting movie.comment and return
previousComment in the mutation context so rollback can restore it if needed.

---

Duplicate comments:
In `@docs/framework/solid/guides/optimistic-updates.md`:
- Around line 101-129: The mutation callbacks use a `context.client` API that
may not match Solid Query's actual context shape; verify and update
useMutation's callbacks (onMutate, onError, onSettled) to use the correct
context object and method names (e.g., confirm whether cancelQueries,
getQueryData, setQueryData, invalidateQueries are accessed via context.client or
directly via context or a queryClient provided by useMutation), then adjust
references to newTodo/onMutateResult accordingly so onMutate returns the proper
snapshot shape and onError/onSettled read that shape correctly (ensure functions
like cancelQueries(['todos', newTodo.id]) and setQueryData/getQueryData use the
actual API signature and key format used by Solid Query).

---

Nitpick comments:
In `@docs/framework/solid/guides/disabling-queries.md`:
- Around line 23-38: The Switch/Match block currently checks todosQuery.data
before todosQuery.isError and todosQuery.isLoading, which can hide error/loading
states when stale data exists; reorder the Match checks in the Switch so that
todosQuery.isError and todosQuery.isLoading are evaluated before todosQuery.data
(i.e., check todosQuery.isError, then todosQuery.isLoading, then
todosQuery.data, finally the fallback Match when={true}) to ensure explicit
prioritization of error/loading states in the rendering logic for the todosQuery
component.

In `@docs/framework/solid/guides/placeholder-query-data.md`:
- Line 22: Add an explicit inline comment above the "Placeholder Data
Memoization" h3 to indicate the heading-level skip is intentional because this
is a ref-based document whose parent h2 sections are inherited from the React
guide; this comment should reference the h3 ("Placeholder Data Memoization") so
reviewers and linters understand the skip (or alternatively add a markdownlint
disable directive at the top of the file for the specific heading-level rule) to
prevent the isolated markdownlint violation.

In `@examples/solid/offline/src/api.ts`:
- Around line 17-42: The three API helpers (fetchMovie, fetchMovies,
updateMovie) don't check response.ok before calling response.json(), so update
each function to verify response.ok after fetch; if not ok, read the response
body (preferably text or safe JSON parse), and throw an Error that includes the
HTTP status and returned message/body so callers can handle failures.
Specifically, in fetchMovie, fetchMovies, and updateMovie add a conditional like
"if (!response.ok) { const errBody = await response.text(); throw new
Error(`Fetch <functionName> failed ${response.status}: ${errBody}`) }" (replace
<functionName> with the real function names) before returning response.json().

In `@examples/solid/offline/src/movies.ts`:
- Around line 43-45: The onError handler currently uses any for all params;
change it to use meaningful types (avoid any) such as (error: unknown,
variables: { movieId: string } | undefined, context: { previousData?: Movie |
null } | undefined) => { queryClient.setQueryData(movieKeys.detail(movieId),
context?.previousData) }; import or reference the Movie type used by your
fetcher and/or define a small MutationContext type for context to improve
type-safety while keeping the call to
queryClient.setQueryData(movieKeys.detail(movieId), context?.previousData)
intact.


[//]: # 'NestedExample'

Note that while `<Comments>` takes a prop `id` from the parent, that id is already available when the `<Article>` renders so there is no reason we could not fetch the comments at the same time as the article. In real world applications, the child might be nested far below the parent and these kinds of waterfalls are often trickier to spot and fix, but for our example, one way to flatten the waterfall would be to hoist the comments query to the parent instead:
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 | 🟡 Minor

Hyphenate compound modifiers for grammar consistency.

These read as compound adjectives and should be hyphenated.

✍️ Suggested edits
- In real world applications, the child might be nested far below the parent
+ In real-world applications, the child might be nested far below the parent

- - Include all data fetching code in the main bundle, even if we seldom use it
- - Put the data fetching code in the code split bundle, but with a request waterfall
+ - Include all data-fetching code in the main bundle, even if we seldom use it
+ - Put the data-fetching code in the code-split bundle, but with a request-waterfall

Also applies to: 368-369

🧰 Tools
🪛 LanguageTool

[grammar] ~187-~187: Use a hyphen to join words.
Context: ...at the same time as the article. In real world applications, the child might be n...

(QB_NEW_EN_HYPHEN)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/framework/react/guides/request-waterfalls.md` at line 187, Summary:
Hyphenate compound modifiers like "real world" to "real-world" for grammar
consistency. Fix: update the prose that mentions "<Comments>" and "<Article>" to
use "real-world applications" and any other compound modifiers (e.g., "child
might be nested far below the parent" if used as a modifier) to be hyphenated;
search for occurrences around the discussion of hoisting the comments query to
the parent and at the alternate locations noted (also applies to lines
referencing 368-369) and replace plain compounds with hyphenated forms to
maintain consistent compound-adjective styling.

Comment on lines 272 to 275
```
1. |> getFeed()
2. |> getGraphDataById()
```
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 | 🟡 Minor

Add a language to the fenced code block.

Markdownlint warns about missing language specifier here. Consider marking it as plain text.

💡 Suggested change
-```
+```text
 1. |> getFeed()
 2.   |> getGraphDataById()
</details>

<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion

🧰 Tools
🪛 markdownlint-cli2 (0.21.0)

[warning] 272-272: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/framework/react/guides/request-waterfalls.md` around lines 272 - 275,
The fenced code block showing the waterfall steps (the lines containing
"getFeed()" and "getGraphDataById()") is missing a language tag; update that
triple-backtick block to include a language specifier (e.g., "text") so
Markdownlint stops warning and the snippet is properly annotated.

Comment on lines +143 to +168
```tsx
useMutation(() => ({
mutationFn: addTodo,
onSuccess: (data, variables, onMutateResult, context) => {
// I will fire first
},
onError: (error, variables, onMutateResult, context) => {
// I will fire first
},
onSettled: (data, error, variables, onMutateResult, context) => {
// I will fire first
},
}))

mutate(todo, {
onSuccess: (data, variables, onMutateResult, context) => {
// I will fire second!
},
onError: (error, variables, onMutateResult, context) => {
// I will fire second!
},
onSettled: (data, error, variables, onMutateResult, context) => {
// I will fire second!
},
})
```
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

Same callback signature issue in Example6.

The callbacks here also use the incorrect onMutateResult parameter pattern.

Proposed fix
 useMutation(() => ({
   mutationFn: addTodo,
-  onSuccess: (data, variables, onMutateResult, context) => {
+  onSuccess: (data, variables, context) => {
     // I will fire first
   },
-  onError: (error, variables, onMutateResult, context) => {
+  onError: (error, variables, context) => {
     // I will fire first
   },
-  onSettled: (data, error, variables, onMutateResult, context) => {
+  onSettled: (data, error, variables, context) => {
     // I will fire first
   },
 }))

 mutate(todo, {
-  onSuccess: (data, variables, onMutateResult, context) => {
+  onSuccess: (data, variables, context) => {
     // I will fire second!
   },
-  onError: (error, variables, onMutateResult, context) => {
+  onError: (error, variables, context) => {
     // I will fire second!
   },
-  onSettled: (data, error, variables, onMutateResult, context) => {
+  onSettled: (data, error, variables, context) => {
     // I will fire second!
   },
 })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/framework/solid/guides/mutations.md` around lines 143 - 168, The example
callbacks for useMutation and mutate use an incorrect fourth parameter name
"onMutateResult"; update all onSuccess/onError/onSettled callback signatures in
the useMutation options and in the mutate call to match the correct mutation
callback shape (three params): onSuccess(data, variables, context),
onError(error, variables, context), onSettled(dataOrUndefined, errorOrUndefined,
variables, context) — or if your API expects three params for settled, use
(data, error, context); replace any "onMutateResult" occurrences with "context"
and remove the extra parameter so the callbacks in useMutation and mutate
reference the correct symbols and ordering.

Comment on lines +173 to +190
```tsx
useMutation(() => ({
mutationFn: addTodo,
onSuccess: (data, variables, onMutateResult, context) => {
// Will be called 3 times
},
}))

const todos = ['Todo 1', 'Todo 2', 'Todo 3']
todos.forEach((todo) => {
mutate(todo, {
onSuccess: (data, variables, onMutateResult, context) => {
// Will execute only once, for the last mutation (Todo 3),
// regardless which mutation resolves first
},
})
})
```
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

Same callback signature issue in Example7.

Proposed fix
 useMutation(() => ({
   mutationFn: addTodo,
-  onSuccess: (data, variables, onMutateResult, context) => {
+  onSuccess: (data, variables, context) => {
     // Will be called 3 times
   },
 }))

 const todos = ['Todo 1', 'Todo 2', 'Todo 3']
 todos.forEach((todo) => {
   mutate(todo, {
-    onSuccess: (data, variables, onMutateResult, context) => {
+    onSuccess: (data, variables, context) => {
       // Will execute only once, for the last mutation (Todo 3),
       // regardless which mutation resolves first
     },
   })
 })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/framework/solid/guides/mutations.md` around lines 173 - 190, The
onSuccess callbacks in the Example7 snippet use an incorrect four-parameter
signature (data, variables, onMutateResult, context); update both the global
useMutation onSuccess and the per-call mutate onSuccess to the correct signature
used by this library (e.g., onSuccess: (data, variables, context) => { ... })
and adjust any references to the removed parameter (onMutateResult) accordingly;
specifically edit the useMutation block and the mutate(...) call that reference
addTodo, useMutation, and mutate to use (data, variables, context) so the docs
match the real API.

Comment on lines +5 to +6
- `npm install`
- `npm run start`
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 | 🟡 Minor

Incorrect script name in instructions.

The README references npm run start, but the package.json defines a dev script instead. This will cause the command to fail.

📝 Proposed fix
 - `npm install`
-- `npm run start`
+- `npm run dev`
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- `npm install`
- `npm run start`
- `npm install`
- `npm run dev`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/solid/offline/README.md` around lines 5 - 6, The README references
running `npm run start` but package.json only defines the `dev` script, causing
the command to fail; update the README.md instructions to use `npm run dev` (or
add a `start` script to package.json that maps to `dev`) so the command matches
the actual script name—look for the script entries in package.json and the
command lines in README.md and make them consistent.

Comment on lines +146 to +153
function submitForm(event: SubmitEvent) {
event.preventDefault()

updateMovie.mutate({
id: props.movieId,
comment: comment(),
})
}
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

Potential undefined comment when submitting.

comment() could return undefined if no local edits were made (it falls back to movieQuery.data?.movie.comment in the hook return). When mutating, if comment is undefined, the API call in api.ts line 75 will call comment.toUpperCase() which throws a TypeError.

Proposed fix
   function submitForm(event: SubmitEvent) {
     event.preventDefault()

+    const currentComment = comment()
+    if (currentComment === undefined) return
+
     updateMovie.mutate({
       id: props.movieId,
-      comment: comment(),
+      comment: currentComment,
     })
   }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/solid/offline/src/App.tsx` around lines 146 - 153, submitForm can
pass an undefined comment into updateMovie.mutate which triggers a TypeError in
api.ts when comment.toUpperCase() is called; change submitForm to ensure a
defined string is sent (e.g., compute const safeComment = comment() ??
movieQuery.data?.movie.comment ?? "" before calling updateMovie.mutate) or
validate and coerce comment() to a string and pass that into updateMovie.mutate
so api.ts always receives a string value; update the submitForm function (and
any hook return usage of comment()) to guarantee non-undefined input to
updateMovie.mutate.

Comment on lines +6 to +14
worker.start()

render(
() => (
<div style={{ padding: '16px' }}>
<App />
</div>
),
document.getElementById('root')!,
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

Race condition: worker.start() should be awaited.

worker.start() returns a Promise. Rendering the app immediately without waiting could cause initial fetch requests to bypass MSW interception if they fire before the worker is ready.

Proposed fix
-worker.start()
-
-render(
-  () => (
-    <div style={{ padding: '16px' }}>
-      <App />
-    </div>
-  ),
-  document.getElementById('root')!,
-)
+worker.start().then(() => {
+  render(
+    () => (
+      <div style={{ padding: '16px' }}>
+        <App />
+      </div>
+    ),
+    document.getElementById('root')!,
+  )
+})
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
worker.start()
render(
() => (
<div style={{ padding: '16px' }}>
<App />
</div>
),
document.getElementById('root')!,
worker.start().then(() => {
render(
() => (
<div style={{ padding: '16px' }}>
<App />
</div>
),
document.getElementById('root')!,
)
})
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/solid/offline/src/index.tsx` around lines 6 - 14, worker.start() is
asynchronous and currently races with initial renders; await the worker startup
before calling render so MSW is ready to intercept requests — change the flow so
you await the Promise returned by worker.start() (referencing worker.start())
and only then call render(...) to mount <App /> (referencing render and App) to
eliminate the race condition.

Comment on lines +22 to +42
const updateMovie = useMutation(() => ({
mutationKey: movieKeys.detail(movieId),
onMutate: async () => {
await queryClient.cancelQueries({ queryKey: movieKeys.detail(movieId) })
const previousData = queryClient.getQueryData<
Awaited<ReturnType<typeof api.fetchMovie>>
>(movieKeys.detail(movieId))

// remove local state so that server state is taken instead
setComment(undefined)

queryClient.setQueryData(movieKeys.detail(movieId), {
...previousData,
movie: {
...previousData?.movie,
comment: comment(),
},
})

return { previousData }
},
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

Bug: comment() returns undefined after clearing the signal.

On line 31, setComment(undefined) clears the local comment state. Then on line 37, comment() is called to set the optimistic cache value, but it will return undefined since the signal was just cleared. The previous comment value should be captured before clearing.

Proposed fix
     onMutate: async () => {
       await queryClient.cancelQueries({ queryKey: movieKeys.detail(movieId) })
       const previousData = queryClient.getQueryData<
         Awaited<ReturnType<typeof api.fetchMovie>>
       >(movieKeys.detail(movieId))

+      // Capture the current comment before clearing
+      const newComment = comment()
+
       // remove local state so that server state is taken instead
       setComment(undefined)

       queryClient.setQueryData(movieKeys.detail(movieId), {
         ...previousData,
         movie: {
           ...previousData?.movie,
-          comment: comment(),
+          comment: newComment,
         },
       })

       return { previousData }
     },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const updateMovie = useMutation(() => ({
mutationKey: movieKeys.detail(movieId),
onMutate: async () => {
await queryClient.cancelQueries({ queryKey: movieKeys.detail(movieId) })
const previousData = queryClient.getQueryData<
Awaited<ReturnType<typeof api.fetchMovie>>
>(movieKeys.detail(movieId))
// remove local state so that server state is taken instead
setComment(undefined)
queryClient.setQueryData(movieKeys.detail(movieId), {
...previousData,
movie: {
...previousData?.movie,
comment: comment(),
},
})
return { previousData }
},
const updateMovie = useMutation(() => ({
mutationKey: movieKeys.detail(movieId),
onMutate: async () => {
await queryClient.cancelQueries({ queryKey: movieKeys.detail(movieId) })
const previousData = queryClient.getQueryData<
Awaited<ReturnType<typeof api.fetchMovie>>
>(movieKeys.detail(movieId))
// Capture the current comment before clearing
const newComment = comment()
// remove local state so that server state is taken instead
setComment(undefined)
queryClient.setQueryData(movieKeys.detail(movieId), {
...previousData,
movie: {
...previousData?.movie,
comment: newComment,
},
})
return { previousData }
},
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/solid/offline/src/movies.ts` around lines 22 - 42, In updateMovie's
onMutate handler capture the current comment value into a local variable before
calling setComment(undefined) so the optimistic update uses the preserved value;
specifically, inside the onMutate for updateMovie (which uses
movieKeys.detail(movieId)) read comment() into e.g. previousComment, then call
setComment(undefined), and pass previousComment into
queryClient.setQueryData(...) when setting movie.comment and return
previousComment in the mutation context so rollback can restore it if needed.

@sukvvon sukvvon force-pushed the docs/solid-query-guides branch from 35ca6d1 to cd3b683 Compare February 26, 2026 07:06
@sukvvon sukvvon requested a review from TkDodo February 26, 2026 07:13
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (7)
docs/framework/solid/guides/query-cancellation.md (1)

111-120: ⚠️ Potential issue | 🔴 Critical

Missing return statement in queryFn.

The queryFn doesn't return the promise from client.request(), so the query will resolve with undefined instead of the actual data.

🐛 Proposed fix
 const todosQuery = useQuery(() => ({
   queryKey: ['todos'],
   queryFn: ({ signal }) => {
-    client.request({ document: query, signal })
+    return client.request({ document: query, signal })
   },
 }))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/framework/solid/guides/query-cancellation.md` around lines 111 - 120,
The queryFn passed to useQuery (in todosQuery) forgets to return the promise
from client.request, so the query resolves to undefined; update the queryFn to
return client.request({ document: query, signal }) (i.e., ensure the function
body of queryFn returns the client.request promise) so useQuery receives the
fetched data from GraphQLClient.request.
docs/framework/solid/guides/mutations.md (5)

275-275: ⚠️ Potential issue | 🟡 Minor

Heading level skips from h1 to h3.

Per markdownlint MD001, heading levels should increment by one. This ### (h3) heading appears without a preceding ## (h2) heading.

📝 Proposed fix
-### Persisting Offline mutations
+## Persisting Offline mutations
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/framework/solid/guides/mutations.md` at line 275, The heading
"Persisting Offline mutations" is written as an h3 (###) but there is no
preceding h2; change the header to an h2 (## Persisting Offline mutations) or
insert an appropriate h2 above it so heading levels increment correctly per
MD001; update the header token for the "Persisting Offline mutations" line in
docs/framework/solid/guides/mutations.md (look for the exact heading text) to
restore proper heading hierarchy.

106-122: ⚠️ Potential issue | 🟠 Major

Incorrect callback signatures with extra onMutateResult parameter.

The onError, onSuccess, and onSettled callbacks use an incorrect four-parameter signature. The correct signatures are:

  • onError: (error, variables, context) => {}
  • onSuccess: (data, variables, context) => {}
  • onSettled: (data, error, variables, context) => {}

The context parameter already contains the result returned from onMutate, so there's no separate onMutateResult parameter.

🔧 Proposed fix
 useMutation(() => ({
   mutationFn: addTodo,
-  onMutate: (variables, context) => {
+  onMutate: (variables) => {
     // A mutation is about to happen!
 
     // Optionally return a result containing data to use when for example rolling back
     return { id: 1 }
   },
-  onError: (error, variables, onMutateResult, context) => {
+  onError: (error, variables, context) => {
     // An error happened!
-    console.log(`rolling back optimistic update with id ${onMutateResult.id}`)
+    console.log(`rolling back optimistic update with id ${context.id}`)
   },
-  onSuccess: (data, variables, onMutateResult, context) => {
+  onSuccess: (data, variables, context) => {
     // Boom baby!
   },
-  onSettled: (data, error, variables, onMutateResult, context) => {
+  onSettled: (data, error, variables, context) => {
     // Error or success... doesn't matter!
   },
 }))
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/framework/solid/guides/mutations.md` around lines 106 - 122, The
callback signatures in the mutation example are incorrect: update the handlers
so they match the correct Solid Query signatures — keep onMutate: (variables,
context) => { return { id: 1 } }, change onError to onError: (error, variables,
context) => { console.log(`rolling back optimistic update with id
${context?.id}`) }, change onSuccess to onSuccess: (data, variables, context) =>
{ /* ... */ }, and change onSettled to onSettled: (data, error, variables,
context) => { /* ... */ }; remove the extra onMutateResult parameter and read
the onMutate result from context in onError/onSuccess/onSettled.

173-190: ⚠️ Potential issue | 🟠 Major

Same callback signature issue in Example7.

🔧 Proposed fix
 useMutation(() => ({
   mutationFn: addTodo,
-  onSuccess: (data, variables, onMutateResult, context) => {
+  onSuccess: (data, variables, context) => {
     // Will be called 3 times
   },
 }))
 
 const todos = ['Todo 1', 'Todo 2', 'Todo 3']
 todos.forEach((todo) => {
   mutate(todo, {
-    onSuccess: (data, variables, onMutateResult, context) => {
+    onSuccess: (data, variables, context) => {
       // Will execute only once, for the last mutation (Todo 3),
       // regardless which mutation resolves first
     },
   })
 })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/framework/solid/guides/mutations.md` around lines 173 - 190, Example7
uses the wrong onSuccess callback signature (it lists four args: data,
variables, onMutateResult, context); update both the useMutation onSuccess and
the per-call mutate onSuccess to the correct signature used by the library
(data, variables, context) so the parameters match the actual implementation for
useMutation, mutationFn, onSuccess and mutate.

143-168: ⚠️ Potential issue | 🟠 Major

Same callback signature issue in Example6.

The callbacks here also use the incorrect onMutateResult parameter pattern.

🔧 Proposed fix
 useMutation(() => ({
   mutationFn: addTodo,
-  onSuccess: (data, variables, onMutateResult, context) => {
+  onSuccess: (data, variables, context) => {
     // I will fire first
   },
-  onError: (error, variables, onMutateResult, context) => {
+  onError: (error, variables, context) => {
     // I will fire first
   },
-  onSettled: (data, error, variables, onMutateResult, context) => {
+  onSettled: (data, error, variables, context) => {
     // I will fire first
   },
 }))
 
 mutate(todo, {
-  onSuccess: (data, variables, onMutateResult, context) => {
+  onSuccess: (data, variables, context) => {
     // I will fire second!
   },
-  onError: (error, variables, onMutateResult, context) => {
+  onError: (error, variables, context) => {
     // I will fire second!
   },
-  onSettled: (data, error, variables, onMutateResult, context) => {
+  onSettled: (data, error, variables, context) => {
     // I will fire second!
   },
 })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/framework/solid/guides/mutations.md` around lines 143 - 168, The
callback signatures in the useMutation and mutate examples are wrong — they
include a spurious onMutateResult parameter; update the handlers for
useMutation(() => ({ mutationFn: addTodo, onSuccess, onError, onSettled })) and
the mutate(todo, { onSuccess, onError, onSettled }) call to use the correct
parameter ordering: onSuccess should be (data, variables, context), onError
should be (error, variables, context), and onSettled should be (data, error,
variables, context), removing any onMutateResult parameter from all callback
definitions.

221-255: ⚠️ Potential issue | 🟠 Major

Incorrect callback signatures and context usage in Example10.

The onMutate callback has an incorrect signature (shouldn't accept context parameter), and the other callbacks use the wrong parameter names. Additionally, context.client should be replaced with the outer queryClient reference.

🔧 Proposed fix
 const queryClient = new QueryClient()
 
 // Define the "addTodo" mutation
 queryClient.setMutationDefaults(['addTodo'], {
   mutationFn: addTodo,
-  onMutate: async (variables, context) => {
+  onMutate: async (variables) => {
     // Cancel current queries for the todos list
-    await context.client.cancelQueries({ queryKey: ['todos'] })
+    await queryClient.cancelQueries({ queryKey: ['todos'] })
 
     // Create optimistic todo
     const optimisticTodo = { id: uuid(), title: variables.title }
 
     // Add optimistic todo to todos list
-    context.client.setQueryData(['todos'], (old) => [...old, optimisticTodo])
+    queryClient.setQueryData(['todos'], (old) => [...old, optimisticTodo])
 
     // Return a result with the optimistic todo
     return { optimisticTodo }
   },
-  onSuccess: (result, variables, onMutateResult, context) => {
+  onSuccess: (result, variables, context) => {
     // Replace optimistic todo in the todos list with the result
-    context.client.setQueryData(['todos'], (old) =>
+    queryClient.setQueryData(['todos'], (old) =>
       old.map((todo) =>
-        todo.id === onMutateResult.optimisticTodo.id ? result : todo,
+        todo.id === context.optimisticTodo.id ? result : todo,
       ),
     )
   },
-  onError: (error, variables, onMutateResult, context) => {
+  onError: (error, variables, context) => {
     // Remove optimistic todo from the todos list
-    context.client.setQueryData(['todos'], (old) =>
-      old.filter((todo) => todo.id !== onMutateResult.optimisticTodo.id),
+    queryClient.setQueryData(['todos'], (old) =>
+      old.filter((todo) => todo.id !== context.optimisticTodo.id),
     )
   },
   retry: 3,
 })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/framework/solid/guides/mutations.md` around lines 221 - 255, The example
uses incorrect callback signatures and wrongly references context.client; fix
setMutationDefaults on the QueryClient so onMutate has the signature onMutate =
async (variables) => { await queryClient.cancelQueries({ queryKey: ['todos'] });
const optimisticTodo = { id: uuid(), title: variables.title };
queryClient.setQueryData(['todos'], (old) => [...old, optimisticTodo]); return {
optimisticTodo }; } and update onSuccess and onError to use the correct
parameters (onSuccess = (result, variables, context) => {
queryClient.setQueryData(['todos'], (old) => old.map((todo) => todo.id ===
context.optimisticTodo.id ? result : todo)); } and onError = (error, variables,
context) => { queryClient.setQueryData(['todos'], (old) => old.filter((todo) =>
todo.id !== context.optimisticTodo.id)); }); also keep mutationFn: addTodo and
retry: 3 as before.
docs/framework/solid/guides/prefetching.md (1)

216-253: ⚠️ Potential issue | 🟠 Major

Outdated TanStack Router API - uses constructor-style instead of v1 functional API.

The example uses deprecated patterns (new RouterContext(), routerContext.createRootRoute(), new Route()). TanStack Router v1 for Solid uses functional APIs:

  • Use createRootRoute() directly (or createRootRouteWithContext<MyContext>() for typed context)
  • Use createRoute() for child routes
  • Build the tree with rootRoute.addChildren([...])
  • Create router with createRouter({ routeTree })
TanStack Router v1 Solid createRoute createRootRoute API syntax
🔧 Proposed fix
-const queryClient = new QueryClient()
-const routerContext = new RouterContext()
-const rootRoute = routerContext.createRootRoute({
-  component: () => { ... }
-})
-
-const articleRoute = new Route({
-  getParentRoute: () => rootRoute,
-  path: 'article',
+const rootRoute = createRootRoute({
+  component: () => <Outlet />,
+})
+
+const articleRoute = createRoute({
+  getParentRoute: () => rootRoute,
+  path: '/article',
   beforeLoad: () => {
     return {
       articleQueryOptions: { queryKey: ['article'], queryFn: fetchArticle },
       commentsQueryOptions: { queryKey: ['comments'], queryFn: fetchComments },
     }
   },
   loader: async ({
     context: { queryClient },
     routeContext: { articleQueryOptions, commentsQueryOptions },
   }) => {
     // Fetch comments asap, but don't block
     queryClient.prefetchQuery(commentsQueryOptions)
 
     // Don't render the route at all until article has been fetched
     await queryClient.prefetchQuery(articleQueryOptions)
   },
   component: ({ useRouteContext }) => {
     const { articleQueryOptions, commentsQueryOptions } = useRouteContext()
     const articleQuery = useQuery(() => articleQueryOptions)
     const commentsQuery = useQuery(() => commentsQueryOptions)
 
     return (
       ...
     )
   },
   errorComponent: () => 'Oh crap!',
 })
+
+const routeTree = rootRoute.addChildren([articleRoute])
+const router = createRouter({ routeTree })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/framework/solid/guides/prefetching.md` around lines 216 - 253, Update
the example to use TanStack Router v1 functional APIs instead of the deprecated
constructors: replace RouterContext/new RouterContext and
routerContext.createRootRoute() with createRootRoute() or
createRootRouteWithContext<...>(), replace new Route({...}) with
createRoute({...}), assemble children via rootRoute.addChildren([...]) and
create the router with createRouter({ routeTree: rootRoute }) (or the v1
equivalent), while keeping the existing query-related symbols
(queryClient.prefetchQuery, beforeLoad, loader, useRouteContext,
articleQueryOptions, commentsQueryOptions) intact so the prefetching logic and
component useQuery calls work the same under the new API.
🧹 Nitpick comments (1)
docs/framework/solid/guides/optimistic-updates.md (1)

67-67: Remove or use the queryClient variable.

The queryClient variable is declared but never used in the example. The mutation callbacks access the client via context.client instead. Consider removing this line or clarifying its purpose if it's meant to demonstrate an alternative pattern.

🧹 Proposed cleanup
-const queryClient = useQueryClient()
-
 useMutation(() => ({
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/framework/solid/guides/optimistic-updates.md` at line 67, Remove the
unused local variable by deleting the line that assigns queryClient via
useQueryClient(), or if you intended to show the alternate pattern, update the
mutation callbacks to use the local queryClient instead of context.client and
add a brief comment explaining the alternative; specifically address the const
queryClient = useQueryClient() declaration and either remove it or replace
usages of context.client inside the mutation callback functions with queryClient
so the example is consistent.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/framework/react/guides/important-defaults.md`:
- Line 41: Update the documentation text to hyphenate the compound modifier;
replace the phrase "non-JSON compatible values" with "non-JSON-compatible
values" in the paragraph that references structural sharing and the
config.structuralSharing option so the wording reads correctly (retain
surrounding context about providing a custom function for
config.structuralSharing).

In `@docs/framework/solid/guides/dependent-queries.md`:
- Line 23: The current enabled flag uses a truthy coercion (!!userId()) which
will disable the query when userId() returns 0; change the guard to a nullish
check so numeric IDs like 0 are allowed — e.g., replace the coercion with a
nullish comparison such as userId() != null (or userId() !== null && userId()
!== undefined) for the enabled property that references userId(). Ensure you
update the enabled assignment where it's defined to use this nullish check.

In `@docs/framework/solid/guides/placeholder-query-data.md`:
- Line 22: The heading "Placeholder Data Memoization" is at h3 (###) but should
be incremented from an h2; change the heading token from "### Placeholder Data
Memoization" to "## Placeholder Data Memoization" so the document uses
sequential heading levels and complies with Markdown conventions.

---

Duplicate comments:
In `@docs/framework/solid/guides/mutations.md`:
- Line 275: The heading "Persisting Offline mutations" is written as an h3 (###)
but there is no preceding h2; change the header to an h2 (## Persisting Offline
mutations) or insert an appropriate h2 above it so heading levels increment
correctly per MD001; update the header token for the "Persisting Offline
mutations" line in docs/framework/solid/guides/mutations.md (look for the exact
heading text) to restore proper heading hierarchy.
- Around line 106-122: The callback signatures in the mutation example are
incorrect: update the handlers so they match the correct Solid Query signatures
— keep onMutate: (variables, context) => { return { id: 1 } }, change onError to
onError: (error, variables, context) => { console.log(`rolling back optimistic
update with id ${context?.id}`) }, change onSuccess to onSuccess: (data,
variables, context) => { /* ... */ }, and change onSettled to onSettled: (data,
error, variables, context) => { /* ... */ }; remove the extra onMutateResult
parameter and read the onMutate result from context in
onError/onSuccess/onSettled.
- Around line 173-190: Example7 uses the wrong onSuccess callback signature (it
lists four args: data, variables, onMutateResult, context); update both the
useMutation onSuccess and the per-call mutate onSuccess to the correct signature
used by the library (data, variables, context) so the parameters match the
actual implementation for useMutation, mutationFn, onSuccess and mutate.
- Around line 143-168: The callback signatures in the useMutation and mutate
examples are wrong — they include a spurious onMutateResult parameter; update
the handlers for useMutation(() => ({ mutationFn: addTodo, onSuccess, onError,
onSettled })) and the mutate(todo, { onSuccess, onError, onSettled }) call to
use the correct parameter ordering: onSuccess should be (data, variables,
context), onError should be (error, variables, context), and onSettled should be
(data, error, variables, context), removing any onMutateResult parameter from
all callback definitions.
- Around line 221-255: The example uses incorrect callback signatures and
wrongly references context.client; fix setMutationDefaults on the QueryClient so
onMutate has the signature onMutate = async (variables) => { await
queryClient.cancelQueries({ queryKey: ['todos'] }); const optimisticTodo = { id:
uuid(), title: variables.title }; queryClient.setQueryData(['todos'], (old) =>
[...old, optimisticTodo]); return { optimisticTodo }; } and update onSuccess and
onError to use the correct parameters (onSuccess = (result, variables, context)
=> { queryClient.setQueryData(['todos'], (old) => old.map((todo) => todo.id ===
context.optimisticTodo.id ? result : todo)); } and onError = (error, variables,
context) => { queryClient.setQueryData(['todos'], (old) => old.filter((todo) =>
todo.id !== context.optimisticTodo.id)); }); also keep mutationFn: addTodo and
retry: 3 as before.

In `@docs/framework/solid/guides/prefetching.md`:
- Around line 216-253: Update the example to use TanStack Router v1 functional
APIs instead of the deprecated constructors: replace RouterContext/new
RouterContext and routerContext.createRootRoute() with createRootRoute() or
createRootRouteWithContext<...>(), replace new Route({...}) with
createRoute({...}), assemble children via rootRoute.addChildren([...]) and
create the router with createRouter({ routeTree: rootRoute }) (or the v1
equivalent), while keeping the existing query-related symbols
(queryClient.prefetchQuery, beforeLoad, loader, useRouteContext,
articleQueryOptions, commentsQueryOptions) intact so the prefetching logic and
component useQuery calls work the same under the new API.

In `@docs/framework/solid/guides/query-cancellation.md`:
- Around line 111-120: The queryFn passed to useQuery (in todosQuery) forgets to
return the promise from client.request, so the query resolves to undefined;
update the queryFn to return client.request({ document: query, signal }) (i.e.,
ensure the function body of queryFn returns the client.request promise) so
useQuery receives the fetched data from GraphQLClient.request.

---

Nitpick comments:
In `@docs/framework/solid/guides/optimistic-updates.md`:
- Line 67: Remove the unused local variable by deleting the line that assigns
queryClient via useQueryClient(), or if you intended to show the alternate
pattern, update the mutation callbacks to use the local queryClient instead of
context.client and add a brief comment explaining the alternative; specifically
address the const queryClient = useQueryClient() declaration and either remove
it or replace usages of context.client inside the mutation callback functions
with queryClient so the example is consistent.

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 35ca6d1 and ec4bb38.

⛔ Files ignored due to path filters (2)
  • examples/solid/offline/src/assets/favicon.ico is excluded by !**/*.ico
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (45)
  • docs/config.json
  • docs/framework/react/guides/caching.md
  • docs/framework/react/guides/important-defaults.md
  • docs/framework/react/guides/mutations.md
  • docs/framework/react/guides/parallel-queries.md
  • docs/framework/react/guides/queries.md
  • docs/framework/react/guides/query-cancellation.md
  • docs/framework/react/guides/query-options.md
  • docs/framework/react/guides/request-waterfalls.md
  • docs/framework/solid/guides/background-fetching-indicators.md
  • docs/framework/solid/guides/caching.md
  • docs/framework/solid/guides/default-query-function.md
  • docs/framework/solid/guides/dependent-queries.md
  • docs/framework/solid/guides/disabling-queries.md
  • docs/framework/solid/guides/important-defaults.md
  • docs/framework/solid/guides/infinite-queries.md
  • docs/framework/solid/guides/initial-query-data.md
  • docs/framework/solid/guides/invalidations-from-mutations.md
  • docs/framework/solid/guides/mutations.md
  • docs/framework/solid/guides/optimistic-updates.md
  • docs/framework/solid/guides/paginated-queries.md
  • docs/framework/solid/guides/parallel-queries.md
  • docs/framework/solid/guides/placeholder-query-data.md
  • docs/framework/solid/guides/prefetching.md
  • docs/framework/solid/guides/queries.md
  • docs/framework/solid/guides/query-cancellation.md
  • docs/framework/solid/guides/query-functions.md
  • docs/framework/solid/guides/query-invalidation.md
  • docs/framework/solid/guides/query-keys.md
  • docs/framework/solid/guides/query-options.md
  • docs/framework/solid/guides/request-waterfalls.md
  • docs/framework/solid/guides/scroll-restoration.md
  • docs/framework/solid/guides/suspense.md
  • docs/framework/solid/guides/updates-from-mutation-responses.md
  • docs/framework/solid/guides/window-focus-refetching.md
  • examples/solid/offline/.eslintrc.cjs
  • examples/solid/offline/README.md
  • examples/solid/offline/index.html
  • examples/solid/offline/package.json
  • examples/solid/offline/src/App.tsx
  • examples/solid/offline/src/api.ts
  • examples/solid/offline/src/index.tsx
  • examples/solid/offline/src/movies.ts
  • examples/solid/offline/tsconfig.json
  • examples/solid/offline/vite.config.ts
✅ Files skipped from review due to trivial changes (3)
  • examples/solid/offline/index.html
  • docs/framework/react/guides/caching.md
  • docs/framework/solid/guides/parallel-queries.md
🚧 Files skipped from review as they are similar to previous changes (19)
  • docs/framework/solid/guides/background-fetching-indicators.md
  • examples/solid/offline/package.json
  • examples/solid/offline/src/index.tsx
  • docs/framework/solid/guides/initial-query-data.md
  • docs/framework/react/guides/query-cancellation.md
  • docs/framework/solid/guides/query-options.md
  • docs/framework/solid/guides/window-focus-refetching.md
  • docs/framework/solid/guides/default-query-function.md
  • docs/framework/solid/guides/invalidations-from-mutations.md
  • examples/solid/offline/src/api.ts
  • docs/framework/solid/guides/query-invalidation.md
  • examples/solid/offline/README.md
  • docs/config.json
  • examples/solid/offline/vite.config.ts
  • examples/solid/offline/src/App.tsx
  • examples/solid/offline/.eslintrc.cjs
  • docs/framework/react/guides/parallel-queries.md
  • docs/framework/solid/guides/suspense.md
  • docs/framework/solid/guides/paginated-queries.md

> Structural sharing only works with JSON-compatible values, any other value types will always be considered as changed. If you are seeing performance issues because of large responses for example, you can disable this feature with the `config.structuralSharing` flag. If you are dealing with non-JSON compatible values in your query responses and still want to detect if data has changed or not, you can provide your own custom function as `config.structuralSharing` to compute a value from the old and new responses, retaining references as required.
[//]: # 'StructuralSharing'

> Structural sharing only works with JSON-compatible values, any other value types will always be considered as changed. If you are seeing performance issues because of large responses for example, you can disable this feature with the `config.structuralSharing` flag. If you are dealing with non-JSON compatible values in your query responses and still want to detect if data has changed or not, you can provide your own custom function as `config.structuralSharing` to compute a value from the old and new responses, retaining references as required.
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 | 🟡 Minor

Hyphenate compound modifier in docs text.

On Line 41, “non-JSON compatible values” should be “non-JSON-compatible values” for correct grammar and consistency.

✏️ Proposed wording tweak
-> Structural sharing only works with JSON-compatible values, any other value types will always be considered as changed. If you are seeing performance issues because of large responses for example, you can disable this feature with the `config.structuralSharing` flag. If you are dealing with non-JSON compatible values in your query responses and still want to detect if data has changed or not, you can provide your own custom function as `config.structuralSharing` to compute a value from the old and new responses, retaining references as required.
+> Structural sharing only works with JSON-compatible values, any other value types will always be considered as changed. If you are seeing performance issues because of large responses for example, you can disable this feature with the `config.structuralSharing` flag. If you are dealing with non-JSON-compatible values in your query responses and still want to detect if data has changed or not, you can provide your own custom function as `config.structuralSharing` to compute a value from the old and new responses, retaining references as required.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
> Structural sharing only works with JSON-compatible values, any other value types will always be considered as changed. If you are seeing performance issues because of large responses for example, you can disable this feature with the `config.structuralSharing` flag. If you are dealing with non-JSON compatible values in your query responses and still want to detect if data has changed or not, you can provide your own custom function as `config.structuralSharing` to compute a value from the old and new responses, retaining references as required.
> Structural sharing only works with JSON-compatible values, any other value types will always be considered as changed. If you are seeing performance issues because of large responses for example, you can disable this feature with the `config.structuralSharing` flag. If you are dealing with non-JSON-compatible values in your query responses and still want to detect if data has changed or not, you can provide your own custom function as `config.structuralSharing` to compute a value from the old and new responses, retaining references as required.
🧰 Tools
🪛 LanguageTool

[grammar] ~41-~41: Use a hyphen to join words.
Context: ...` flag. If you are dealing with non-JSON compatible values in your query response...

(QB_NEW_EN_HYPHEN)


[style] ~41-~41: In contexts where ‘if’ is followed by ‘or’, using ‘whether’ may be more appropriate (and formal).
Context: ...uery responses and still want to detect if data has changed or not, you can provid...

(IF_WHETHER)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/framework/react/guides/important-defaults.md` at line 41, Update the
documentation text to hyphenate the compound modifier; replace the phrase
"non-JSON compatible values" with "non-JSON-compatible values" in the paragraph
that references structural sharing and the config.structuralSharing option so
the wording reads correctly (retain surrounding context about providing a custom
function for config.structuralSharing).

queryKey: ['projects', userId()],
queryFn: getProjectsByUser,
// The query will not execute until the userId exists
enabled: !!userId(),
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 | 🟡 Minor

Use a nullish check for enabled instead of truthiness.

enabled: !!userId() will incorrectly block execution when a valid ID is 0. Prefer a nullish guard.

Suggested fix
-  enabled: !!userId(),
+  enabled: userId() != null,
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
enabled: !!userId(),
enabled: userId() != null,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/framework/solid/guides/dependent-queries.md` at line 23, The current
enabled flag uses a truthy coercion (!!userId()) which will disable the query
when userId() returns 0; change the guard to a nullish check so numeric IDs like
0 are allowed — e.g., replace the coercion with a nullish comparison such as
userId() != null (or userId() !== null && userId() !== undefined) for the
enabled property that references userId(). Ensure you update the enabled
assignment where it's defined to use this nullish check.

[//]: # 'ExampleValue'
[//]: # 'Memoization'

### Placeholder Data Memoization
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 | 🟡 Minor

Fix heading level to comply with markdown conventions.

The heading jumps directly to h3 (###) without a preceding h2 (##). Markdown headings should increment by one level at a time.

📝 Proposed fix
-### Placeholder Data Memoization
+## Placeholder Data Memoization
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
### Placeholder Data Memoization
## Placeholder Data Memoization
🧰 Tools
🪛 markdownlint-cli2 (0.21.0)

[warning] 22-22: Heading levels should only increment by one level at a time
Expected: h2; Actual: h3

(MD001, heading-increment)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/framework/solid/guides/placeholder-query-data.md` at line 22, The
heading "Placeholder Data Memoization" is at h3 (###) but should be incremented
from an h2; change the heading token from "### Placeholder Data Memoization" to
"## Placeholder Data Memoization" so the document uses sequential heading levels
and complies with Markdown conventions.

@TkDodo TkDodo merged commit e31bda9 into TanStack:main Feb 26, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants