TanStack Table version
v9.0.0-beta.17
Framework/Library version
React v19.2.7
Describe the bug and the steps to reproduce it
When using the new composable tables API with createTableHook and registered tableComponents, a component can throw during Vite HMR even though it is rendered inside <table.AppTable>.
The runtime error is:
`useTableContext` must be used within an `AppTable` component. Make sure your component is wrapped with `<table.AppTable>...</table.AppTable>`.
This happens with the same structure shown/recommended by the composable tables example: the hook module creates the table hook and imports registered table components, while those table components import useTableContext from the hook module.
This creates a circular dependency:
hooks.ts
imports TableComponent from table-components.tsx
table-components.tsx
imports useTableContext from hooks.ts
The app works after a full reload, but after HMR/Fast Refresh the registered component may read from a different/stale context identity than the one provided by <table.AppTable>.
Minimal reproduction
// hooks.ts
import { createTableHook } from '@tanstack/react-table'
import { TableComponent } from './table-components'
export const {
useAppTable,
useTableContext,
} = createTableHook({
features,
tableComponents: {
TableComponent,
},
})
// table-components.tsx
import { useTableContext } from './hooks'
export function TableComponent() {
const table = useTableContext()
return <div />
}
// app.tsx
import { useAppTable } from './hooks'
function App() {
const table = useAppTable({
// table options
})
return (
<table.AppTable>
<table.TableComponent />
</table.AppTable>
)
}
Steps to reproduce
- Create a Vite React app.
- Use
@tanstack/react-table@beta.
- Set up
createTableHook with tableComponents.
- In a registered component, call
useTableContext() imported from the same hook module that imports the component.
- Render the component inside
<table.AppTable>.
- Edit/save either the registered component file or the hook file so that Vite HMR runs.
- Observe that
useTableContext() may throw even though the component is still rendered inside <table.AppTable>.
Expected behavior
Registered table components should be safe to use with HMR when following the documented/recommended pattern.
This should work reliably:
<table.AppTable>
<table.TableComponent />
</table.AppTable>
and inside TableComponent:
const table = useTableContext()
Actual behavior
After HMR, useTableContext() sometimes throws as if there was no provider:
useTableContext must be used within an AppTable component.
A full page reload fixes the issue.
Why this is hard to avoid with the current API
The current API returns useTableContext from createTableHook:
export const {
useAppTable,
useTableContext,
} = createTableHook({
features,
tableComponents,
})
But tableComponents are often defined in a separate module, and the docs/comments suggest using useTableContext() inside those components.
That naturally leads to this circular dependency:
hooks.ts -> table-components.tsx -> hooks.ts
Suggested API
TanStack Form already solves a very similar problem by separating context creation from hook creation:
// context.ts
export const {
fieldContext,
formContext,
useFieldContext,
useFormContext,
} = createFormHookContexts()
// hooks.ts
export const {
useAppForm,
} = createFormHook({
fieldComponents,
formComponents,
fieldContext,
formContext,
})
It would be very helpful if TanStack Table had a similar API, for example:
// table-features.ts
export const features = tableFeatures({ ... })
// table-context.ts
import { createTableHookContexts } from '@tanstack/react-table'
import { features } from './table-features'
export const {
tableContext,
cellContext,
headerContext,
useTableContext,
useCellContext,
useHeaderContext,
} = createTableHookContexts({
features,
})
// table-components.tsx
import { useTableContext } from './table-context'
export function TableComponent() {
const table = useTableContext()
return <div />
}
// hooks.ts
import { createTableHook } from '@tanstack/react-table'
import { features } from './table-features'
import { tableContext, cellContext, headerContext } from './table-context'
import { TableComponent } from './table-components'
export const {
useAppTable,
createAppColumnHelper,
} = createTableHook({
features,
tableComponents: {
TableComponent,
},
tableContext,
cellContext,
headerContext,
})
This would avoid the circular import entirely:
hooks.ts -> table-components.tsx -> table-context.ts
hooks.ts -> table-context.ts
hooks.ts -> table-features.ts
instead of:
hooks.ts -> table-components.tsx -> hooks.ts
The important part is that useTableContext, useCellContext, useHeaderContext, etc. would still be typed according to the same TFeatures used by createTableHook.
The exact API shape may differ, but an official HMR-safe pattern that preserves feature-specific context hook types would be very useful.
Your Minimal, Reproducible Example - (Sandbox Highly Recommended)
https://stackblitz.com/github/tanstack/table/tree/beta/examples/react/composable-tables
Screenshots or Videos (Optional)
No response
Do you intend to try to help solve this bug with your own PR?
None
Terms & Code of Conduct
TanStack Table version
v9.0.0-beta.17
Framework/Library version
React v19.2.7
Describe the bug and the steps to reproduce it
When using the new composable tables API with
createTableHookand registeredtableComponents, a component can throw during Vite HMR even though it is rendered inside<table.AppTable>.The runtime error is:
This happens with the same structure shown/recommended by the composable tables example: the hook module creates the table hook and imports registered table components, while those table components import
useTableContextfrom the hook module.This creates a circular dependency:
The app works after a full reload, but after HMR/Fast Refresh the registered component may read from a different/stale context identity than the one provided by
<table.AppTable>.Minimal reproduction
Steps to reproduce
@tanstack/react-table@beta.createTableHookwithtableComponents.useTableContext()imported from the same hook module that imports the component.<table.AppTable>.useTableContext()may throw even though the component is still rendered inside<table.AppTable>.Expected behavior
Registered table components should be safe to use with HMR when following the documented/recommended pattern.
This should work reliably:
and inside
TableComponent:Actual behavior
After HMR,
useTableContext()sometimes throws as if there was no provider:A full page reload fixes the issue.
Why this is hard to avoid with the current API
The current API returns
useTableContextfromcreateTableHook:But
tableComponentsare often defined in a separate module, and the docs/comments suggest usinguseTableContext()inside those components.That naturally leads to this circular dependency:
Suggested API
TanStack Form already solves a very similar problem by separating context creation from hook creation:
It would be very helpful if TanStack Table had a similar API, for example:
This would avoid the circular import entirely:
instead of:
The important part is that
useTableContext,useCellContext,useHeaderContext, etc. would still be typed according to the sameTFeaturesused bycreateTableHook.The exact API shape may differ, but an official HMR-safe pattern that preserves feature-specific context hook types would be very useful.
Your Minimal, Reproducible Example - (Sandbox Highly Recommended)
https://stackblitz.com/github/tanstack/table/tree/beta/examples/react/composable-tables
Screenshots or Videos (Optional)
No response
Do you intend to try to help solve this bug with your own PR?
None
Terms & Code of Conduct