From f2ce8fa84e50049a158593b10103aa6a78db1dff Mon Sep 17 00:00:00 2001 From: Rutvij Patel Date: Thu, 8 Oct 2026 01:34:04 +0530 Subject: [PATCH] fix(svelte-table): set App* contexts in their own components --- .changeset/svelte-app-wrapper-context.md | 5 +++ packages/svelte-table/src/AppCell.svelte | 7 ++++ packages/svelte-table/src/AppHeader.svelte | 7 ++++ packages/svelte-table/src/AppTable.svelte | 10 ++++- .../src/createTableHook.svelte.ts | 15 +++---- .../tests/fixtures/HookContextProbe.svelte | 14 +++++++ .../tests/fixtures/HookContextScope.svelte | 40 +++++++++++++++++++ packages/svelte-table/tests/rendering.test.ts | 11 +++++ 8 files changed, 98 insertions(+), 11 deletions(-) create mode 100644 .changeset/svelte-app-wrapper-context.md create mode 100644 packages/svelte-table/tests/fixtures/HookContextProbe.svelte create mode 100644 packages/svelte-table/tests/fixtures/HookContextScope.svelte diff --git a/.changeset/svelte-app-wrapper-context.md b/.changeset/svelte-app-wrapper-context.md new file mode 100644 index 0000000000..0530312988 --- /dev/null +++ b/.changeset/svelte-app-wrapper-context.md @@ -0,0 +1,5 @@ +--- +'@tanstack/svelte-table': patch +--- + +fix(svelte-table): set the `AppTable`/`AppCell`/`AppHeader`/`AppFooter` contexts in their own components, so cells created later (e.g. when showing a column) no longer throw `set_context_after_init` in async mode diff --git a/packages/svelte-table/src/AppCell.svelte b/packages/svelte-table/src/AppCell.svelte index 4818989dbe..db2c670968 100644 --- a/packages/svelte-table/src/AppCell.svelte +++ b/packages/svelte-table/src/AppCell.svelte @@ -1,4 +1,6 @@ {@render children?.(Object.assign(cell, cellComponents))} diff --git a/packages/svelte-table/src/AppHeader.svelte b/packages/svelte-table/src/AppHeader.svelte index fd7d091d09..99ba4f5cd8 100644 --- a/packages/svelte-table/src/AppHeader.svelte +++ b/packages/svelte-table/src/AppHeader.svelte @@ -1,4 +1,6 @@ {@render children?.(Object.assign(header, headerComponents))} diff --git a/packages/svelte-table/src/AppTable.svelte b/packages/svelte-table/src/AppTable.svelte index c335d17674..da697662c3 100644 --- a/packages/svelte-table/src/AppTable.svelte +++ b/packages/svelte-table/src/AppTable.svelte @@ -1,11 +1,19 @@ {@render children?.()} diff --git a/packages/svelte-table/src/createTableHook.svelte.ts b/packages/svelte-table/src/createTableHook.svelte.ts index a3f282f235..79ea7153bc 100644 --- a/packages/svelte-table/src/createTableHook.svelte.ts +++ b/packages/svelte-table/src/createTableHook.svelte.ts @@ -1,4 +1,4 @@ -import { getContext, setContext } from 'svelte' +import { getContext } from 'svelte' import { createColumnHelper as coreCreateColumnHelper } from '@tanstack/table-core' import { createTable } from './createTable.svelte' import { mergeObjects } from './merge-objects' @@ -647,17 +647,14 @@ export function createTableHook< } // Create wrapper components using the svelte-form (internal, props) => pattern. - // setContext is called in the closure — this runs during component - // initialization, so Svelte's context API works correctly. - // With keyed {#each} blocks, components are recreated on reorder, - // so context is always fresh. + // They only add props: each App*Svelte component sets its own context while + // it initializes. Calling setContext here would run before that component + // exists and write into the context of the component rendering it. const AppTable = ((internal: any, props: any) => { - setContext(tableContextKey, table) - return AppTableSvelte(internal, { ...props }) + return AppTableSvelte(internal, { ...props, table }) }) as Component<{ children: Snippet }> const AppCell = ((internal: any, { children, cell }: any) => { - setContext(cellContextKey, cell) return AppCellSvelte(internal, { cell, cellComponents: cellComponentsWithFlexRender, @@ -669,7 +666,6 @@ export function createTableHook< }> const AppHeader = ((internal: any, { children, header }: any) => { - setContext(headerContextKey, header) return AppHeaderSvelte(internal, { header, headerComponents: headerComponentsWithFlexRender, @@ -682,7 +678,6 @@ export function createTableHook< // AppFooter reuses AppHeaderSvelte (footers use Header type in table-core) const AppFooter = ((internal: any, { children, header }: any) => { - setContext(headerContextKey, header) return AppHeaderSvelte(internal, { header, headerComponents: headerComponentsWithFlexRender, diff --git a/packages/svelte-table/tests/fixtures/HookContextProbe.svelte b/packages/svelte-table/tests/fixtures/HookContextProbe.svelte new file mode 100644 index 0000000000..6b727d03cb --- /dev/null +++ b/packages/svelte-table/tests/fixtures/HookContextProbe.svelte @@ -0,0 +1,14 @@ + + +{visible.join(',')} diff --git a/packages/svelte-table/tests/fixtures/HookContextScope.svelte b/packages/svelte-table/tests/fixtures/HookContextScope.svelte new file mode 100644 index 0000000000..b869780b84 --- /dev/null +++ b/packages/svelte-table/tests/fixtures/HookContextScope.svelte @@ -0,0 +1,40 @@ + + + + + + + + {#snippet children(value)} + + {/snippet} + + + + {#snippet children(value)} + + {/snippet} + + + + {#snippet children(value)} + + {/snippet} + + + diff --git a/packages/svelte-table/tests/rendering.test.ts b/packages/svelte-table/tests/rendering.test.ts index fadb933e9d..85eacc86c1 100644 --- a/packages/svelte-table/tests/rendering.test.ts +++ b/packages/svelte-table/tests/rendering.test.ts @@ -6,6 +6,7 @@ import { stockFeatures } from '@tanstack/table-core' import { renderComponent } from '../src/render-component' import ContextFailure from './fixtures/ContextFailure.svelte' import FlexRenderHarness from './fixtures/FlexRenderHarness.svelte' +import HookContextScope from './fixtures/HookContextScope.svelte' import HookHarness from './fixtures/HookHarness.svelte' import RenderBadge from './fixtures/RenderBadge.svelte' import { hook } from './fixtures/hook-fixture' @@ -209,6 +210,16 @@ describe('createTableHook', () => { expect(outputText('Hook footer')).toBe('footer:title') }) + // https://github.com/TanStack/table/issues/6614 + test('wrapper contexts stay inside the wrappers', () => { + render(HookContextScope) + + expect(screen.getByText('table-component:row-1')).toBeTruthy() + expect(screen.getByText('cell-component:First')).toBeTruthy() + expect(screen.getAllByText('header-component:title')).toHaveLength(2) + expect(outputText('Contexts outside the wrappers')).toBe('') + }) + test.each([ ['useTableContext', () => hook.useTableContext()], ['useCellContext', () => hook.useCellContext()],