diff --git a/docs/vue/devtools.md b/docs/vue/devtools.md
index f6a93fb085..2cb20971fb 100644
--- a/docs/vue/devtools.md
+++ b/docs/vue/devtools.md
@@ -11,3 +11,52 @@ The only thing you need to do is to install the official **[Vue Devtools](https:
Vue Query will seamlessly integrate with the official devtools, adding custom inspector and timeline events.
Devtool code will be treeshaken from production bundles by default.
+
+## Component based Devtools (Vue 3)
+
+You can embeed devtools component into your page by using dedicated package.
+Component based devtools are using the same framework-agnostic implementation, have more features and are updated more frequently.
+
+The devtools component is a separate package that you need to install:
+
+```bash
+$ npm i @tanstack/vue-query-devtools
+# or
+$ pnpm add @tanstack/vue-query-devtools
+# or
+$ yarn add @tanstack/vue-query-devtools
+```
+
+By default, Vue Query Devtools are only included in bundles when `process.env.NODE_ENV === 'development'`, so you don't need to worry about excluding them during a production build.
+
+Devtools will be mounted as a fixed, floating element in your app and provide a toggle in the corner of the screen to show and hide the devtools. This toggle state will be stored and remembered in localStorage across reloads.
+
+Place the following code as high in your Vue app as you can. The closer it is to the root of the page, the better it will work!
+
+```vue
+
+
+
+ The app!
+
+
+```
+
+### Options
+
+- `initialIsOpen: Boolean`
+ - Set this `true` if you want the dev tools to default to being open
+- `buttonPosition?: "top-left" | "top-right" | "bottom-left" | "bottom-right"`
+ - Defaults to `bottom-left`
+ - The position of the React Query logo to open and close the devtools panel
+- `position?: "top" | "bottom" | "left" | "right"`
+ - Defaults to `bottom`
+ - The position of the React Query devtools panel
+- `client?: QueryClient`,
+ - Use this to use a custom QueryClient. Otherwise, the one from the nearest context will be used.
+- `errorTypes?: { name: string; initializer: (query: Query) => TError}`
+ - Use this to predefine some errors that can be triggered on your queries. Initializer will be called (with the specific query) when that error is toggled on from the UI. It must return an Error.
+- `styleNonce?: string`
+ - Use this to pass a nonce to the style tag that is added to the document head. This is useful if you are using a Content Security Policy (CSP) nonce to allow inline styles.
diff --git a/examples/vue/basic/package.json b/examples/vue/basic/package.json
index 10244dd465..958afb1f49 100644
--- a/examples/vue/basic/package.json
+++ b/examples/vue/basic/package.json
@@ -9,6 +9,7 @@
},
"dependencies": {
"@tanstack/vue-query": "^5.0.0",
+ "@tanstack/vue-query-devtools": "^5.0.0",
"vue": "^3.3.0"
},
"devDependencies": {
diff --git a/examples/vue/basic/src/App.vue b/examples/vue/basic/src/App.vue
index c8275e1840..5a9b09d3bf 100644
--- a/examples/vue/basic/src/App.vue
+++ b/examples/vue/basic/src/App.vue
@@ -1,12 +1,13 @@
+
+
+
+
diff --git a/packages/vue-query-devtools/src/index.ts b/packages/vue-query-devtools/src/index.ts
new file mode 100644
index 0000000000..39c4912e86
--- /dev/null
+++ b/packages/vue-query-devtools/src/index.ts
@@ -0,0 +1,11 @@
+import devtools from './devtools.vue'
+import type { DefineComponent } from 'vue'
+import type { DevtoolsOptions } from './types'
+
+export const VueQueryDevtools = (
+ process.env.NODE_ENV !== 'development'
+ ? function () {
+ return null
+ }
+ : devtools
+) as DefineComponent
diff --git a/packages/vue-query-devtools/src/production.ts b/packages/vue-query-devtools/src/production.ts
new file mode 100644
index 0000000000..11cd60bd96
--- /dev/null
+++ b/packages/vue-query-devtools/src/production.ts
@@ -0,0 +1,3 @@
+import devtools from './devtools.vue'
+
+export default devtools
diff --git a/packages/vue-query-devtools/src/shims-vue.d.ts b/packages/vue-query-devtools/src/shims-vue.d.ts
new file mode 100644
index 0000000000..b07a05969e
--- /dev/null
+++ b/packages/vue-query-devtools/src/shims-vue.d.ts
@@ -0,0 +1,6 @@
+declare module '*.vue' {
+ import type { DefineComponent } from 'vue'
+
+ const component: DefineComponent<{}, {}, any>
+ export default component
+}
diff --git a/packages/vue-query-devtools/src/types.ts b/packages/vue-query-devtools/src/types.ts
new file mode 100644
index 0000000000..87673849e1
--- /dev/null
+++ b/packages/vue-query-devtools/src/types.ts
@@ -0,0 +1,37 @@
+import type {
+ DevToolsErrorType,
+ DevtoolsButtonPosition,
+ DevtoolsPosition,
+} from '@tanstack/query-devtools'
+import type { QueryClient } from '@tanstack/vue-query'
+
+export interface DevtoolsOptions {
+ /**
+ * Set this true if you want the dev tools to default to being open
+ */
+ initialIsOpen?: boolean
+ /**
+ * The position of the React Query logo to open and close the devtools panel.
+ * 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right'
+ * Defaults to 'bottom-left'.
+ */
+ buttonPosition?: DevtoolsButtonPosition
+ /**
+ * The position of the React Query devtools panel.
+ * 'top' | 'bottom' | 'left' | 'right'
+ * Defaults to 'bottom'.
+ */
+ position?: DevtoolsPosition
+ /**
+ * Custom instance of QueryClient
+ */
+ client?: QueryClient
+ /**
+ * Use this so you can define custom errors that can be shown in the devtools.
+ */
+ errorTypes?: Array
+ /**
+ * Use this to pass a nonce to the style tag that is added to the document head. This is useful if you are using a Content Security Policy (CSP) nonce to allow inline styles.
+ */
+ styleNonce?: string
+}
diff --git a/packages/vue-query-devtools/tsconfig.json b/packages/vue-query-devtools/tsconfig.json
new file mode 100644
index 0000000000..22796613f9
--- /dev/null
+++ b/packages/vue-query-devtools/tsconfig.json
@@ -0,0 +1,9 @@
+{
+ "extends": "../../tsconfig.json",
+ "compilerOptions": {
+ "noEmit": false,
+ "emitDeclarationOnly": true,
+ "outDir": "dist"
+ },
+ "include": ["src/**/*.ts", "src/**/*.vue", "shims-vue.d.ts"]
+}
diff --git a/packages/vue-query-devtools/vite.config.js b/packages/vue-query-devtools/vite.config.js
new file mode 100644
index 0000000000..1a5c090954
--- /dev/null
+++ b/packages/vue-query-devtools/vite.config.js
@@ -0,0 +1,29 @@
+import { resolve } from 'path'
+import { defineConfig } from 'vite'
+import vuePlugin from '@vitejs/plugin-vue'
+
+export default defineConfig({
+ plugins: [vuePlugin()],
+ build: {
+ lib: {
+ // Could also be a dictionary or array of multiple entry points
+ entry: [
+ resolve(__dirname, 'src/index.ts'),
+ resolve(__dirname, 'src/production.ts'),
+ ],
+ formats: ['es'],
+ },
+ rollupOptions: {
+ // make sure to externalize deps that shouldn't be bundled
+ // into your library
+ external: ['vue'],
+ output: {
+ // Provide global variables to use in the UMD build
+ // for externalized deps
+ globals: {
+ vue: 'Vue',
+ },
+ },
+ },
+ },
+})
diff --git a/packages/vue-query-devtools/vitest.config.ts b/packages/vue-query-devtools/vitest.config.ts
new file mode 100644
index 0000000000..d7af60d17e
--- /dev/null
+++ b/packages/vue-query-devtools/vitest.config.ts
@@ -0,0 +1,12 @@
+import { defineConfig } from 'vitest/config'
+
+export default defineConfig({
+ test: {
+ name: 'vue-query-devtools',
+ dir: './src',
+ watch: false,
+ setupFiles: ['test-setup.ts'],
+ environment: 'jsdom',
+ coverage: { provider: 'istanbul' },
+ },
+})
diff --git a/packages/vue-query/README.md b/packages/vue-query/README.md
index 4d24a42a5c..867d01a0e7 100644
--- a/packages/vue-query/README.md
+++ b/packages/vue-query/README.md
@@ -13,7 +13,7 @@ Support for Vue 2.x via [vue-demi](https://github.com/vueuse/vue-demi)
# Documentation
-Visit https://tanstack.com/query/v4/docs/adapters/vue-query
+Visit https://tanstack.com/query/latest/docs/vue/overview
# Quick Features
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index bf87c7714d..0539eb1811 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1306,6 +1306,9 @@ importers:
'@tanstack/vue-query':
specifier: ^5.0.0
version: link:../../../packages/vue-query
+ '@tanstack/vue-query-devtools':
+ specifier: ^5.0.0
+ version: link:../../../packages/vue-query-devtools
vue:
specifier: ^3.3.0
version: 3.3.0
@@ -1860,6 +1863,28 @@ importers:
specifier: npm:vue@2.7
version: /vue@2.7.0
+ packages/vue-query-devtools:
+ dependencies:
+ '@tanstack/query-devtools':
+ specifier: workspace:*
+ version: link:../query-devtools
+ devDependencies:
+ '@tanstack/vue-query':
+ specifier: workspace:*
+ version: link:../vue-query
+ '@vitejs/plugin-vue':
+ specifier: ^4.4.0
+ version: 4.4.0(vite@4.4.11)(vue@3.3.0)
+ vite:
+ specifier: ^4.4.4
+ version: 4.4.11(@types/node@18.18.0)
+ vue:
+ specifier: ^3.3.0
+ version: 3.3.0
+ vue-tsc:
+ specifier: ^1.8.21
+ version: 1.8.21(typescript@5.2.2)
+
packages:
/@aashutoshrathi/word-wrap@1.2.6:
@@ -9380,6 +9405,17 @@ packages:
vite: 4.4.11(@types/node@18.18.0)
vue: 3.3.0
+ /@vitejs/plugin-vue@4.4.0(vite@4.4.11)(vue@3.3.0):
+ resolution: {integrity: sha512-xdguqb+VUwiRpSg+nsc2HtbAUSGak25DXYvpQQi4RVU1Xq1uworyoH/md9Rfd8zMmPR/pSghr309QNcftUVseg==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+ peerDependencies:
+ vite: ^4.0.0
+ vue: ^3.2.25
+ dependencies:
+ vite: 4.4.11(@types/node@18.18.0)
+ vue: 3.3.0
+ dev: true
+
/@vitest/coverage-istanbul@0.33.0(vitest@0.33.0):
resolution: {integrity: sha512-DGv6ybomCbLFGlNOGHgVCsaqHPWJWLp8JPrwzZo8I4vZ/O3muqTyZq5R52CZl0ENqgjFGWjra7yNPFUgxKf5pw==}
peerDependencies:
@@ -9434,6 +9470,25 @@ packages:
pretty-format: 29.7.0
dev: true
+ /@volar/language-core@1.10.10:
+ resolution: {integrity: sha512-nsV1o3AZ5n5jaEAObrS3MWLBWaGwUj/vAsc15FVNIv+DbpizQRISg9wzygsHBr56ELRH8r4K75vkYNMtsSNNWw==}
+ dependencies:
+ '@volar/source-map': 1.10.10
+ dev: true
+
+ /@volar/source-map@1.10.10:
+ resolution: {integrity: sha512-GVKjLnifV4voJ9F0vhP56p4+F3WGf+gXlRtjFZsv6v3WxBTWU3ZVeaRaEHJmWrcv5LXmoYYpk/SC25BKemPRkg==}
+ dependencies:
+ muggle-string: 0.3.1
+ dev: true
+
+ /@volar/typescript@1.10.10:
+ resolution: {integrity: sha512-4a2r5bdUub2m+mYVnLu2wt59fuoYWe7nf0uXtGHU8QQ5LDNfzAR0wK7NgDiQ9rcl2WT3fxT2AA9AylAwFtj50A==}
+ dependencies:
+ '@volar/language-core': 1.10.10
+ path-browserify: 1.0.1
+ dev: true
+
/@vue/compiler-core@3.3.0:
resolution: {integrity: sha512-iYvUFe9/tIXNI1FyDCQYhkwJI5M9htqeCGfdZ2LiR+ZqVQE6KAH2+qUPdXixjMPUL36LdpVIBTNhxstx5RRhEw==}
dependencies:
@@ -9487,6 +9542,25 @@ packages:
resolution: {integrity: sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q==}
dev: false
+ /@vue/language-core@1.8.21(typescript@5.2.2):
+ resolution: {integrity: sha512-dKQJc1xfWIZfv6BeXyxz3SSNrC7npJpDIN/VOb1rodAm4o247TElrXOHYAJdV9x1KilaEUo3YbnQE+WA3vQwMw==}
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+ dependencies:
+ '@volar/language-core': 1.10.10
+ '@volar/source-map': 1.10.10
+ '@vue/compiler-dom': 3.3.0
+ '@vue/shared': 3.3.0
+ computeds: 0.0.1
+ minimatch: 9.0.3
+ muggle-string: 0.3.1
+ typescript: 5.2.2
+ vue-template-compiler: 2.7.15
+ dev: true
+
/@vue/reactivity-transform@3.3.0:
resolution: {integrity: sha512-Pli2ClOXOEMG2AExCfUwiPQQo7U7zcRlnZLb6FI9ns/nEiQ9KLJJYD3wAuJHSx0VXLhACaINd/1VbMeKfa8GhQ==}
dependencies:
@@ -12017,6 +12091,10 @@ packages:
transitivePeerDependencies:
- supports-color
+ /computeds@0.0.1:
+ resolution: {integrity: sha512-7CEBgcMjVmitjYo5q8JTJVra6X5mQ20uTThdK+0kR7UEaDrAWEQcRiBtWJzga4eRpP6afNwwLsX2SET2JhVB1Q==}
+ dev: true
+
/concat-map@0.0.1:
resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
@@ -12829,6 +12907,10 @@ packages:
resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==}
dev: false
+ /de-indent@1.0.2:
+ resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
+ dev: true
+
/debug@2.6.9(supports-color@6.1.0):
resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
peerDependencies:
@@ -15968,7 +16050,6 @@ packages:
/he@1.2.0:
resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
hasBin: true
- dev: false
/headers-polyfill@3.3.0:
resolution: {integrity: sha512-5e57etwBpNcDc0b6KCVWEh/Ro063OxPvzVimUdM0/tsYM/T7Hfy3kknIGj78SFTOhNd8AZY41U8mOHoO4LzmIQ==}
@@ -19971,6 +20052,10 @@ packages:
- supports-color
dev: false
+ /muggle-string@0.3.1:
+ resolution: {integrity: sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==}
+ dev: true
+
/multicast-dns-service-types@1.1.0:
resolution: {integrity: sha512-cnAsSVxIDsYt0v7HmC0hWZFwwXSh+E6PgCrREDuN/EsjgLwA5XRmlMHhSiDPrt6HxY1gTivEa/Zh7GtODoLevQ==}
dev: false
@@ -20960,7 +21045,6 @@ packages:
/path-browserify@1.0.1:
resolution: {integrity: sha512-b7uo2UCUOYZcnF/3ID0lulOJi/bafxa1xPe7ZPsammBSpjSWQkjNxlt635YGS2MiR9GjvuXCtz2emr3jbsz98g==}
- dev: false
/path-dirname@1.0.2:
resolution: {integrity: sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==}
@@ -26968,6 +27052,25 @@ packages:
vue: 3.3.0
dev: false
+ /vue-template-compiler@2.7.15:
+ resolution: {integrity: sha512-yQxjxMptBL7UAog00O8sANud99C6wJF+7kgbcwqkvA38vCGF7HWE66w0ZFnS/kX5gSoJr/PQ4/oS3Ne2pW37Og==}
+ dependencies:
+ de-indent: 1.0.2
+ he: 1.2.0
+ dev: true
+
+ /vue-tsc@1.8.21(typescript@5.2.2):
+ resolution: {integrity: sha512-gc9e+opdeF0zKixaadXT5v2s+x+77oqpuza/vwqDhdDyEeLZUOmZaVeb9noZpkdhFaLq7t7ils/7lFU8E/Hgew==}
+ hasBin: true
+ peerDependencies:
+ typescript: '*'
+ dependencies:
+ '@volar/typescript': 1.10.10
+ '@vue/language-core': 1.8.21(typescript@5.2.2)
+ semver: 7.5.4
+ typescript: 5.2.2
+ dev: true
+
/vue@2.6.0:
resolution: {integrity: sha512-QSKHpmV17wqDmS5jVCc8X9LyTcCCQP4M1RTRpLBO+hRveePduJaGz1FGjVpRVcE6cKYbhsooz6RW7GWLGNjKGg==}
dev: true
diff --git a/scripts/config.js b/scripts/config.js
index acd8d1ae95..12d32a7700 100644
--- a/scripts/config.js
+++ b/scripts/config.js
@@ -98,6 +98,11 @@ export const packages = [
packageDir: 'packages/vue-query',
entries: ['main', 'module', 'types'],
},
+ {
+ name: '@tanstack/vue-query-devtools',
+ packageDir: 'packages/vue-query-devtools',
+ entries: ['main', 'module', 'types'],
+ },
]
/**