Author a TanStack Table v9 feature plugin across every FeatureMap and API installation surface: state, options, column definitions, table, column, row, cell, header, row-model functions/caches, defaults, prototypes, and table/row/column instance data lifecycles. Load for initTableInstanceData, resetTableInstanceData, constructTableAPIs, or reusable behavior not covered by built-ins, meta, or option composition.
68
83%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
This skill builds on core, table-features, and typescript. Prefer a built-in feature or typed tableMeta/columnMeta for application callbacks; create a feature only for reusable state or behavior that must augment Table objects.
Declaration-merge Plugins to register the feature key, then merge only the maps backed by runtime behavior:
| Feature map | Contribution |
|---|---|
TableState_FeatureMap | State slices in options, atoms, and store |
TableOptions_FeatureMap<TFeatures, TData> | Table options and change callbacks |
Table_FeatureMap<TFeatures, TData> | Public table APIs |
ColumnDef_FeatureMap<TFeatures, TData, TValue> | Column-definition options |
Column_FeatureMap<TFeatures, TData> | Public column APIs/data |
Row_FeatureMap<TFeatures, TData> | Public row APIs/data |
Cell_FeatureMap | Public cell APIs |
Header_FeatureMap | Public header APIs |
RowModelFns_FeatureMap<TFeatures, TData> | Advanced table._rowModelFns registries |
CachedRowModels_FeatureMap<TFeatures, TData> | Advanced table._rowModels cache getters |
assignTableAPIs installs singleton table methods. Use assignPrototypeAPIs inside assignColumnPrototype, assignRowPrototype, assignCellPrototype, or assignHeaderPrototype for shared object methods. Use initColumnInstanceData and initRowInstanceData only for per-instance fields.
Use initTableInstanceData for mutable, non-reactive data owned by one table. It runs once after options, state atoms, and the store exist, and every feature initialization finishes before any constructTableAPIs hook runs. Use resetTableInstanceData to clear transient instance data after internal atoms reset during table.reset(); it does not own state slices or externally controlled state. Keep constructTableAPIs exclusively for method assignment.
import type { TableFeature } from '@tanstack/table-core'
interface InteractionData {
_interactionHistory: Set<string>
}
const interactionData = (table: object): InteractionData =>
table as unknown as InteractionData
export const interactionFeature: TableFeature = {
initTableInstanceData: (table) => {
interactionData(table)._interactionHistory = new Set()
},
resetTableInstanceData: (table) => {
interactionData(table)._interactionHistory.clear()
},
}Initialization may allocate resources once while reset clears their transient contents. Do not rerun initialization during table.reset().
This single example shows every ordinary FeatureMap and API installation path. It names the two advanced row-model maps without enabling them because density does not own the row-model pipeline.
import {
assignPrototypeAPIs,
assignTableAPIs,
functionalUpdate,
makeStateUpdater,
tableFeatures,
type CellData,
type OnChangeFn,
type RowData,
type TableFeature,
type TableFeatures,
type Updater,
} from '@tanstack/table-core'
type Density = 'sm' | 'md'
interface DensityState {
density: Density
}
interface DensityOptions {
onDensityChange?: OnChangeFn<Density>
}
interface DensityColumnDef {
enableDensity?: boolean
}
interface DensityAPI {
getDensity: () => Density
}
interface DensityTable extends DensityAPI {
setDensity: (updater: Updater<Density>) => void
}
interface DensityColumn extends DensityAPI {
densityAtCreation?: Density
}
interface DensityRow extends DensityAPI {
densityAtCreation?: Density
}
type DensityCell = DensityAPI
type DensityHeader = DensityAPI
declare module '@tanstack/table-core' {
interface Plugins {
densityFeature: TableFeature
}
interface TableState_FeatureMap {
densityFeature: DensityState
}
interface TableOptions_FeatureMap<
TFeatures extends TableFeatures,
TData extends RowData,
> {
densityFeature: DensityOptions
}
interface Table_FeatureMap<
TFeatures extends TableFeatures,
TData extends RowData,
> {
densityFeature: DensityTable
}
interface ColumnDef_FeatureMap<
TFeatures extends TableFeatures,
TData extends RowData,
TValue extends CellData,
> {
densityFeature: DensityColumnDef
}
interface Column_FeatureMap<
TFeatures extends TableFeatures,
TData extends RowData,
> {
densityFeature: DensityColumn
}
interface Row_FeatureMap<
TFeatures extends TableFeatures,
TData extends RowData,
> {
densityFeature: DensityRow
}
interface Cell_FeatureMap {
densityFeature: DensityCell
}
interface Header_FeatureMap {
densityFeature: DensityHeader
}
// If this feature actually owned a row-model stage, it would also merge
// RowModelFns_FeatureMap and CachedRowModels_FeatureMap here, then populate
// table._rowModelFns/table._rowModels with matching runtime/cache wiring.
}
const readDensity = (table: unknown): Density =>
(
table as {
atoms: { density: { get: () => Density } }
}
).atoms.density.get()
export const densityFeature: TableFeature = {
getInitialState: (state) => ({ density: 'md', ...state }),
getDefaultTableOptions: (table) => ({
onDensityChange: makeStateUpdater('density', table),
}),
constructTableAPIs: (table) => {
assignTableAPIs('densityFeature', table, {
table_getDensity: { fn: () => readDensity(table) },
table_setDensity: {
fn: (updater: Updater<Density>) =>
(table.options as DensityOptions).onDensityChange?.((old) =>
functionalUpdate(updater, old),
),
},
})
},
assignColumnPrototype: (prototype, table) => {
assignPrototypeAPIs('densityFeature', prototype, table, {
column_getDensity: { fn: (column) => readDensity(column.table) },
})
},
assignRowPrototype: (prototype, table) => {
assignPrototypeAPIs('densityFeature', prototype, table, {
row_getDensity: { fn: (row) => readDensity(row.table) },
})
},
assignCellPrototype: (prototype, table) => {
assignPrototypeAPIs('densityFeature', prototype, table, {
cell_getDensity: { fn: (cell) => readDensity(cell.table) },
})
},
assignHeaderPrototype: (prototype, table) => {
assignPrototypeAPIs('densityFeature', prototype, table, {
header_getDensity: { fn: (header) => readDensity(header.table) },
})
},
initColumnInstanceData: (column) => {
;(column as unknown as DensityColumn).densityAtCreation = readDensity(
column.table,
)
},
initRowInstanceData: (row) => {
;(row as unknown as DensityRow).densityAtCreation = readDensity(row.table)
},
}
export const features = tableFeatures({ densityFeature })getDefaultColumnDef is also available for overridable column defaults; add it when the feature owns a real column-definition default. Do not add lifecycle hooks merely to fill the surface.
tableFeatures({ densityFeature }) result stable.getInitialState; put user state after defaults.table_, column_, row_, cell_, or header_; the prefix is removed on installation.fn receives declared arguments. Prototype API fn receives the current object first.initTableInstanceData, not constructTableAPIs; all feature data is initialized before any table API is assigned.resetTableInstanceData. Reset state slices through atoms/updaters, and do not expect this hook to reset externally controlled state.memoDeps only for a genuinely derived method. Prototype methods are shared and must not close over per-object mutable data.assignColumnAPIs, assignRowAPIs, assignCellAPIs, or assignHeaderAPIs; use assignPrototypeAPIs in the matching hook.Inspect exported *_FeatureMap interfaces under node_modules/@tanstack/table-core/dist/types/, TableFeature in types/TableFeatures.d.ts, and assignTableAPIs/assignPrototypeAPIs in utils.d.ts. Copy lifecycle shapes—not domain behavior—from the nearest stock feature under dist/features/.
9e523bc
If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.