TanStack Table V9 Roadmap #5270
Replies: 7 comments 8 replies
-
that sounds amazing. Making it easier to introduce/build plugins and better row selection features stand out for me. I will consider some suggestions in in next few days! |
Beta Was this translation helpful? Give feedback.
-
API Alignment With TanStack FormWhile some of the TanStack projects, like Query or Store use only "hooks" - others, like Router and Form need "components" or, rather, templating to handle what we need. Looking at Form and Router's APIs - we have something of a builder pattern emerging with components accepting functions as the means of headless distribution. It would be nice to have this consistent across all TanStack projects, as I think components enable us to have an easier time educating users, controlling lifecycle methods or other FW specific APIs inside of the components, and more. Because of this, I'm wondering if instead of the proposed: //TanStack Table V9 (original proposal)
import {
useReactTableCore, //core instead of useReactTable
flexRender,
getTableHeaderGroups,
getHeaderContext,
getTableRowModel,
getRowVisibleCells,
getCellContext,
getCanColumnSort,
getToggleSortingHandler,
getIsColumnSorted,
} from '@tanstack/react-table';
const table = useReactTableCore({
columns,
data,
// etc...
});
return (
<table>
<thead>
{getTableHeaderGroups({ table }).map((headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<th key={header.id}>
{header.isPlaceholder ? null : (
<div
{...{
className: getCanColumnSort({
column: header.column,
table,
})
? 'cursor-pointer select-none'
: '',
onClick: getToggleSortingHandler({
column: header.column,
table,
}),
}}
>
{flexRender(
header.column.columnDef.header,
getHeaderContext({ header, table }),
)}
{{
asc: ' 🔼',
desc: ' 🔽',
}[
getIsColumnSorted({
column: header.column,
table,
}) as string
] ?? null}
</div>
)}
</th>
))}
</tr>
))}
</thead>
<tbody>
{getTableRowModel({ table }).rows.map((row) => (
<tr key={row.id}>
{getRowVisibleCells(row).map((cell) => (
<td key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
getCellContext({ cell, table }),
)}
</td>
))}
</tr>
))}
</tbody>
</table>
); We have something like this: //TanStack Table V9 (new proposal)
import {
createTable, //core instead of useReactTable
flexRender,
getTableHeaderGroups,
getHeaderContext,
getTableRowModel,
getRowVisibleCells,
getCellContext,
getCanColumnSort,
getToggleSortingHandler,
getIsColumnSorted,
} from '@tanstack/react-table';
const tableFactory = createTable({
methods: {
// If any of these methods are not provided, but are utilized under-the-hood,
// we will warn the user that they need to provide an implementation.
// Otherwise these methods will NOOP: `() => null`
getTableHeaderGroups,
getHeaderContext,
getTableRowModel,
getRowVisibleCells,
getCellContext,
getCanColumnSort,
getToggleSortingHandler,
getIsColumnSorted,
}
})
const App = () => {
const table = tableFactory.useTable({
columns,
data,
// etc...
});
return (
<table>
<thead>
<table.HeaderGroups>
{(headerGroup) => (
<tr key={headerGroup.id}>
{headerGroup.headers.map((header) => (
<th key={header.id}>
{header.isPlaceholder ? null : (
<div
{...{
className: table.getCanColumnSort(header.column)
? 'cursor-pointer select-none'
: '',
onClick: table.getToggleSortingHandler(header.column),
}}
>
{flexRender(
header.column.columnDef.header,
table.getHeaderContext(header),
)}
{{
asc: ' 🔼',
desc: ' 🔽',
}[
table.getIsColumnSorted(header.column) as string
] ?? null}
</div>
)}
</th>
))}
</tr>
)}
</table.HeaderGroups>
</thead>
<tbody>
<table.RowModel>
{({ rows }) => (
<>
{rows.map((row) => (
<tr key={row.id}>
{getRowVisibleCells(row).map((cell) => (
<td key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
getCellContext({ cell, table }),
)}
</td>
))}
</tr>))}
</>
)}
</table.RowModel>
</tbody>
</table>
);
} What do you think? |
Beta Was this translation helpful? Give feedback.
-
Hey hey, long time user here. I came across this discussion while doing a input in my local web group about this library, as it's one of my favorite libraries. Keep up the good work. Hope my input is appreciated. I just want to share my POV what makes the library great for me. 1. Statically Defined and Importable API Functions and Core HooksI'm not so sold on the proposal to split every single method out. One thing I like is the discoverability via intellisense. Moving all functions makes it way harder to use without visiting the documentation. Searching through imports instead of just an instance sounds like a worse consumer experience to me. I really love the documentation was well. But I like to use my IDE to search for a set of methods or options that sound like what I want to do and consult the documentation for the specific behavior. That said, I really like the idea to move things out, declutter and make user plugins easier again. What do you think about the an api along the lines of this proposal: import {
paginated,
sorted,
filtered,
} from '@tanstack/table-core'
import {
withPagination,
withSorting,
withFilters,
useReactTableCore,
} from "@tanstack/react-table";
import {
customized,
withCustomization
} from "./my-custom-plugin"
const useTable = useReactTableCore([
withFilters,
withPagination,
withSorting,
withCustomization
])
const App = () => {
const table = useTable({
data,
columns,
// ...otherOptions
})
// do things with a specific row model
paginated(table).getPreRowModel()
return () => <>
{table.getRowModel().rows.map(row => {/** ... **/})}
<button onClick={() => paginated(table).setPageIndex(0)}>Go to first page</button>
<button onClick={() => customized(table).shuffleRows()}>Shuffle</button>
</>
} Considering other levels // delegated decorator looks neater
filtered(column)
filtered(row)
// dedicated decorator
filteredColumn(column)
filteredRow(row) In this proposal, extensions would be split. The table feature basically handles it's slice of state, it's data transformation, caching etc. The decorator accesses a feature to provide a convenient, rich api. Maybe some methods could be sliced into separate decorators or actual methods only, but at least It could make sense to introduce a feature registry on the table that decorators could use to access the feature, so maybe a full plugin would be a triplet of withFeature, getFeature(table) and featured(table) I remember a pain about plugin order issues in react-table v6 that lead to the introduction of internal features, removing them from user control. One common example was filters after pagination. Maybe, standard features could access another feature in development mode and throw or warn if this constellations are detected. 5. Multiple Filters Per ColumnSince the filter value can be anything, multi filters are already supported, aren't they? I've implemented them a couple of times. How would the changed API look like? I think this would be a really cool addition, but maybe this could also just be an example in the documentation. Is there anything I'm missing? I don't see complex filter ui scenarios that are not possible at the moment. Which is another reason, why I love❤ this project 6. Make Column FilterFns StatefulActually, similar to above, my personal preferred solution for this issue in the current version is an object as filter value that reference the filter function and value. I totally second your point that convenient api for this would be awesome. If the shift of proposal 1 is made to extract functions from the core instance - no matter whether it's through my decorator proposal, independent or extended functions - this could be also interesting to promote community extensions, because the usage wouldn't feel so different from the built-in functionality. That said, more built-in features are always nice |
Beta Was this translation helpful? Give feedback.
-
IMO, priority should be given to better and more complete documentation for faster onboarding of new users. And when I say new users I mean users that are relatively new to JS/TS and can't figure out things simply by reading the APIs and the source code. |
Beta Was this translation helpful? Give feedback.
-
Beta Was this translation helpful? Give feedback.
-
5. Multiple Filters Per ColumnI think this screenshot from airtable is an excellent example of a filtering UI which should be possible in v9. The data model required will be more complex, but this kind of flexibility is pretty powerful. |
Beta Was this translation helpful? Give feedback.
-
Are there any plans to fix reactivity problems? #4876 (comment) |
Beta Was this translation helpful? Give feedback.
-
TanStack Table V9 Roadmap
This is the first draft of ideas for what a TanStack Table V9 could look like. V9 does not have to have that many breaking changes (though there inevitably will still be some), but there are new paradigms that we could introduce that could give you the option to use TanStack Table in a new way that may use significantly less bundle size.
We can, of course, just keep improving TanStack Table V8 with new features, bug fixes, better docs and examples (and we will!), but there are some architecture changes that we could introduce that could unlock untapped potential in TanStack Table, warranting an investment in a full V9 release.
Down below, we will list ideas for new features and improvements that we could make to TanStack Table. If some of these ideas are able to be introduced without breaking changes, we can ship them in a V8.x release. If they require breaking changes, we can ship them in a V9 release.
Not all of these ideas will make it into V9. Some of these ideas may just be experimental, and we'll make considerations based on community feedback. If you have additional ideas you would like to be discussed here, feel free to add to the discussion down below, but please try to offer realistic solutions that should be core to the library.
1. Statically Defined and Importable API Functions and Core Hooks
This a proposal for a new "core" way to use TanStack Table that could have a bundle size starting as low as 2-3kb, while still allowing you to use all of the TanStack Table features that you need.
The
useReactTable
,useVueTable
,createSvelteTable
,createSolidTable
andcreateTable
hooks/functions would be unchanged, but new lightweight "core" versions of these hooks would be introduced with no built-in feature APIs.Click to expand core proposal
Consider how the row models already work in V8, or how the old feature hooks worked in V7. If you want to add features like client-side filtering, pagination, grouping, etc. You have to import those feature's respective row models.
This is already great, as we are only adding the code for rendering the rows that we actually need. If you're table does not need filtering or pagination, you don't have to import those row models, and they won't be included in your bundle. This already saves you up to ~20% of the projected 16kb bundle size of TanStack Table V8. But we can do better.
With the remaining ~80% of the core code, we could be loading just the code for the feature APIs that your table needs.
Internally, the code for loading all of the feature code looks like this:
All of these features are hard coded into the table core, and are 1-2kb in size, each. Even if you never plan on using row selection features, there will still be row selection APIs and code in your bundle.
Now with a maximum of 16-18kb, this is not really a huge deal for most people. But if we want to have room to grow these features and handle more edge cases without having to worry so much about bundle size, we have a few options.
One option for TanStack Table v9 would be to remove all built-in feature APIs from the core table instance.
What? Why?! ...Let me explain.
This line of thinking is inspired by another library recently introduced to the TypeScript ecosystem, Valibot
Valibot is a competitor to the popular library Zod. It does pretty much the same thing as Zod, but while Zod is a 13kb library, Valibot claims to be a "< 1kb Zod Alternative".
Valibot mostly does this by making you import every function that you want to use, instead of providing every imaginable API on a core object with the builder patter. For example, instead of:
You would do:
So, what if we introduced the same pattern to TanStack Table? Instead of:
You would do:
This does not even look like that much of a radical change, but it theoretically takes this from being ~14kb to ~2-3kb. (TBD on the actual numbers). That's a potential 6x or 80% reduction in bundle size for simple use cases.
Now I want to point out, that we want to introduce as few breaking changes as possible. I think that the current
useReactTable
,createSvelteTable
,createSolidTable
,useVueTable
andcreateTable
hooks/functions should retain all of their current APIs. But we could introduce a newuseReactTableCore
,createSvelteTableCore
,createSolidTableCore
,useVueTableCore
andcreateTableCore
hooks/functions that would be the new way to use TanStack Table.How are we going to accomplish this?
The
@tanstack/table-core
package is going under a significant refactor. All API functions are being refactored to be statically defined functions that consumetable
,column
,row
,cell
, etc. instances and perform the state mutations that they need to or return the output that they need. These functions will be importable from whichever framework adapter package that you are using.TanStack Table will then offer 2 ways to create a table instance:
What about TypeScript?
Getting the TypeScript types to work perfectly with all of this is still a work in progress, but promising. More to come on this. We won't ship anything that is not a leading TypeScript experience.
2. Make Extending TanStack Table with Custom Features Easier
As mentioned in the previous section, the current TanStack Table features are all hard coded into the core table instance. We can make it easier to add custom features/plugins to TanStack Table.
Click to expand feature details
It could be a simple as exposing the
_features
table option, though the hardest part of this will be to see if we can get TypeScript to infer all feature APIs correctly from external plugins without resorting to declaration merging.3. Make State Slices Subscribable with TanStack Store
TanStack Table could replace its current state management system with the new "TanStack Store" package that is being used by TanStack Router and TanStack Form.
Click to expand feature details
This will need to be explored further to see if it is a good fit.
4. Alternative Data Structures for State
If TanStack Table could benefit from using more Sets and Maps instead of Arrays and Objects, we could explore that.
Click to expand feature details
Currently all state data structures are either Arrays or Objects. We could explore using other data structures like Maps and Sets.
If this change is made, we would want to still make it easy to serialize the state to JSON, so that it can be persisted in localStorage, etc.
5. Multiple Filters Per Column
Click to expand feature details
Currently TanStack Table only really supports adding 1 filter value per column. This would change the API to allow for multiple filter values per column, which would allow for building more complex filter UIs.
6. Make Column FilterFns Stateful
Click to expand feature details
It can be a common use case to create "Filter Switching" features, where the user can switch between different filter functions for a column. Currently, the filter functions are defined directly on the column, and while you can just switch it out for a new filter function already, dedicated APIs for this could make this easier.
Something like this could be made easier.
7. Multiple Aggregation Functions Per Column
Click to expand feature details
Currently TanStack Table only supports specifying 1 aggregation function per column. This change would allow for specifying an array of aggregation functions to be applied to the column.
Note: I have already implemented this in a derivative project of TanStack Table. See here
8. Better Row Selection Features
Click to expand feature details
The Row Selection features in TanStack Table could be improved in a few ways:
9. More filterFns, sortFns, and aggregationFns
Click to expand feature details
More specific filter functions dealing with specific data types could be added. More match oriented filters like "gte", "lte", "between", etc.
If we do add more of these functions, we might want to require that each function can be independently imported for smaller bundle sizes.
10. Offer More Alternative Row Models
Click to expand feature details
Currently for each feature, TanStack Table offers 1 row model. For features that can way more complicated and diverse, like grouping, we could offer multiple row models that offer different tradeoffs, and are optimized for creating different UIs.
Also, Async or Server-Side row models could be offered here.
11. Change the Compilation Target to ES2020
Click to expand feature details
We will make the same change that TanStack Query made to its compilation targets. Optional chaining add a bit of somewhat significant bundle size when targeting older browsers.
12. Renaming Stuff
We should never rename APIs for the sake of renaming them, but major version bumps are the only chance we get to rename things, and people only get a little mad. Here are some things we could rename:
Click to expand feature details
getRowModel() -> createRowModel()
The row models functions that you import and pass to the table as a table option could be renamed to a
createRowModel()
instead ofgetRowModel()
patternWhy? Mostly to differentiate it from the
getRowModel()
functions that you would be able to import if #1 is implemented.onStateChange -> ???
The current
on[StateName]Change
table options seem to cause a lot of confusion to new users. A lot of people don't realize that they need to pass in the correspondingstate
back into the table state.Open to suggestions here.
on[StateName]Mutation
Beta Was this translation helpful? Give feedback.
All reactions