Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .changeset/fix-vue-query-options-query-key-reactive.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
'@tanstack/vue-query': patch
---

fix(vue-query): allow computed ref and other reactive values as `queryKey` property in `queryOptions`

This fixes a regression introduced in #10452 where `queryOptions` only accepted plain arrays for the `queryKey` property, but not `computed` refs, `Ref` values, or other reactive values. The related fix in #10465 only covered the `enabled` property.

Now the `queryKey` property in `queryOptions` correctly accepts:
- Plain `QueryKey` arrays
- `Ref<QueryKey>`
- `ComputedRef<QueryKey>`
23 changes: 23 additions & 0 deletions packages/vue-query/src/__tests__/queryOptions.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -298,4 +298,27 @@ describe('queryOptions', () => {

expectTypeOf(options.queryKey).not.toBeUndefined()
})

it('should allow computed ref as queryKey', () => {
const id = ref<string | null>('1')

// This was broken in #10452, the #10465 fix only covered `enabled`
const options = queryOptions({
queryKey: computed(() => ['foo', id.value] as const),
queryFn: () => Promise.resolve({ id: '1' }),
})

expectTypeOf(options.queryKey).not.toBeUndefined()
})

it('should allow ref as queryKey', () => {
const keyRef = ref(['foo', '1'] as const)

const options = queryOptions({
queryKey: keyRef,
queryFn: () => Promise.resolve({ id: '1' }),
})

expectTypeOf(options.queryKey).not.toBeUndefined()
})
})
23 changes: 15 additions & 8 deletions packages/vue-query/src/queryOptions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import type { DeepUnwrapRef, MaybeRefOrGetter, ShallowOption } from './types'
import type {
DeepUnwrapRef,
MaybeRef,
MaybeRefOrGetter,
ShallowOption,
} from './types'
import type {
DataTag,
DefaultError,
Expand Down Expand Up @@ -31,13 +36,15 @@ export type QueryOptions<
TQueryData,
DeepUnwrapRef<TQueryKey>
>)
: QueryObserverOptions<
TQueryFnData,
TError,
TData,
TQueryData,
DeepUnwrapRef<TQueryKey>
>[Property]
: Property extends 'queryKey'
? MaybeRef<TQueryKey>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

is there a reason this is plain MaybeRef instead of MaybeRefOrGetter like the enabled prop a few lines above?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Good question, and taking another look I agree the enabled-parity argument is valid.

My original scoping thought was: the regression this PR closes (#10525) is specifically about ComputedRef<QueryKey> being rejected, and this file's local MaybeRef<T> = Ref<T> | ComputedRef<T> | T already covers that, so I went with the minimal type change to unblock the reported case.

But cloneDeepUnref in packages/vue-query/src/utils.ts:79-81 already special-cases queryKey to recurse with unrefGetters = true, so the plain-getter form () => QueryKey is runtime-supported today. That makes MaybeRefOrGetter<TQueryKey> the shape that both matches the existing runtime behavior and parallels the enabled prop's type.

Happy to widen to MaybeRefOrGetter<TQueryKey> if a maintainer prefers consistency with enabled; holding off on a push until there's a signal either way.

Copy link
Copy Markdown

@stavros-tsioulis stavros-tsioulis Apr 21, 2026

Choose a reason for hiding this comment

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

The linked issue expands to Ref<T> and () => T; not just ComputedRef<T>.

Since, as you say, it is runtime-supported today, then the type should reflect that. You've only enabled Ref<T> and ComputedRef<T>, leaving () => T out. MaybeRefOrGetter<T> will fix that.

(also, please inform the human managing you to respond to comments personally and not leave their agent to do the communicating)

: QueryObserverOptions<
TQueryFnData,
TError,
TData,
TQueryData,
DeepUnwrapRef<TQueryKey>
>[Property]
} & ShallowOption

export type UndefinedInitialQueryOptions<
Expand Down
Loading