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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions .changeset/svelte-findone-support.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
'@tanstack/svelte-db': patch
---

Add `findOne()` / `SingleResult` support to `useLiveQuery` hook.

When using `.findOne()` in a query, the `data` property is now correctly typed as `T | undefined` instead of `Array<T>`, matching the React implementation.

**Example:**

```ts
const query = useLiveQuery((q) =>
q
.from({ users: usersCollection })
.where(({ users }) => eq(users.id, userId))
.findOne(),
)

// query.data is now typed as User | undefined (not User[])
```

This works with all query patterns:

- Query functions: `useLiveQuery((q) => q.from(...).findOne())`
- Config objects: `useLiveQuery({ query: (q) => q.from(...).findOne() })`
- Pre-created collections with `SingleResult`
152 changes: 139 additions & 13 deletions docs/framework/svelte/reference/functions/useLiveQuery.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ title: useLiveQuery
## Call Signature

```ts
function useLiveQuery<TContext>(queryFn, deps?): UseLiveQueryReturn<{ [K in string | number | symbol]: (TContext["result"] extends object ? any[any] : TContext["hasJoins"] extends true ? TContext["schema"] : TContext["schema"][TContext["fromSourceName"]])[K] }>;
function useLiveQuery<TContext>(queryFn, deps?): UseLiveQueryReturn<{ [K in string | number | symbol]: (TContext["result"] extends object ? any[any] : TContext["hasJoins"] extends true ? TContext["schema"] : TContext["schema"][TContext["fromSourceName"]])[K] }, InferResultType<TContext>>;
```

