feat(angular-query): move devtools to conditional sub-paths
Breaking changes
Import paths
withDevtools moved to subpath
Most applications, to completely tree shake devtools from production builds
import { withDevtools } from '@tanstack/angular-query-experimental/devtools'
If you want to also use devtools in production builds
import { withDevtools } from '@tanstack/angular-query-experimental/devtools/production'
injectDevtoolsPanel moved to main package
Most applications, to completely tree shake devtools panel from production builds
import { injectDevtoolsPanel } from '@tanstack/angular-query-experimental/devtools-panel'
If you want to also use devtools panel in production builds
import { injectDevtoolsPanel } from '@tanstack/angular-query-experimental/devtools-panel/production'
angular-query-devtools-experimental package
After moving injectDevtoolsPanel to the main package, the angular devtools package is obsolete.
Improvements and fixes
Subpath exports
Experience with subpath exports paves the way to add functionality such as RxJs integration without needing to have the whole Angular TanStack Query package depend on RxJs.
Isolating devtools to a subpath reliably fixes #9078, where esbuild was generating chunks even when devtools were not used at all. While these chunks were not adding to the main bundle size it unnecessarily increased build time and deployment size.
Conditional exports
The devtools subpath exports further utilize conditional exports. For development builds, the function is exported normally. For production builds a tiny stub function is exported, resulting in excellent tree shaking without having to configure environments.
Dependency injection in the devtools callback
Fixes #8824 After no longer running the options computation function in the injection context it was no longer possible to use dependency injection to inject values. The withDevtools function now has a deps option to automatically inject dependencies and pass it as a parameter to the computation function. This solves this issue while not injecting a value each time the computation function is run which would be the case if the function would run in the injection context. This is similar to and internally uses deps on Angular's useFactory.
withDevtools(
(devToolsOptionsManager: DevtoolsOptionsManager) => ({
loadDevtools: devToolsOptionsManager.loadDevtools(),
}),
{
// `deps` is used to inject and pass `DevtoolsOptionsManager` to the `withDevtools` callback.
deps: [DevtoolsOptionsManager],
},
)
Adds a check if devtools were already provided
Fixes #8705
View your CI Pipeline Execution ↗ for commit 1f2db27426a35e45904678e9cd6b9dd91c590247
| Command | Status | Duration | Result |
|---|---|---|---|
nx affected --targets=test:sherif,test:knip,tes... |
✅ Succeeded | 1m 25s | View ↗ |
nx run-many --target=build --exclude=examples/*... |
✅ Succeeded | 8s | View ↗ |
☁️ Nx Cloud last updated this comment at 2025-09-15 20:47:05 UTC
Codecov Report
:x: Patch coverage is 92.30769% with 8 lines in your changes missing coverage. Please review.
:white_check_mark: Project coverage is 45.75%. Comparing base (ca56514) to head (1f2db27).
:warning: Report is 2 commits behind head on main.
Additional details and impacted files
@@ Coverage Diff @@
## main #9270 +/- ##
==========================================
+ Coverage 45.51% 45.75% +0.23%
==========================================
Files 209 213 +4
Lines 8379 8427 +48
Branches 1895 1913 +18
==========================================
+ Hits 3814 3856 +42
- Misses 4118 4124 +6
Partials 447 447
:rocket: New features to boost your workflow:
- :snowflake: Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
- :package: JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.
More templates
- @tanstack/query-example-angular-auto-refetching
- @tanstack/query-example-angular-basic
- @tanstack/query-example-angular-basic-persister
- @tanstack/query-example-angular-devtools-panel
- @tanstack/query-example-angular-infinite-query-with-max-pages
- @tanstack/query-example-angular-optimistic-updates
- @tanstack/query-example-angular-pagination
- @tanstack/query-example-angular-query-options-from-a-service
- @tanstack/query-example-angular-router
- @tanstack/query-example-angular-rxjs
- @tanstack/query-example-angular-simple
- @tanstack/query-example-react-algolia
- @tanstack/query-example-react-auto-refetching
- @tanstack/query-example-react-basic
- @tanstack/query-example-react-basic-graphql-request
- @tanstack/query-example-chat
- @tanstack/query-example-react-default-query-function
- @tanstack/query-example-react-devtools-panel
- @tanstack/query-example-eslint-legacy
- @tanstack/query-example-react-infinite-query-with-max-pages
- @tanstack/query-example-react-load-more-infinite-scroll
- @tanstack/query-example-react-nextjs
- @tanstack/query-example-react-nextjs-app-prefetching
- @tanstack/query-example-nextjs-suspense-streaming
- @tanstack/query-example-react-offline
- @tanstack/query-example-react-optimistic-updates-cache
- @tanstack/query-example-react-optimistic-updates-ui
- @tanstack/query-example-react-pagination
- @tanstack/query-example-react-playground
- @tanstack/query-example-react-prefetching
- @tanstack/query-example-react-react-native
- @tanstack/query-example-react-router
- @tanstack/query-example-react-rick-morty
- @tanstack/query-example-react-shadow-dom
- @tanstack/query-example-react-simple
- @tanstack/query-example-react-star-wars
- @tanstack/query-example-react-suspense
- @tanstack/query-example-solid-astro
- @tanstack/query-example-solid-basic
- @tanstack/query-example-solid-basic-graphql-request
- @tanstack/query-example-solid-default-query-function
- @tanstack/query-example-solid-simple
- @tanstack/query-example-solid-start-streaming
- @tanstack/query-example-svelte-auto-refetching
- @tanstack/query-example-svelte-basic
- @tanstack/query-example-svelte-load-more-infinite-scroll
- @tanstack/query-example-svelte-optimistic-updates
- @tanstack/query-example-svelte-playground
- @tanstack/query-example-svelte-simple
- @tanstack/query-example-svelte-ssr
- @tanstack/query-example-svelte-star-wars
- @tanstack/query-example-vue-2.6-basic
- @tanstack/query-example-vue-2.7-basic
- @tanstack/query-example-vue-basic
- @tanstack/query-example-vue-dependent-queries
- @tanstack/query-example-vue-nuxt3
- @tanstack/query-example-vue-persister
- @tanstack/query-example-vue-simple
@tanstack/angular-query-experimental
npm i https://pkg.pr.new/@tanstack/angular-query-experimental@9270
@tanstack/eslint-plugin-query
npm i https://pkg.pr.new/@tanstack/eslint-plugin-query@9270
@tanstack/query-async-storage-persister
npm i https://pkg.pr.new/@tanstack/query-async-storage-persister@9270
@tanstack/query-broadcast-client-experimental
npm i https://pkg.pr.new/@tanstack/query-broadcast-client-experimental@9270
@tanstack/query-core
npm i https://pkg.pr.new/@tanstack/query-core@9270
@tanstack/query-devtools
npm i https://pkg.pr.new/@tanstack/query-devtools@9270
@tanstack/query-persist-client-core
npm i https://pkg.pr.new/@tanstack/query-persist-client-core@9270
@tanstack/query-sync-storage-persister
npm i https://pkg.pr.new/@tanstack/query-sync-storage-persister@9270
@tanstack/react-query
npm i https://pkg.pr.new/@tanstack/react-query@9270
@tanstack/react-query-devtools
npm i https://pkg.pr.new/@tanstack/react-query-devtools@9270
@tanstack/react-query-next-experimental
npm i https://pkg.pr.new/@tanstack/react-query-next-experimental@9270
@tanstack/react-query-persist-client
npm i https://pkg.pr.new/@tanstack/react-query-persist-client@9270
@tanstack/solid-query
npm i https://pkg.pr.new/@tanstack/solid-query@9270
@tanstack/solid-query-devtools
npm i https://pkg.pr.new/@tanstack/solid-query-devtools@9270
@tanstack/solid-query-persist-client
npm i https://pkg.pr.new/@tanstack/solid-query-persist-client@9270
@tanstack/svelte-query
npm i https://pkg.pr.new/@tanstack/svelte-query@9270
@tanstack/svelte-query-devtools
npm i https://pkg.pr.new/@tanstack/svelte-query-devtools@9270
@tanstack/svelte-query-persist-client
npm i https://pkg.pr.new/@tanstack/svelte-query-persist-client@9270
@tanstack/vue-query
npm i https://pkg.pr.new/@tanstack/vue-query@9270
@tanstack/vue-query-devtools
npm i https://pkg.pr.new/@tanstack/vue-query-devtools@9270
commit: 1f2db27
Walkthrough
Modularizes Angular devtools into dedicated devtools and devtools-panel submodules with production stubs and lazy dynamic imports; removes the legacy @tanstack/angular-query-devtools-experimental package and its build/config; updates package exports, examples, tests, and many autogenerated Angular docs and metadata.
Changes
| Cohort / File(s) | Summary |
|---|---|
Devtools core & exportspackages/angular-query-experimental/src/devtools/*, packages/angular-query-experimental/package.json, packages/angular-query-experimental/src/index.ts |
Add new devtools public module (types, withDevtools, production stub/index), optionalize @tanstack/query-devtools, add package exports for devtools/devtools-panel, remove top-level re-exports of devtools, add provideAngularQuery. |
Devtools panel API & runtimepackages/angular-query-experimental/src/devtools-panel/* |
Add devtools-panel types, runtime injectDevtoolsPanel implementation, production stub and production index, and re-export index. |
Remove legacy devtools packagepackages/angular-query-devtools-experimental/* |
Delete legacy package manifest, source files, configs, tests, and build tooling; remove re-exports and example dependency. |
Examples updatedexamples/angular/*/src/app/app.config.ts, examples/angular/devtools-panel/*, examples/angular/devtools-panel/package.json |
Update example imports to @tanstack/angular-query-experimental/devtools and .../devtools-panel; remove deprecated example dependency. |
Tests adaptedpackages/angular-query-experimental/src/__tests__/* |
Update tests to new import locations, handle async dynamic imports, add lifecycle/platform scenarios, rename Devtools option fields (loadDeveloperTools → loadDevtools). |
Docs: devtools guidance & indexdocs/framework/angular/devtools.md, docs/framework/angular/reference/index.md, (removed) docs/.../functions/withdevtools.md |
Rewrite devtools docs for DI-enabled withDevtools, add production import path guidance, remove old withDevtools doc, update index entries and public API listings. |
Docs: autogenerated Angular referencesdocs/framework/angular/reference/** |
Large batch of autogenerated metadata edits: DO NOT EDIT notices, inline “Defined in” links, unified type shapes, generic-arity fixes, literal renames (e.g., DeveloperTools → Devtools), and many page removals/relocations. |
Examples: devtools-panel componentsexamples/angular/devtools-panel/src/app/components/* |
Change import paths and dynamic import targets to new @tanstack/angular-query-experimental/devtools-panel. |
Config & toolingknip.json, package.json, codecov.yml, packages/angular-query-experimental/eslint.config.js |
Add knip workspace entries for production entry points; remove pnpm override and codecov component for legacy devtools; simplify ESLint jsdoc rule. |
Public API/type adjustments & annotationspackages/angular-query-experimental/src/*, many docs/... |
Remove numerous @public JSDoc tags, adjust exported type aliases and generic arities (infinite/inject/mutation-related), and add new devtools types and overloads. |
Misc: internals & doc snippetspackages/angular-query-experimental/src/inject-is-restoring.ts, packages/angular-query-experimental/src/mutation-options.ts, others |
Internal token naming/documentation tweaks and JSDoc example reformatting. |
Sequence Diagram(s)
sequenceDiagram
autonumber
actor App as App bootstrap
participant DI as Angular DI
participant With as withDevtools
participant Options as DevtoolsOptionsSignal
participant Loader as DynamicImport(@tanstack/query-devtools)
participant TD as TanstackQueryDevtools
App->>DI: provideTanStackQuery(..., withDevtools(fn,{deps}))
DI->>With: register providers & ENVIRONMENT_INITIALIZER
With->>Options: derive reactive options (inject deps via DI)
DI->>With: run initializer (browser only)
alt loadDevtools true OR dev mode
With->>Loader: import('@tanstack/query-devtools') (async)
Loader-->>With: module
With->>TD: new TanstackQueryDevtools(...) and mount to body
Options-->>TD: apply updates reactively
else skip
With-->>DI: no-op (devtools not loaded)
end
DI->>TD: destroy on injector teardown
sequenceDiagram
autonumber
actor Component as Consumer
participant Injector as Injector
participant Fn as injectDevtoolsPanel
participant Comp as computed options
participant Loader as DynamicImport(@tanstack/query-devtools)
participant Panel as TanstackQueryDevtoolsPanel
Component->>Fn: call factory -> DevtoolsPanelOptions
Fn->>Injector: runInInjectionContext (resolve DestroyRef, PLATFORM_ID)
alt non-browser
Fn-->>Component: return noop destroy
else browser
Fn->>Comp: compute options (hostElement?, client?, ...)
alt hostElement appears & no panel
Fn->>Loader: import('@tanstack/query-devtools') (async)
Loader-->>Fn: module
Fn->>Panel: new Panel(...) and mount(hostElement)
else options change
Fn->>Panel: update(client/errorTypes/onClose)
else hostElement removed
Fn->>Panel: destroy()
end
Injector->>Panel: ensure destroy on teardown
Fn-->>Component: return destroy handle
end
Estimated code review effort
🎯 4 (Complex) | ⏱️ ~60 minutes
Possibly related PRs
- TanStack/query#9648 — Doc/example edits touching
mutation-optionsexamples; overlaps documentation changes in this PR. - TanStack/query#8817 — Prior work that changed DI behavior for devtools; conceptually related to DI-aware devtools loading and fixes.
- (No further strongly code-linked PRs found.)
Suggested reviewers
- TkDodo
"I nibble docs and hop between files,
I move the tools where production smiles.
If DI whispers yes, they'll wake and play,
else tucked away, they save the bundle's day.
A rabbit cheers—lazy imports all the way!" 🐇✨
Pre-merge checks and finishing touches
❌ Failed checks (1 warning)
| Check name | Status | Explanation | Resolution |
|---|---|---|---|
| Out of Scope Changes Check | ⚠️ Warning | While the devtools work is well scoped, the PR also contains several public API/type-surface changes that are not documented in the PR objectives and may be breaking, including mutationOptions overloads (mutation-options.ts), a change to CreateMutationResult to include BaseMutationNarrowing (types.ts), broad unification/retyping of query/infinite-query option types (query-options.ts, infinite-query-options.ts and related type-alias changes), and reducing generic arity in injectInfiniteQuery and related aliases; these type/signature changes go beyond the stated devtools/export objectives. Many documentation-only edits are fine, but the surfaced API/type changes should be explicitly justified or split out because they may affect downstream consumers. | Request the author to either separate the unrelated public API/type changes into a distinct PR with rationale and migration notes or explicitly document and justify them in this PR (including compatibility tests and changelog entries); block merge until the scope and compatibility impact of those API/type changes are clarified and CI/tests run. |
✅ Passed checks (4 passed)
| Check name | Status | Explanation |
|---|---|---|
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled. |
| Title Check | ✅ Passed | The title "feat(angular-query): move devtools to conditional sub-paths" is concise, follows conventional-commit style, and accurately summarizes the primary change (moving devtools to dedicated conditional sub-paths and related export/packaging work) as reflected across the diff. The title is specific to the main change and readable for teammates scanning history. |
| Linked Issues Check | ✅ Passed | The changes implement the linked-issue goals: conditional subpath exports, stubs, and making @tanstack/query-devtools optional to avoid emitting devtools chunks in production builds ([#9078]); adding DI-aware devtools options (deps support, DevtoolsOptionsManager and updated withDevtools API) to restore injection inside loadDevtools ([#8824]); and adding provider-scoped guards (DEVTOOLS_PROVIDED) plus lifecycle tests to avoid duplicate registrations and to support lazy-route/provider usage ([#8705]). Test additions and packaging/export edits in packages/angular-query-experimental align with these objectives and include explicit tests for injector lifecycle, platform gating, and dependency injection. |
| Docstring Coverage | ✅ Passed | No functions found in the changes. Docstring coverage check skipped. |
✨ Finishing touches
- [ ] 📝 Generate Docstrings
🧪 Generate unit tests
- [ ] Create PR with unit tests
- [ ] Post copyable unit tests in a comment
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands and usage tips.
I see this is in active development, thank you for your work @arnoud-dv 💪🏼