diff --git a/.changeset/nullable-collection-getter.md b/.changeset/nullable-collection-getter.md new file mode 100644 index 000000000..1ca4ce3d0 --- /dev/null +++ b/.changeset/nullable-collection-getter.md @@ -0,0 +1,5 @@ +--- +'@tanstack/svelte-db': patch +--- + +Fix nullable collection getters in useLiveQuery causing errors during SSR diff --git a/packages/svelte-db/src/useLiveQuery.svelte.ts b/packages/svelte-db/src/useLiveQuery.svelte.ts index 76dedd3c7..93502085b 100644 --- a/packages/svelte-db/src/useLiveQuery.svelte.ts +++ b/packages/svelte-db/src/useLiveQuery.svelte.ts @@ -350,6 +350,9 @@ export function useLiveQuery( startSync: true, }) } else { + if (unwrappedParam === null || unwrappedParam === undefined) { + return null + } return createLiveQueryCollection({ ...unwrappedParam, startSync: true, diff --git a/packages/svelte-db/tests/useLiveQuery.svelte.test.ts b/packages/svelte-db/tests/useLiveQuery.svelte.test.ts index cb16e8579..5b7e0047c 100644 --- a/packages/svelte-db/tests/useLiveQuery.svelte.test.ts +++ b/packages/svelte-db/tests/useLiveQuery.svelte.test.ts @@ -837,6 +837,55 @@ describe(`Query Collections`, () => { }) }) + it(`should handle nullable collection getter returning null`, () => { + const collection = createCollection( + mockSyncCollectionOptions({ + id: `nullable-getter-test`, + getKey: (person: Person) => person.id, + initialData: initialPersons, + }), + ) + + cleanup = $effect.root(() => { + // Create a live query collection + const liveQueryCollection = createLiveQueryCollection({ + query: (q) => + q + .from({ persons: collection }) + .where(({ persons }) => gt(persons.age, 30)) + .select(({ persons }) => ({ + id: persons.id, + name: persons.name, + })), + startSync: true, + }) + + // Simulate SSR scenario: collection getter initially returns null + let currentCollection = $state(null) + + // This should not throw when the getter returns null + const queryResult = useLiveQuery(() => currentCollection) + + flushSync() + + // When collection is null, should return empty/idle state + expect(queryResult.state.size).toBe(0) + expect(queryResult.data).toEqual([]) + + // Now set the actual collection + currentCollection = liveQueryCollection + + flushSync() + + // Should now have data from the collection + expect(queryResult.state.size).toBe(1) + expect(queryResult.state.get(`3`)).toMatchObject({ + id: `3`, + name: `John Smith`, + }) + }) + }) + describe(`isReady property`, () => { it(`should be false initially and true after collection is ready`, () => { let beginFn: (() => void) | undefined