Defined in: [useLiveQuery.svelte.ts:155](https://github.com/TanStack/db/blob/main/packages/svelte-db/src/useLiveQuery.svelte.ts#L155)
Defined in: [useLiveQuery.svelte.ts:160](https://github.com/TanStack/db/blob/main/packages/svelte-db/src/useLiveQuery.svelte.ts#L160)

Create a live query using a query function

Expand All @@ -37,7 +37,7 @@ Array of reactive dependencies that trigger query re-execution when changed

### Returns

[`UseLiveQueryReturn`](../interfaces/UseLiveQueryReturn.md)\<\{ \[K in string \| number \| symbol\]: (TContext\["result"\] extends object ? any\[any\] : TContext\["hasJoins"\] extends true ? TContext\["schema"\] : TContext\["schema"\]\[TContext\["fromSourceName"\]\])\[K\] \}\>
[`UseLiveQueryReturn`](../interfaces/UseLiveQueryReturn.md)\<\{ \[K in string \| number \| symbol\]: (TContext\["result"\] extends object ? any\[any\] : TContext\["hasJoins"\] extends true ? TContext\["schema"\] : TContext\["schema"\]\[TContext\["fromSourceName"\]\])\[K\] \}, `InferResultType`\<`TContext`\>\>

Reactive object with query data, state, and status information

Expand Down Expand Up @@ -134,10 +134,10 @@ const todosQuery = useLiveQuery((q) =>
## Call Signature

```ts
function useLiveQuery<TContext>(queryFn, deps?): UseLiveQueryReturn<{ [K in string | number | symbol]: (TContext["result"] extends object ? any[any] : TContext["hasJoins"] extends true ? TContext["schema"] : TContext["schema"][TContext["fromSourceName"]])[K] }>;
function useLiveQuery<TContext>(queryFn, deps?): UseLiveQueryReturn<{ [K in string | number | symbol]: (TContext["result"] extends object ? any[any] : TContext["hasJoins"] extends true ? TContext["schema"] : TContext["schema"][TContext["fromSourceName"]])[K] }, InferResultType<TContext> | undefined>;
```

Defined in: [useLiveQuery.svelte.ts:161](https://github.com/TanStack/db/blob/main/packages/svelte-db/src/useLiveQuery.svelte.ts#L161)
Defined in: [useLiveQuery.svelte.ts:166](https://github.com/TanStack/db/blob/main/packages/svelte-db/src/useLiveQuery.svelte.ts#L166)

Create a live query using a query function

Expand All @@ -163,7 +163,7 @@ Array of reactive dependencies that trigger query re-execution when changed

### Returns

[`UseLiveQueryReturn`](../interfaces/UseLiveQueryReturn.md)\<\{ \[K in string \| number \| symbol\]: (TContext\["result"\] extends object ? any\[any\] : TContext\["hasJoins"\] extends true ? TContext\["schema"\] : TContext\["schema"\]\[TContext\["fromSourceName"\]\])\[K\] \}\>
[`UseLiveQueryReturn`](../interfaces/UseLiveQueryReturn.md)\<\{ \[K in string \| number \| symbol\]: (TContext\["result"\] extends object ? any\[any\] : TContext\["hasJoins"\] extends true ? TContext\["schema"\] : TContext\["schema"\]\[TContext\["fromSourceName"\]\])\[K\] \}, `InferResultType`\<`TContext`\> \| `undefined`\>

Reactive object with query data, state, and status information

Expand Down Expand Up @@ -260,10 +260,10 @@ const todosQuery = useLiveQuery((q) =>
## Call Signature

```ts
function useLiveQuery<TContext>(config, deps?): UseLiveQueryReturn<{ [K in string | number | symbol]: (TContext["result"] extends object ? any[any] : TContext["hasJoins"] extends true ? TContext["schema"] : TContext["schema"][TContext["fromSourceName"]])[K] }>;
function useLiveQuery<TContext>(config, deps?): UseLiveQueryReturn<{ [K in string | number | symbol]: (TContext["result"] extends object ? any[any] : TContext["hasJoins"] extends true ? TContext["schema"] : TContext["schema"][TContext["fromSourceName"]])[K] }, InferResultType<TContext>>;
```

Defined in: [useLiveQuery.svelte.ts:206](https://github.com/TanStack/db/blob/main/packages/svelte-db/src/useLiveQuery.svelte.ts#L206)
Defined in: [useLiveQuery.svelte.ts:214](https://github.com/TanStack/db/blob/main/packages/svelte-db/src/useLiveQuery.svelte.ts#L214)

Create a live query using configuration object

Expand All @@ -289,7 +289,7 @@ Array of reactive dependencies that trigger query re-execution when changed

### Returns

[`UseLiveQueryReturn`](../interfaces/UseLiveQueryReturn.md)\<\{ \[K in string \| number \| symbol\]: (TContext\["result"\] extends object ? any\[any\] : TContext\["hasJoins"\] extends true ? TContext\["schema"\] : TContext\["schema"\]\[TContext\["fromSourceName"\]\])\[K\] \}\>
[`UseLiveQueryReturn`](../interfaces/UseLiveQueryReturn.md)\<\{ \[K in string \| number \| symbol\]: (TContext\["result"\] extends object ? any\[any\] : TContext\["hasJoins"\] extends true ? TContext\["schema"\] : TContext\["schema"\]\[TContext\["fromSourceName"\]\])\[K\] \}, `InferResultType`\<`TContext`\>\>

Reactive object with query data, state, and status information

Expand Down Expand Up @@ -333,10 +333,10 @@ const itemsQuery = useLiveQuery({
## Call Signature

```ts
function useLiveQuery<TResult, TKey, TUtils>(liveQueryCollection): UseLiveQueryReturnWithCollection<TResult, TKey, TUtils>;
function useLiveQuery<TResult, TKey, TUtils>(liveQueryCollection): UseLiveQueryReturnWithCollection<TResult, TKey, TUtils, TResult[]>;
```

Defined in: [useLiveQuery.svelte.ts:255](https://github.com/TanStack/db/blob/main/packages/svelte-db/src/useLiveQuery.svelte.ts#L255)
Defined in: [useLiveQuery.svelte.ts:263](https://github.com/TanStack/db/blob/main/packages/svelte-db/src/useLiveQuery.svelte.ts#L263)

Subscribe to an existing query collection (can be reactive)

Expand All @@ -358,13 +358,13 @@ Subscribe to an existing query collection (can be reactive)

#### liveQueryCollection

`MaybeGetter`\<`Collection`\<`TResult`, `TKey`, `TUtils`, `StandardSchemaV1`\<`unknown`, `unknown`\>, `TResult`\>\>
`MaybeGetter`\<`Collection`\<`TResult`, `TKey`, `TUtils`, `StandardSchemaV1`\<`unknown`, `unknown`\>, `TResult`\> & `NonSingleResult`\>

Pre-created query collection to subscribe to (can be a getter)

### Returns

[`UseLiveQueryReturnWithCollection`](../interfaces/UseLiveQueryReturnWithCollection.md)\<`TResult`, `TKey`, `TUtils`\>
[`UseLiveQueryReturnWithCollection`](../interfaces/UseLiveQueryReturnWithCollection.md)\<`TResult`, `TKey`, `TUtils`, `TResult`[]\>

Reactive object with query data, state, and status information

Expand Down Expand Up @@ -412,3 +412,129 @@ const queryResult = useLiveQuery(sharedQuery)
// {/each}
// {/if}
```

## Call Signature

```ts
function useLiveQuery<TResult, TKey, TUtils>(liveQueryCollection): UseLiveQueryReturnWithCollection<TResult, TKey, TUtils, TResult | undefined>;
```

Defined in: [useLiveQuery.svelte.ts:274](https://github.com/TanStack/db/blob/main/packages/svelte-db/src/useLiveQuery.svelte.ts#L274)

Create a live query using a query function

### Type Parameters

#### TResult

`TResult` *extends* `object`

#### TKey

`TKey` *extends* `string` \| `number`

#### TUtils

`TUtils` *extends* `Record`\<`string`, `any`\>

### Parameters

#### liveQueryCollection

`MaybeGetter`\<`Collection`\<`TResult`, `TKey`, `TUtils`, `StandardSchemaV1`\<`unknown`, `unknown`\>, `TResult`\> & `SingleResult`\>

### Returns

[`UseLiveQueryReturnWithCollection`](../interfaces/UseLiveQueryReturnWithCollection.md)\<`TResult`, `TKey`, `TUtils`, `TResult` \| `undefined`\>

Reactive object with query data, state, and status information

### Remarks

**IMPORTANT - Destructuring in Svelte 5:**
Direct destructuring breaks reactivity. To destructure, wrap with `$derived`:

❌ **Incorrect** - Loses reactivity:
```ts
const { data, isLoading } = useLiveQuery(...)
```

✅ **Correct** - Maintains reactivity:
```ts
// Option 1: Use dot notation (recommended)
const query = useLiveQuery(...)
// Access: query.data, query.isLoading

// Option 2: Wrap with $derived for destructuring
const query = useLiveQuery(...)
const { data, isLoading } = $derived(query)
```

This is a fundamental Svelte 5 limitation, not a library bug. See:
https://github.com/sveltejs/svelte/issues/11002

### Examples

```ts
// Basic query with object syntax (recommended pattern)
const todosQuery = useLiveQuery((q) =>
q.from({ todos: todosCollection })
.where(({ todos }) => eq(todos.completed, false))
.select(({ todos }) => ({ id: todos.id, text: todos.text }))
)
// Access via: todosQuery.data, todosQuery.isLoading, etc.
```

```ts
// With reactive dependencies
let minPriority = $state(5)
const todosQuery = useLiveQuery(
(q) => q.from({ todos: todosCollection })
.where(({ todos }) => gt(todos.priority, minPriority)),
[() => minPriority] // Re-run when minPriority changes
)
```

```ts
// Destructuring with $derived (if needed)
const query = useLiveQuery((q) =>
q.from({ todos: todosCollection })
)
const { data, isLoading, isError } = $derived(query)
// Now data, isLoading, and isError maintain reactivity
```

```ts
// Join pattern
const issuesQuery = useLiveQuery((q) =>
q.from({ issues: issueCollection })
.join({ persons: personCollection }, ({ issues, persons }) =>
eq(issues.userId, persons.id)
)
.select(({ issues, persons }) => ({
id: issues.id,
title: issues.title,
userName: persons.name
}))
)
```

```ts
// Handle loading and error states in template
const todosQuery = useLiveQuery((q) =>
q.from({ todos: todoCollection })
)

// In template:
// {#if todosQuery.isLoading}
// <div>Loading...</div>
// {:else if todosQuery.isError}
// <div>Error: {todosQuery.status}</div>
// {:else}
// <ul>
// {#each todosQuery.data as todo (todo.id)}
// <li>{todo.text}</li>
// {/each}
// </ul>
// {/if}
```
30 changes: 17 additions & 13 deletions docs/framework/svelte/reference/interfaces/UseLiveQueryReturn.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ id: UseLiveQueryReturn
title: UseLiveQueryReturn
---

# Interface: UseLiveQueryReturn\<T\>
# Interface: UseLiveQueryReturn\<T, TData\>

Defined in: [useLiveQuery.svelte.ts:29](https://github.com/TanStack/db/blob/main/packages/svelte-db/src/useLiveQuery.svelte.ts#L29)
Defined in: [useLiveQuery.svelte.ts:33](https://github.com/TanStack/db/blob/main/packages/svelte-db/src/useLiveQuery.svelte.ts#L33)

Return type for useLiveQuery hook

Expand All @@ -15,6 +15,10 @@ Return type for useLiveQuery hook

`T` *extends* `object`

### TData

`TData` = `T`[]

## Properties

### collection
Expand All @@ -24,7 +28,7 @@ collection: Collection<T, string | number, {
}>;
```

Defined in: [useLiveQuery.svelte.ts:32](https://github.com/TanStack/db/blob/main/packages/svelte-db/src/useLiveQuery.svelte.ts#L32)
Defined in: [useLiveQuery.svelte.ts:36](https://github.com/TanStack/db/blob/main/packages/svelte-db/src/useLiveQuery.svelte.ts#L36)

The underlying query collection instance

Expand All @@ -33,12 +37,12 @@ The underlying query collection instance
### data

```ts
data: T[];
data: TData;
```

Defined in: [useLiveQuery.svelte.ts:31](https://github.com/TanStack/db/blob/main/packages/svelte-db/src/useLiveQuery.svelte.ts#L31)
Defined in: [useLiveQuery.svelte.ts:35](https://github.com/TanStack/db/blob/main/packages/svelte-db/src/useLiveQuery.svelte.ts#L35)

Reactive array of query results in order
Reactive array of query results in order, or single item when using findOne()

***

Expand All @@ -48,7 +52,7 @@ Reactive array of query results in order
isCleanedUp: boolean;
```

Defined in: [useLiveQuery.svelte.ts:38](https://github.com/TanStack/db/blob/main/packages/svelte-db/src/useLiveQuery.svelte.ts#L38)
Defined in: [useLiveQuery.svelte.ts:42](https://github.com/TanStack/db/blob/main/packages/svelte-db/src/useLiveQuery.svelte.ts#L42)

True when query has been cleaned up

Expand All @@ -60,7 +64,7 @@ True when query has been cleaned up
isError: boolean;
```

Defined in: [useLiveQuery.svelte.ts:37](https://github.com/TanStack/db/blob/main/packages/svelte-db/src/useLiveQuery.svelte.ts#L37)
Defined in: [useLiveQuery.svelte.ts:41](https://github.com/TanStack/db/blob/main/packages/svelte-db/src/useLiveQuery.svelte.ts#L41)

True when query encountered an error

Expand All @@ -72,7 +76,7 @@ True when query encountered an error
isIdle: boolean;
```

Defined in: [useLiveQuery.svelte.ts:36](https://github.com/TanStack/db/blob/main/packages/svelte-db/src/useLiveQuery.svelte.ts#L36)
Defined in: [useLiveQuery.svelte.ts:40](https://github.com/TanStack/db/blob/main/packages/svelte-db/src/useLiveQuery.svelte.ts#L40)

True when query hasn't started yet

Expand All @@ -84,7 +88,7 @@ True when query hasn't started yet
isLoading: boolean;
```

Defined in: [useLiveQuery.svelte.ts:34](https://github.com/TanStack/db/blob/main/packages/svelte-db/src/useLiveQuery.svelte.ts#L34)
Defined in: [useLiveQuery.svelte.ts:38](https://github.com/TanStack/db/blob/main/packages/svelte-db/src/useLiveQuery.svelte.ts#L38)

True while initial query data is loading

Expand All @@ -96,7 +100,7 @@ True while initial query data is loading
isReady: boolean;
```

Defined in: [useLiveQuery.svelte.ts:35](https://github.com/TanStack/db/blob/main/packages/svelte-db/src/useLiveQuery.svelte.ts#L35)
Defined in: [useLiveQuery.svelte.ts:39](https://github.com/TanStack/db/blob/main/packages/svelte-db/src/useLiveQuery.svelte.ts#L39)

True when query has received first data and is ready

Expand All @@ -108,7 +112,7 @@ True when query has received first data and is ready
state: Map<string | number, T>;
```

Defined in: [useLiveQuery.svelte.ts:30](https://github.com/TanStack/db/blob/main/packages/svelte-db/src/useLiveQuery.svelte.ts#L30)
Defined in: [useLiveQuery.svelte.ts:34](https://github.com/TanStack/db/blob/main/packages/svelte-db/src/useLiveQuery.svelte.ts#L34)

Reactive Map of query results (key → item)

Expand All @@ -120,6 +124,6 @@ Reactive Map of query results (key → item)
status: CollectionStatus;
```

Defined in: [useLiveQuery.svelte.ts:33](https://github.com/TanStack/db/blob/main/packages/svelte-db/src/useLiveQuery.svelte.ts#L33)
Defined in: [useLiveQuery.svelte.ts:37](https://github.com/TanStack/db/blob/main/packages/svelte-db/src/useLiveQuery.svelte.ts#L37)

Current query status
Loading
Loading