diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000000..9d08a1a828
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,9 @@
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000000..5a0d5e480b
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,2 @@
+# Auto detect text files and perform LF normalization
+* text=auto eol=lf
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000000..e2ea8ad3b9
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,3 @@
+# These are supported funding model platforms
+
+github: tannerlinsley
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
new file mode 100644
index 0000000000..67bf29eb62
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -0,0 +1,100 @@
+name: '🐛 Bug report'
+description: Report a reproducible bug or regression
+body:
+ - type: markdown
+ attributes:
+ value: |
+ Thank you for reporting an issue :pray:.
+
+ This issue tracker is for reporting reproducible bugs or regression's found in [react-table](https://github.com/tanstack/table)
+ If you have a question about how to achieve or implement something and are struggling, please post a question
+ inside of react-table's [Discussions tab](https://github.com/tanstack/table/discussions) instead of filing an issue.
+
+ Before submitting a new bug/issue, please check the links below to see if there is a solution or question posted there already:
+ - TanStack Table's [Discussions tab](https://github.com/tanstack/table/discussions)
+ - TanStack Table's [Open Issues](https://github.com/tanstack/table/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc)
+ - TanStack Table's [Closed Issues](https://github.com/tanstack/table/issues?q=is%3Aissue+sort%3Aupdated-desc+is%3Aclosed)
+
+ The more information you fill in, the better the community can help you.
+
+ - type: input
+ id: tanstack-table-version
+ attributes:
+ label: TanStack Table version
+ description: |
+ - Please let us know the exact version of the TanStack Table framework adapter that you were using when the issue occurred. If you are using an older version, check to see if your bug has already been solved in the latest version. Please don't just put in "latest", as this is subject to change.
+ - The latest "table-core" version is
+ placeholder: |
+ e.g. v8.11.6
+ validations:
+ required: true
+
+ - type: input
+ id: framework-library-version
+ attributes:
+ label: Framework/Library version
+ description: Which framework and what version of that framework are you using?
+ placeholder: |
+ e.g. React v17.0.2
+ validations:
+ required: true
+
+ - type: textarea
+ id: description
+ attributes:
+ label: Describe the bug and the steps to reproduce it
+ description: Provide a clear and concise description of the challenge you are running into, and the steps we should take to try to reproduce your bug.
+ validations:
+ required: true
+
+ - type: input
+ id: link
+ attributes:
+ label: Your Minimal, Reproducible Example - (Sandbox Highly Recommended)
+ description: |
+ Please add a link to a minimal reproduction.
+ Note:
+ - Your bug may get fixed much faster if we can run your code and it doesn't have dependencies other than React.
+ - To create a shareable code example for web, you can use CodeSandbox (https://codesandbox.io/s/new) or Stackblitz (https://stackblitz.com/).
+ - Please make sure the example is complete and runnable without prior dependencies and free of unnecessary abstractions
+ - Feel free to fork any of the official CodeSandbox examples to reproduce your issue: https://github.com/tanstack/table/tree/main/examples/
+ - For React Native, you can use: https://snack.expo.dev/
+ - For TypeScript related issues only, a TypeScript Playground link might be sufficient: https://www.typescriptlang.org/play
+ - Please read these tips for providing a minimal example: https://stackoverflow.com/help/mcve.
+ placeholder: |
+ e.g. Code Sandbox, Stackblitz, TypeScript Playground, etc.
+ validations:
+ required: true
+
+ - type: textarea
+ id: screenshots_or_videos
+ attributes:
+ label: Screenshots or Videos (Optional)
+ description: |
+ If applicable, add screenshots or a video to help explain your problem.
+ For more information on the supported file image/file types and the file size limits, please refer
+ to the following link: https://docs.github.com/en/github/writing-on-github/working-with-advanced-formatting/attaching-files
+ placeholder: |
+ You can drag your video or image files inside of this editor ↓
+
+ - type: dropdown
+ attributes:
+ options:
+ - No, because I do not know how
+ - No, because I do not have time to dig into it
+ - Maybe, I'll investigate and start debugging
+ - Yes, I think I know how to fix it and will discuss it in the comments of this issue
+ - Yes, I am also opening a PR that solves the problem along side this issue
+ label: Do you intend to try to help solve this bug with your own PR?
+ description: |
+ If you think you know the cause of the problem, the fastest way to get it fixed is to suggest a fix, or fix it yourself! However, it is ok if you cannot solve this yourself and are just wanting help.
+ - type: checkboxes
+ id: agrees-to-terms
+ attributes:
+ label: Terms & Code of Conduct
+ description: By submitting this issue, you agree to follow our Code of Conduct and can verify that you have followed the requirements outlined above to the best of your ability.
+ options:
+ - label: I agree to follow this project's Code of Conduct
+ required: true
+ - label: I understand that if my bug cannot be reliable reproduced in a debuggable environment, it will probably not be fixed and this issue may even be closed.
+ required: true
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 0000000000..963fe444ec
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,11 @@
+blank_issues_enabled: false
+contact_links:
+ - name: 🤔 Feature Requests & Questions
+ url: https://github.com/TanStack/table/discussions
+ about: Please ask and answer questions here.
+ - name: 💬 Community Chat
+ url: https://discord.gg/mQd7egN
+ about: A dedicated discord server hosted by TanStack
+ - name: 🦋 TanStack Bluesky
+ url: https://bsky.app/profile/tanstack.com
+ about: Stay up to date with new releases of our libraries
diff --git a/.github/pull_request_template b/.github/pull_request_template
new file mode 100644
index 0000000000..2c10bc7d7d
--- /dev/null
+++ b/.github/pull_request_template
@@ -0,0 +1,8 @@
+## 🎯 Changes
+
+
+
+## ✅ Checklist
+
+- [ ] I have followed the steps in the [Contributing guide](https://github.com/TanStack/table/blob/main/CONTRIBUTING.md).
+- [ ] I have tested this code locally with `pnpm test:pr`.
diff --git a/.github/workflows/autofix.yml b/.github/workflows/autofix.yml
new file mode 100644
index 0000000000..e3bc82ecfd
--- /dev/null
+++ b/.github/workflows/autofix.yml
@@ -0,0 +1,31 @@
+name: autofix.ci # needed to securely identify the workflow
+
+on:
+ pull_request:
+ push:
+ branches: [main, alpha, beta, rc]
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.event.number || github.ref }}
+ cancel-in-progress: true
+
+permissions:
+ contents: read
+
+jobs:
+ autofix:
+ name: autofix
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v6.0.1
+ with:
+ fetch-depth: 0
+ - name: Setup Tools
+ uses: tanstack/config/.github/setup@main
+ - name: Fix formatting
+ run: pnpm format
+ - name: Apply fixes
+ uses: autofix-ci/action@dd55f44df8f7cdb7a6bf74c78677eb8acd40cd0a
+ with:
+ commit-message: 'ci: apply automated fixes'
diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml
new file mode 100644
index 0000000000..698c3b0a35
--- /dev/null
+++ b/.github/workflows/pr.yml
@@ -0,0 +1,55 @@
+name: PR
+
+on:
+ pull_request:
+ paths-ignore:
+ - 'docs/**'
+ - 'media/**'
+ - '**/*.md'
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.event.number || github.ref }}
+ cancel-in-progress: true
+
+env:
+ NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
+
+permissions:
+ contents: read
+
+jobs:
+ test:
+ name: Test
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v6.0.1
+ with:
+ fetch-depth: 0
+ - name: Start Nx Agents
+ run: npx nx-cloud start-ci-run --distribute-on=".nx/workflows/dynamic-changesets.yaml"
+ - name: Setup Tools
+ uses: tanstack/config/.github/setup@main
+ - name: Get base and head commits for `nx affected`
+ uses: nrwl/nx-set-shas@v4.4.0
+ with:
+ main-branch-name: main
+ - name: Run Checks
+ run: pnpm run test:pr --parallel=3
+ - name: Stop Nx Agents
+ if: ${{ always() }}
+ run: npx nx-cloud stop-all-agents
+ preview:
+ name: Preview
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v6.0.1
+ with:
+ fetch-depth: 0
+ - name: Setup Tools
+ uses: tanstack/config/.github/setup@main
+ - name: Build Packages
+ run: pnpm run build:all
+ - name: Publish Previews
+ run: pnpx pkg-pr-new publish --pnpm --compact './packages/*' --template './examples/*/*'
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000000..c44687d853
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,49 @@
+name: Release
+
+on:
+ workflow_dispatch:
+ inputs:
+ tag:
+ description: override release tag
+ required: false
+ push:
+ branches: [main, alpha, beta, rc]
+
+concurrency:
+ group: ${{ github.workflow }}-${{ github.event.number || github.ref }}
+ cancel-in-progress: true
+
+env:
+ NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
+
+permissions:
+ contents: write
+ id-token: write
+
+jobs:
+ release:
+ name: Release
+ if: github.repository_owner == 'TanStack'
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v6.0.1
+ with:
+ fetch-depth: 0
+ - name: Start Nx Agents
+ run: npx nx-cloud start-ci-run --distribute-on=".nx/workflows/dynamic-changesets.yaml"
+ - name: Setup Tools
+ uses: tanstack/config/.github/setup@main
+ - name: Run Tests
+ run: pnpm run test:ci --parallel=3
+ - name: Stop Nx Agents
+ if: ${{ always() }}
+ run: npx nx-cloud stop-all-agents
+ - name: Publish
+ run: |
+ git config --global user.name 'Tanner Linsley'
+ git config --global user.email 'tannerlinsley@users.noreply.github.com'
+ pnpm run cipublish
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ TAG: ${{ inputs.tag }}
diff --git a/.gitignore b/.gitignore
index 59d4885155..003c036732 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,8 +1,57 @@
-node_modules/
-lib/
-react-table.js
-react-table.css
+
+# See https://help.github.com/ignore-files/ for more about ignoring files.
+
+# dependencies
+node_modules
+package-lock.json
+yarn.lock
+
+# builds
+build
+dist
+lib
+es
+artifacts
+.rpt2_cache
+coverage
+*.tgz
+
+# misc
+.DS_Store
+.env
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+.next
+.svelte-kit
+
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+.history
+size-plugin.json
+stats-hydration.json
+stats-react.json
+stats.html
+.vscode/settings.json
+.idea
+
*.log
-example/dist/
+.DS_Store
+.cache
+.idea
+.pnpm-store
+
+package-lock.json
+yarn.lock
+*.tsbuildinfo
+*.tsbuildinfo
+
+.svelte-kit
+.nx/cache
+.nx/workspace-data
+vite.config.js.timestamp-*
+vite.config.ts.timestamp-*
-storybook-static
+.angular
diff --git a/.npmrc b/.npmrc
new file mode 100644
index 0000000000..268c392d3c
--- /dev/null
+++ b/.npmrc
@@ -0,0 +1 @@
+provenance=true
diff --git a/.nvmrc b/.nvmrc
new file mode 100644
index 0000000000..b404027604
--- /dev/null
+++ b/.nvmrc
@@ -0,0 +1 @@
+24.8.0
diff --git a/.nx/workflows/dynamic-changesets.yaml b/.nx/workflows/dynamic-changesets.yaml
new file mode 100644
index 0000000000..d3536f3ba7
--- /dev/null
+++ b/.nx/workflows/dynamic-changesets.yaml
@@ -0,0 +1,4 @@
+distribute-on:
+ small-changeset: 3 linux-medium-js
+ medium-changeset: 6 linux-medium-js
+ large-changeset: 10 linux-medium-js
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 0000000000..63dd7224e6
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,12 @@
+**/.nx/
+**/.nx/cache
+**/.svelte-kit
+**/build
+**/coverage
+**/dist
+**/docs
+**/old-examples
+**/examples/**/*.svelte
+pnpm-lock.yaml
+
+.angular
diff --git a/.storybook/config.js b/.storybook/config.js
deleted file mode 100644
index 27d9d663d7..0000000000
--- a/.storybook/config.js
+++ /dev/null
@@ -1,40 +0,0 @@
-import React from 'react'
-import { configure, storiesOf } from '@kadira/storybook'
-
-import './reset.css'
-import './fonts.css'
-import './layout.css'
-import '../stories/utils/prism.css'
-import 'github-markdown-css/github-markdown.css'
-import '../react-table.css'
-//
-import Readme from '../README.md'
-//
-import Simple from '../stories/Simple.js'
-import ServerSide from '../stories/ServerSide.js'
-import SubComponents from '../stories/SubComponents.js'
-import Pivoting from '../stories/Pivoting.js'
-import PivotingSubComponents from '../stories/PivotingSubComponents.js'
-import OneHundredKRows from '../stories/OneHundredKRows.js'
-//
-configure(() => {
- storiesOf('1. Docs')
- .add('Readme', () => {
- const ReadmeCmp = React.createClass({
- render () {
- return
- },
- componentDidMount () {
- global.Prism.highlightAll()
- }
- })
- return
- })
- storiesOf('2. Demos')
- .add('Client-side Data', Simple)
- .add('Server-side Data', ServerSide)
- .add('Sub Components', SubComponents)
- .add('Pivoting & Aggregation', Pivoting)
- .add('Pivoting & Aggregation w/ Sub Components', PivotingSubComponents)
- .add('100k Rows w/ Pivoting & Sub Components', OneHundredKRows)
-}, module)
diff --git a/.storybook/fonts.css b/.storybook/fonts.css
deleted file mode 100644
index 7ff471979a..0000000000
--- a/.storybook/fonts.css
+++ /dev/null
@@ -1,6 +0,0 @@
-@import url("https://fonts.googleapis.com/css?family=Open+Sans:300,600");
-body {
- background: #fff;
- font-family: 'Open Sans', sans-serif;
- font-weight: 300;
-}
diff --git a/.storybook/layout.css b/.storybook/layout.css
deleted file mode 100644
index 4f5a133e9f..0000000000
--- a/.storybook/layout.css
+++ /dev/null
@@ -1,3 +0,0 @@
-body{
- padding: 20px;
-}
diff --git a/.storybook/reset.css b/.storybook/reset.css
deleted file mode 100644
index fe73564367..0000000000
--- a/.storybook/reset.css
+++ /dev/null
@@ -1,48 +0,0 @@
-/* http://meyerweb.com/eric/tools/css/reset/
- v2.0 | 20110126
- License: none (public domain)
-*/
-
-html, body, div, span, applet, object, iframe,
-h1, h2, h3, h4, h5, h6, p, blockquote, pre,
-a, abbr, acronym, address, big, cite, code,
-del, dfn, em, img, ins, kbd, q, s, samp,
-small, strike, strong, sub, sup, tt, var,
-b, u, i, center,
-dl, dt, dd, ol, ul, li,
-fieldset, form, label, legend,
-table, caption, tbody, tfoot, thead, tr, th, td,
-article, aside, canvas, details, embed,
-figure, figcaption, footer, header, hgroup,
-menu, nav, output, ruby, section, summary,
-time, mark, audio, video {
- margin: 0;
- padding: 0;
- border: 0;
- font-size: 100%;
- font: inherit;
- vertical-align: baseline;
-}
-/* HTML5 display-role reset for older browsers */
-article, aside, details, figcaption, figure,
-footer, header, hgroup, menu, nav, section {
- display: block;
-}
-body {
- line-height: 1;
-}
-/*ol, ul {
- list-style: none;
-}*/
-blockquote, q {
- quotes: none;
-}
-blockquote:before, blockquote:after,
-q:before, q:after {
- content: '';
- content: none;
-}
-table {
- border-collapse: collapse;
- border-spacing: 0;
-}
diff --git a/.storybook/webpack.config.js b/.storybook/webpack.config.js
deleted file mode 100644
index d09f4beca3..0000000000
--- a/.storybook/webpack.config.js
+++ /dev/null
@@ -1,27 +0,0 @@
-const path = require('path')
-
-// load the default config generator.
-const genDefaultConfig = require('@kadira/storybook/dist/server/config/defaults/webpack.config.js')
-
-module.exports = (config, env) => {
- config = genDefaultConfig(config, env)
-
- Object.assign(config, {
- module: Object.assign(config.module, {
- loaders: config.module.loaders.concat([{
- test: /\.md$/,
- loader: 'html!markdown',
- include: path.resolve(__dirname, '../')
- }, {
- test: /\.html$/,
- loader: 'html',
- query: {
- minimize: true
- },
- include: path.resolve(__dirname, '../')
- }])
- })
- })
-
- return config
-}
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 04f1d52d7e..0000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-language: node_js
-node_js:
- - "6"
-script: npm test
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 0000000000..1d7ac851ea
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,3 @@
+{
+ "recommendations": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode"]
+}
diff --git a/CNAME b/CNAME
deleted file mode 100644
index 9a30abb277..0000000000
--- a/CNAME
+++ /dev/null
@@ -1 +0,0 @@
-react-table.js.org
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000000..fa111aa2db
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,81 @@
+---
+title: Code of Conduct
+id: code-of-conduct
+---
+
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to making participation in our project and
+our community a harassment-free experience for everyone, regardless of age, body
+size, disability, ethnicity, sex characteristics, gender identity and expression,
+level of experience, education, socio-economic status, nationality, personal
+appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+- Using welcoming and inclusive language
+- Being respectful of differing viewpoints and experiences
+- Gracefully accepting constructive criticism
+- Focusing on what is best for the community
+- Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+- The use of sexualized language or imagery and unwelcome sexual attention or
+ advances
+- Trolling, insulting/derogatory comments, and personal or political attacks
+- Public or private harassment
+- Publishing others' private information, such as a physical or electronic
+ address, without explicit permission
+- Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community. Examples of
+representing a project or community include using an official project e-mail
+address, posting via an official social media account, or acting as an appointed
+representative at an online or offline event. Representation of a project may be
+further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting the project team at TANNERLINSLEY@GMAIL.COM. All
+complaints will be reviewed and investigated and will result in a response that
+is deemed necessary and appropriate to the circumstances. The project team is
+obligated to maintain confidentiality with regard to the reporter of an incident.
+Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good
+faith may face temporary or permanent repercussions as determined by other
+members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
+available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
+
+[homepage]: https://www.contributor-covenant.org
+
+For answers to common questions about this code of conduct, see
+https://www.contributor-covenant.org/faq
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000000..287de60920
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,53 @@
+---
+title: Contributing
+id: contributing
+---
+
+# Contributing
+
+## Questions
+
+If you have questions about implementation details, help or support, then please use our dedicated community forum at [Github Discussions](https://github.com/tanstack/table/discussions) **PLEASE NOTE:** If you choose to instead open an issue for your question, your issue will be immediately closed and redirected to the forum.
+
+## Reporting Issues
+
+If you have found what you think is a bug, please [file an issue](https://github.com/tanstack/table/issues/new). **PLEASE NOTE:** Issues that are identified as implementation questions or non-issues will be immediately closed and redirected to [Github Discussions](https://github.com/tanstack/table/discussions)
+
+## Suggesting new features
+
+If you are here to suggest a feature, first create an issue if it does not already exist. From there, we will discuss use-cases for the feature and then finally discuss how it could be implemented.
+
+## Development
+
+Before proceeding with development, ensure you match one of the following criteria:
+
+- Fixing a small bug
+- Fixing a larger issue that has been previously discussed and agreed-upon by maintainers
+- Adding a new feature that has been previously discussed and agreed-upon by maintainers
+
+## Development Workflow
+
+- Fork this repository, we prefer the `feat-*` branch name style
+- Ensure you have `pnpm` installed
+- Install projects dependencies and linkages by running `pnpm install`
+- Auto-build and auto-test files as you edit by running `pnpm dev`
+- Implement your changes and tests
+- To run examples, follow their individual directions. Usually this includes:
+ - cd into the example directory
+ - Do NOT install dependencies again or do any linking. Nx already handles this for you. Only run install from the project root.
+ - Starting the dev server with `pnpm dev` or `pnpm start` (from the example directory)
+- To test in your own projects:
+ - Build/watch for changes with `pnpm build`/`pnpm dev`
+- Document your changes in the appropriate documentation website markdown pages
+- Commit your work and open a pull request
+- Submit PR for review
+
+## Adding a new example
+
+- Clone an existing example into the appropriate `examples` directory
+- Name it the example name in kebab-case
+- Update the new example's package.json to match the new example name and any other details
+- Check dependencies for unused packages
+- Install any additional packages to the example that you may need
+- Update the docs/config.json file to include the new example in the navigation sidebar
+- Commit the example eg. `docs: Add example-name`
diff --git a/README.md b/README.md
index e64ec14836..4490fe2758 100644
--- a/README.md
+++ b/README.md
@@ -1,410 +1,117 @@
-
-
-
-
+
-# React Table
-`react-table` is a **lightweight, fast and extendable datagrid** built for React
-
+
-
-
-
-
-
-
-
-
+
-## Features
-
-- Lightweight at 7kb (and just 2kb more for styles)
-- Fully customizable JSX templating
-- Supports both Client-side & Server-side pagination and multi-sorting
-- Column Pivoting & Aggregation
-- Minimal design & easily themeable
-- Fully controllable via optional props and callbacks
-- "Why I wrote React Table and the problems it has solved for Nozzle.io by Tanner Linsley
-
-## Demo
-
-## Table of Contents
-- [Installation](#installation)
-- [Example](#example)
-- [Data](#data)
-- [Props](#props)
-- [Columns](#columns)
-- [Styles](#styles)
-- [Header Groups](#header-groups)
-- [Pivoting & Aggregation](#pivoting--aggregation)
-- [Sub Tables & Sub Components](#sub-tables--sub-components)
-- [Server-side Data](#server-side-data)
-- [Fully Controlled Component](#fully-controlled-component)
-- [Multi-sort](#multi-sort)
-- [Component Overrides](#component-overrides)
-
-## Installation
-```bash
-$ npm install react-table
-```
-
-## Example
-```javascript
-import ReactTable from 'react-table'
-
-const data = [{
- name: 'Tanner Linsley',
- age: 26,
- friend: {
- name: 'Jason Maurer',
- age: 23,
- }
-},{
- ...
-}]
-
-const columns = [{
- header: 'Name',
- accessor: 'name' // String-based value accessors !
-}, {
- header: 'Age',
- accessor: 'age',
- render: props => props.value // Custom cell components!
-}, {
- header: 'Friend Name',
- accessor: d => d.friend.name // Custom value accessors!
-}, {
- header: props => Friend Age, // Custom header components!
- accessor: 'friend.age'
-}]
-
-
-```
-
-## Data
-Simply pass the `data` prop anything that resembles an array or object. Client-side sorting and pagination are built in, and your table will update gracefully as you change any props. [Server-side data](#server-side-data) is also supported!
-
-
-## Props
-These are all of the available props (and their default values) for the main `` component.
-```javascript
-{
- // General
- loading: false, // Whether to show the loading overlay or not
- defaultPageSize: 20, // The default page size (this can be changed by the user if `showPageSizeOptions` is enabled)
- minRows: 0, // Ensure this many rows are always rendered, regardless of rows on page
- showPagination: true, // Shows or hides the pagination component
- showPageJump: true, // Shows or hides the pagination number input
- showPageSizeOptions: true, // Enables the user to change the page size
- pageSizeOptions: [5, 10, 20, 25, 50, 100], // The available page size options
- expanderColumnWidth: 30, // default columnWidth for the expander column
-
- // Callbacks
- onChange: (state, instance) => null, // Anytime the internal state of the table changes, this will fire
- onTrClick: (row, event) => null, // Handler for row click events
-
- // Text
- previousText: 'Previous',
- nextText: 'Next',
- pageText: 'Page',
- ofText: 'of',
- rowsText: 'rows',
-
- // Classes
- className: '-striped -highlight', // The most top level className for the component
- tableClassName: '', // ClassName for the `table` element
- theadClassName: '', // ClassName for the `thead` element
- tbodyClassName: '', // ClassName for the `tbody` element
- trClassName: '', // ClassName for all `tr` elements
- trClassCallback: row => null, // A call back to dynamically add classes (via the classnames module) to a row element
- paginationClassName: '' // ClassName for `pagination` element
-
- // Styles
- style: {}, // Main style object for the component
- tableStyle: {}, // style object for the `table` component
- theadStyle: {}, // style object for the `thead` component
- tbodyStyle: {}, // style object for the `tbody` component
- trStyle: {}, // style object for the `tr` component
- trStyleCallback: row => {}, // A call back to dynamically add styles to a row element
- thStyle: {}, // style object for the `th` component
- tdStyle: {}, // style object for the `td` component
- paginationStyle: {}, // style object for the `paginination` component
-
- // Controlled Props (see Using as a Fully Controlled Component below)
- page: undefined,
- pageSize: undefined,
- sorting: undefined,
- expandedRows: undefined,
- // Controlled Callbacks
- onExpandRow: undefined,
- onPageChange: undefined,
- onPageSizeChange: undefined,
-}
-```
-
-You can easily override the core defaults like so:
-
-```javascript
-import { ReactTableDefaults } from 'react-table'
-
-Object.assign(ReactTableDefaults, {
- defaultPageSize: 10,
- minRows: 3,
- // etc...
-})
-```
-
-Or just define them on the component per-instance
-
-```javascript
-
-```
-
-## Columns
-`` requires a `columns` prop, which is an array of objects containing the following properties
-
-```javascript
-[{
- // General
- accessor: 'propertyName' or Accessor eg. (row) => row.propertyName,
- id: 'myProperty', // Conditional - A unique ID is required if the accessor is not a string or if you would like to override the column name used in server-side calls
- sortable: true,
- sort: 'asc' or 'desc', // used to determine the column sorting on init
- show: true, // can be used to hide a column
- minWidth: 100 // A minimum width for this column. If there is room, columns will flex to fill available space
-
- // Cell Options
- className: '', // Set the classname of the `td` element of the column
- style: {}, // Set the style of the `td` element of the column
- render: JSX eg. ({value, rowValues, row, index, viewIndex}) => {value}, // Provide a JSX element or stateless function to render whatever you want as the column's cell with access to the entire row
- // value == the accessed value of the column
- // rowValues == an object of all of the accessed values for the row
- // row == the original row of data supplied to the table
- // index == the original index of the data supplied to the table
- // viewIndex == the index of the row in the current page
-
- // Header & HeaderGroup Options
- header: 'Header Name' or JSX eg. ({data, column}) =>
Header Name
,
- headerClassName: '', // Set the classname of the `th` element of the column
- headerStyle: {}, // Set the style of the `th` element of the column
-
- // Header Groups only
- columns: [...] // See Header Groups section below
-
-}]
-```
-
-## Styles
-React-table is built to be dropped into existing applications or styled from the ground up, but if you'd like a decent starting point, you can optionally include our default theme `react-table.css`. We think it looks great, honestly :)
-
-## Header Groups
-To group columns with another header column, just nest your columns in a header column like so:
-```javascript
-const columns = [{
- header: 'Favorites',
- columns: [{
- header: 'Color',
- accessor: 'favorites.color'
- }, {
- header: 'Food',
- accessor: 'favorites.food'
- } {
- header: 'Actor',
- accessor: 'favorites.actor'
- }]
-}]
-```
-
-## Pivoting & Aggregation
-Pivoting the table will group records together based on their accessed values and allow the rows in that group to be expanded underneath it.
-To pivot, pass an array of `columnID`'s to `pivotBy`. Remember, a column's `id` is either the one that you assign it (when using a custom accessors) or its `accessor` string.
-```javascript
-
-```
-
-Naturally when grouping rows together, you may want to aggregate the rows inside it into the grouped column. No aggregation is done by default, however, it is very simple to aggregate any pivoted columns:
-```javascript
-// In this example, we use lodash to sum and average the values, but you can use whatever you want to aggregate.
-const columns = [{
- header: 'Age',
- accessor: 'age',
- aggregate: (values, rows) => _.round(_.mean(values)),
- render: row => {
- // You can even render the cell differently if it's an aggregated cell
- return {row.aggregated ? `${row.value} (avg)` : row.value}
- }
-}, {
- header: 'Visits',
- accessor: 'visits',
- aggregate: (values, rows) => _.sum(values)
-}]
-```
-
-Pivoted columns can be sorted just like regular columns, but not independently of each other. For instance, if you click to sort the pivot column in ascending order, it will sort by each pivot recursively in ascending order together.
-
-## Sub Tables & Sub Components
-By adding a `SubComponent` props, you can easily add an expansion level to all root-level rows:
-```javascript
- {
- return (
-
- You can put any component you want here, even another React Table! You even have access to the row-level data if you need! Spark-charts, drill-throughs, infographics... the possibilities are endless!
-
- )
- }}
-/>
-```
-
-
-## Server-side Data
-If you want to handle pagination, and sorting on the server, `react-table` makes it easy on you.
-
-1. Feed React Table `data` from somewhere dynamic. eg. `state`, a redux store, etc...
-1. Add `manual` as a prop. This informs React Table that you'll be handling sorting and pagination server-side
-1. Subscribe to the `onChange` prop. This function is called at `compomentDidMount` and any time sorting or pagination is changed by the user
-1. In the `onChange` callback, request your data using the provided information in the params of the function (state and instance)
-1. Update your data with the rows to be displayed
-1. Optionally set how many pages there are total
-
-```javascript
- {
- // show the loading overlay
- this.setState({loading: true})
- // fetch your data
- Axios.post('mysite.com/data', {
- page: state.page,
- pageSize: state.pageSize,
- sorting: state.sorting
- })
- .then((res) => {
- // Update react-table
- this.setState({
- data: res.data.rows,
- pages: res.data.pages,
- loading: false
- })
- })
- }}
-/>
-```
-
-For a detailed example, take a peek at our async table mockup
-
-## Fully Controlled Component
-React Table by default works fantastically out of the box, but you can achieve even more control and customization if you choose to maintain the state yourself. It is very easy to do, even if you only want to manage *parts* of the state.
-
-Here are the props and their corresponding callbacks that control the state of the a table:
-```javascript
- {...}} // Called when the page index is changed by the user
- onPageSizeChange={(pageSize, pageIndex) => {...}} // Called when the pageSize is changed by the user. The resolve page is also sent to maintain approximate position in the data
- onSortingChange={(column, shiftKey) => {...}} // Called when a sortable column header is clicked with the column itself and if the shiftkey was held. If the column is a pivoted column, `column` will be an array of columns
- onExpandRow={(index, event) => {...}} // Called when an expander is clicked. Use this to manage `expandedRows`
-/>
-```
-
-## Multi-Sort
-When clicking on a column header, hold shift to multi-sort! You can toggle `ascending` `descending` and `none` for multi-sort columns. Clicking on a header without holding shift will clear the multi-sort and replace it with the single sort of that column. It's quite handy!
-
-## Component Overrides
-Though we confidently stand by the markup and architecture behind it, `react-table` does offer the ability to change the core componentry it uses to render everything. You can extend or override these internal components by passing a react component to it's corresponding prop on either the global props or on a one-off basis like so:
-```javascript
-// Change the global default
-import { ReactTableDefaults } from 'react-table'
-Object.assign(ReactTableDefaults, {
- TableComponent: Component,
- TheadComponent: Component,
- TbodyComponent: Component,
- TrGroupComponent: Component,
- TrComponent: Component,
- ThComponent: Component,
- TdComponent: Component,
- PaginationComponent: Component,
- PreviousComponent: Component,
- NextComponent: Component,
- LoadingComponent: Component,
- ExpanderComponent: Component
-})
-
-// Or change per instance
-
-```
+
-If you choose to change the core components React-Table uses to render, you must make sure your replacement components consume and utilize all of the supplied and inherited props that are needed for that component to function properly. We would suggest investigating the source for the component you wish to replace.
+### [Become a Sponsor!](https://github.com/sponsors/tannerlinsley/)
+
-## Contributing
-To suggest a feature, create an issue if it does not already exist.
-If you would like to help develop a suggested feature follow these steps:
+# TanStack Table
+
+> [!NOTE]
+> You may know TanStack Table by the adapter names:
+>
+> - [Angular Table](https://tanstack.com/table/alpha/docs/framework/angular/angular-table)
+> - [Lit Table](https://tanstack.com/table/alpha/docs/framework/lit/lit-table)
+> - [React Table](https://tanstack.com/table/alpha/docs/framework/react/react-table)
+> - [Solid Table](https://tanstack.com/table/alpha/docs/framework/solid/solid-table)
+> - [Svelte Table](https://tanstack.com/table/alpha/docs/framework/svelte/svelte-table)
+> - [Vue Table](https://tanstack.com/table/alpha/docs/framework/vue/vue-table)
+
+A headless table library for building powerful datagrids with full control over markup, styles, and behavior.
+
+- Framework‑agnostic core with bindings for React, Vue & Solid
+- 100% customizable — bring your own UI, components, and styles
+- Sorting, filtering, grouping, aggregation & row selection
+- Lightweight, virtualizable & server‑side friendly
+
+### Read the Docs →
+
+## Get Involved
+
+- We welcome issues and pull requests!
+- Participate in [GitHub discussions](https://github.com/TanStack/table/discussions)
+- Chat with the community on [Discord](https://discord.com/invite/WrRKjPJ)
+- See [CONTRIBUTING.md](./CONTRIBUTING.md) for setup instructions
+
+## Partners
+
+
-- Fork this repo
-- `npm install`
-- `npm run watch`
-- Implement your changes to files in the `src/` directory
-- Submit PR for review
+
+
+
+We're looking for TanStack Table Partners to join our mission! Partner with us to push the boundaries of TanStack Table and build amazing things together.
+
+
+While we clearly love TanStack Table, we acknowledge that it is not a "batteries" included product packed with customer support and enterprise polish. We realize that some of our users may need this though! To help out here, we want to introduce you to AG Grid, an enterprise-grade data grid solution that can supercharge your applications with its extensive feature set and robust performance. While TanStack Table is also a powerful option for implementing data grids, we believe in providing our users with a diverse range of choices that best fit their specific requirements. AG Grid is one such choice, and we're excited to highlight its capabilities for you.
+
+## Why Choose [AG Grid](https://ag-grid.com/react-data-grid/?utm_source=reacttable&utm_campaign=githubreacttable)?
+
+Here are some good reasons to consider AG Grid for your next project:
+
+### Comprehensive Feature Set
+
+AG Grid offers an extensive set of features, making it a versatile and powerful data grid solution. With AG Grid, you get access to a wide range of functionalities that cater to the needs of complex enterprise applications. From advanced sorting, filtering, and grouping capabilities to column pinning, multi-level headers, and tree data structure support, AG Grid provides you with the tools to create dynamic and interactive data grids that meet your application's unique demands.
+
+### High Performance
+
+When it comes to handling large datasets and achieving exceptional performance, AG Grid delivers outstanding results. It employs highly optimized rendering techniques, efficient data updates, and virtualization to ensure smooth scrolling and fast response times, even when dealing with thousands or millions of rows of data. AG Grid's performance optimizations make it an excellent choice for applications that require high-speed data manipulation and visualization.
+
+### Customization and Extensibility
+
+AG Grid is designed to be highly customizable and extensible, allowing you to tailor the grid to your specific needs. It provides a rich set of APIs and events that enable you to integrate custom functionality seamlessly. You can define custom cell renderers, editors, filters, and aggregators to enhance the grid's behavior and appearance. AG Grid also supports a variety of themes, allowing you to match the grid's visual style to your application's design.
+
+### Support for Enterprise Needs
+
+As an enterprise-focused solution, AG Grid caters to the requirements of complex business applications. It offers enterprise-specific features such as row grouping, column pinning, server-side row model, master/detail grids, and rich editing capabilities. AG Grid also integrates well with other enterprise frameworks and libraries, making it a reliable choice for large-scale projects.
+
+### Active Development and Community Support
+
+AG Grid benefits from active development and a thriving community of developers. The team behind AG Grid consistently introduces new features and enhancements, ensuring that the product evolves to meet the changing needs of the industry. The community support is robust, with forums, documentation, and examples readily available to assist you in utilizing the full potential of AG Grid.
+
+## Conclusion
+
+While TanStack Table remains a powerful and flexible option for implementing data grids, we understand that different projects have different requirements. AG Grid offers a compelling enterprise-grade solution that may be particularly suited to your needs. Its comprehensive feature set, high performance, customization options, and focus on enterprise requirements make AG Grid an excellent choice for projects that demand a robust and scalable data grid solution.
+
+We encourage you to explore AG Grid further by visiting their website and trying out their demo. Remember that both TanStack Table and AG Grid have their unique strengths and considerations. We believe in providing options to our users, empowering you to make informed decisions and choose the best fit for your specific use case.
+
+Visit the [AG Grid website](https://www.ag-grid.com).
diff --git a/docs/faq.md b/docs/faq.md
new file mode 100644
index 0000000000..714a9fa448
--- /dev/null
+++ b/docs/faq.md
@@ -0,0 +1,166 @@
+---
+title: FAQ
+---
+
+## How do I stop infinite rendering loops?
+
+If you are using React, there is a very common pitfall that can cause infinite rendering. If you fail to give your `columns`, `data`, or `state` a stable reference, React will enter an infinite loop of re-rendering upon any change to the table state.
+
+Why does this happen? Is this a bug in TanStack Table? **No**, it is not. *This is fundamentally how React works*, and properly managing your columns, data, and state will prevent this from happening.
+
+TanStack Table is designed to trigger a re-render whenever either the `data` or `columns` that are passed into the table change, or whenever any of the table's state changes.
+
+> Failing to give `columns` or `data` stable references can cause an infinite loop of re-renders.
+
+### Pitfall 1: Creating new columns or data on every render
+
+```js
+export default function MyComponent() {
+ //😵 BAD: This will cause an infinite loop of re-renders because `columns` is redefined as a new array on every render!
+ const columns = [
+ // ...
+ ];
+
+ //😵 BAD: This will cause an infinite loop of re-renders because `data` is redefined as a new array on every render!
+ const data = [
+ // ...
+ ];
+
+ //❌ Columns and data are defined in the same scope as `useTable` without a stable reference, will cause infinite loop!
+ const table = useTable({
+ columns,
+ data,
+ });
+
+ return
...
;
+}
+```
+
+### Solution 1: Stable references with useMemo or useState
+
+In React, you can give a "stable" reference to variables by defining them outside/above the component, or by using `useMemo` or `useState`, or by using a 3rd party state management library (like Redux or React Query 😉)
+
+```js
+//✅ OK: Define columns outside of the component
+const columns = [
+ // ...
+];
+
+//✅ OK: Define data outside of the component
+const data = [
+ // ...
+];
+
+// Usually it's more practical to define columns and data inside the component, so use `useMemo` or `useState` to give them stable references
+export default function MyComponent() {
+ //✅ GOOD: This will not cause an infinite loop of re-renders because `columns` is a stable reference
+ const columns = useMemo(() => [
+ // ...
+ ], []);
+
+ //✅ GOOD: This will not cause an infinite loop of re-renders because `data` is a stable reference
+ const [data, setData] = useState(() => [
+ // ...
+ ]);
+
+ // Columns and data are defined in a stable reference, will not cause infinite loop!
+ const table = useTable({
+ columns,
+ data,
+ });
+
+ return
...
;
+}
+```
+
+### Pitfall 2: Mutating columns or data in place
+
+Even if you give your initial `columns` and `data` stable references, you can still run into infinite loops if you mutate them in place. This is a common pitfall that you may not notice that you are doing at first. Something as simple as an inline `data.filter()` can cause an infinite loop if you are not careful.
+
+```js
+export default function MyComponent() {
+ //✅ GOOD
+ const columns = useMemo(() => [
+ // ...
+ ], []);
+
+ //✅ GOOD (React Query provides stable references to data automatically)
+ const { data, isLoading } = useQuery({
+ //...
+ });
+
+ const table = useTable({
+ columns,
+ //❌ BAD: This will cause an infinite loop of re-renders because `data` is mutated in place (destroys stable reference)
+ data: data?.filter(d => d.isActive) ?? [],
+ });
+
+ return
...
;
+}
+```
+
+### Solution 2: Memoize your data transformations
+
+To prevent infinite loops, you should always memoize your data transformations. This can be done with `useMemo` or similar.
+
+```js
+export default function MyComponent() {
+ //✅ GOOD
+ const columns = useMemo(() => [
+ // ...
+ ], []);
+
+ //✅ GOOD
+ const { data, isLoading } = useQuery({
+ //...
+ });
+
+ //✅ GOOD: This will not cause an infinite loop of re-renders because `filteredData` is memoized
+ const filteredData = useMemo(() => data?.filter(d => d.isActive) ?? [], [data]);
+
+ const table = useTable({
+ columns,
+ data: filteredData, // stable reference!
+ });
+
+ return
...
;
+}
+```
+
+### React Forget
+
+When React Forget is released, these problems might be a thing of the past. Or just use Solid.js... 🤓
+
+## How do I stop my table state from automatically resetting when my data changes?
+
+Most plugins use state that _should_ normally reset when the data sources changes, but sometimes you need to suppress that from happening if you are filtering your data externally, or immutably editing your data while looking at it, or simply doing anything external with your data that you don't want to trigger a piece of table state to reset automatically.
+
+For those situations, each plugin provides a way to disable the state from automatically resetting internally when data or other dependencies for a piece of state change. By setting any of them to `false`, you can stop the automatic resets from being triggered.
+
+Here is a React-based example of stopping basically every piece of state from changing as they normally do while we edit the `data` source for a table:
+
+```js
+const [data, setData] = React.useState([])
+const skipPageResetRef = React.useRef()
+
+const updateData = newData => {
+ // When data gets updated with this function, set a flag
+ // to disable all of the auto resetting
+ skipPageResetRef.current = true
+
+ setData(newData)
+}
+
+React.useEffect(() => {
+ // After the table has updated, always remove the flag
+ skipPageResetRef.current = false
+})
+
+useTable({
+ ...
+ autoResetPageIndex: !skipPageResetRef.current,
+ autoResetExpanded: !skipPageResetRef.current,
+})
+```
+
+Now, when we update our data, the above table states will not automatically reset!
diff --git a/docs/favicon.ico b/docs/favicon.ico
deleted file mode 100644
index 31b707d749..0000000000
Binary files a/docs/favicon.ico and /dev/null differ
diff --git a/docs/framework/angular/angular-table.md b/docs/framework/angular/angular-table.md
new file mode 100644
index 0000000000..2f8437c7f5
--- /dev/null
+++ b/docs/framework/angular/angular-table.md
@@ -0,0 +1,128 @@
+---
+title: Angular Table
+---
+
+The `@tanstack/angular-table` adapter is a wrapper around the core table logic. Most of it's job is related to managing
+state using angular signals, providing types and the rendering implementation of cell/header/footer templates.
+
+## Exports
+
+`@tanstack/angular-table` re-exports all of `@tanstack/table-core`'s APIs and the following:
+
+### `injectTable`
+
+Creates and returns an Angular-reactive table instance.
+
+`injectTable` accepts either:
+
+- an options function `() => TableOptions`
+- a computed signal returning `TableOptions`
+
+The initializer is intentionally re-evaluated whenever any signal read inside it changes.
+This is how the adapter keeps the table in sync with Angular's reactivity model.
+
+Because of that behavior, keep expensive/static values (for example `columns`, feature setup, row models) as stable references outside the initializer, and only read reactive state (`data()`, pagination/filter/sorting signals, etc.) inside it.
+
+Since `ColumnDef` is stricter about generics, prefer building columns with `createColumnHelper()` so feature and row types are inferred consistently.
+
+The returned table is also signal-reactive: table state and table APIs are wired for Angular signals, so you can safely consume table methods inside `computed(...)` and `effect(...)`.
+
+```ts
+import { computed, effect, signal } from '@angular/core'
+import {
+ createColumnHelper,
+ injectTable,
+ type ColumnDef,
+ rowPaginationFeature,
+ stockFeatures
+} from '@tanstack/angular-table'
+
+// Register all table core features
+const _features = tableFeatures(stockFeatures);
+// ...or register only your needed features
+const _features = tableFeatures({
+ rowPaginationFeature,
+ // ...all other features
+})
+
+const columnHelper = createColumnHelper()
+
+export class AppComponent {
+ readonly data = signal([])
+
+ // If you type columns manually, include both generics:
+ // readonly columns: ColumnDef[] = [...]
+ readonly columns = columnHelper.columns([
+ columnHelper.accessor('firstName', {
+ header: 'First name',
+ cell: info => info.getValue(),
+ }),
+ // ...
+ ])
+
+ // This function is re-run when any signal read inside changes.
+ readonly table = injectTable(() => ({
+ _features: _features,
+ // Reactive state can be read directly
+ data: this.data(),
+
+ state: {
+ // ...
+ },
+
+ // Keep stable references outside the initializer
+ columns: this.columns,
+ }))
+
+ constructor() {
+ effect(() => {
+ console.log('Visible rows:', this.table.getRowModel().rows.length)
+ })
+ }
+}
+```
+
+See [injectTable API Reference](reference/functions/injectTable)
+
+### `createTableHook`
+
+`createTableHook` is the Angular composition API for building reusable table infrastructure.
+
+Use it when multiple tables should share the same defaults (features, row models, default options, and component registries) while keeping strong types across the app.
+
+At runtime, `createTableHook` wraps `injectTable` and returns typed helpers such as:
+
+- `injectAppTable` for creating tables with shared defaults
+- `createAppColumnHelper` for strongly typed column definitions
+- pre-typed context helpers (`injectTableContext`, `injectTableCellContext`, `injectTableHeaderContext`, `injectFlexRenderCellContext`, `injectFlexRenderHeaderContext`)
+
+For full setup and patterns, see the [Table Composition Guide](./guide/table-composition.md).
+
+### `FlexRender`
+
+An Angular structural rendering primitive for cell/header/footer content.
+
+It supports the same content kinds as Angular rendering:
+
+- primitive values (`string`, `number`, plain objects)
+- `TemplateRef`
+- component types
+- `flexRenderComponent(component, options?)` wrappers with typed `inputs`, `outputs`, `injector`, `bindings`, and `directives`
+
+Column render functions (`header`, `cell`, `footer`) run in Angular injection context, so you can use `inject()` and signals directly in render logic.
+
+For complete rendering details (`*flexRender`, shorthand directives, `flexRenderComponent`, `TemplateRef`, component inputs/outputs, and `injectFlexRenderContext`), see the [Rendering components Guide](./guide/rendering.md).
+
+### Context helpers and directives
+
+`@tanstack/angular-table` also exports Angular DI helpers and directives for table/cell/header context:
+
+- `TanStackTable` + `injectTableContext()`
+- `TanStackTableCell` + `injectTableCellContext()`
+- `TanStackTableHeader` + `injectTableHeaderContext()`
+
+These APIs provide signal-based context values and are available from nearest directives or from `*flexRender`-rendered components when matching props are present.
+
+### Full API Reference
+
+See [Angular API Reference](reference/index.md)
diff --git a/docs/framework/angular/guide/migrating.md b/docs/framework/angular/guide/migrating.md
new file mode 100644
index 0000000000..102b15d927
--- /dev/null
+++ b/docs/framework/angular/guide/migrating.md
@@ -0,0 +1,764 @@
+---
+title: Migrating to TanStack Table v9 (Angular)
+---
+
+## What's New in TanStack Table v9
+
+TanStack Table v9 is a major release that introduces significant architectural improvements while maintaining the core table logic you're familiar with. Here are the key changes:
+
+### 1. Tree-shaking
+
+- **Features are tree-shakeable**: Features are now treated as plugins—import only what you use. If your table only needs sorting, you won't ship filtering, pagination, or other feature code. Bundlers can eliminate unused code, so for smaller tables you can expect a meaningfully smaller bundle compared to v8. This also lets TanStack Table add features over time without bloating everyone's bundles.
+- **Row models and their functions are refactored**: Row model factories (`createFilteredRowModel`, `createSortedRowModel`, etc.) now accept their processing functions (`filterFns`, `sortFns`, `aggregationFns`) as parameters. This enables tree-shaking of the functions themselves—if you use a custom filter, you don't pay for built-in filters you never use.
+
+### 2. State Management
+
+- **Uses TanStack Store**: The internal state system has been rebuilt on [TanStack Store](https://tanstack.com/store), providing a reactive, framework-agnostic foundation.
+- **Opt-in subscriptions instead of memo hacks**: In Angular, you consume state via signals and `computed(...)`. You can keep reads scoped to the state you actually need and avoid unnecessary template work.
+
+### 3. Composability
+
+- **`tableOptions`**: New utilities let you compose and share table configurations. Define `_features`, `_rowModels`, and default options once, then reuse them across tables or pass them through `createTableHook`.
+- **`createTableHook`** (optional, advanced): Create reusable, strongly typed Angular table factories with pre-bound features, row models, default options, and component registries.
+
+### The Good News: Most Upgrades Are Opt-in
+
+While v9 is a significant upgrade, **you don't have to adopt everything at once**:
+
+- **Don't want to think about tree-shaking yet?** You can start with `stockFeatures` to include most commonly used features.
+- **Your table markup is largely unchanged.** How you render `
`, ``, `
`, `
`, etc. remains the same.
+
+The main change is **how you define a table** with the Angular adapter — specifically the new `_features` and `_rowModels` options.
+
+---
+
+## Quick Legacy Migration
+
+Angular does **not** ship a legacy API.
+
+If you're migrating an Angular project from TanStack Table v8 to v9, you will migrate directly to the v9 Angular adapter APIs (`injectTable`, `_features`, and `_rowModels`).
+
+---
+
+The rest of this guide focuses on migrating to the full v9 API and taking advantage of its features.
+
+## Core Breaking Changes
+
+### Entrypoint Change
+
+The Angular adapter entrypoint to create a table instance is `injectTable`:
+
+```ts
+// v8
+import { createAngularTable } from '@tanstack/angular-table'
+
+const v8Table = createAngularTable(() => ({
+ // options
+}))
+
+// v9
+import { injectTable } from '@tanstack/angular-table'
+
+const v9Table = injectTable(() => ({
+ // options
+}))
+```
+
+> Note: `injectTable` evaluates your initializer whenever any Angular signal read inside of it changes.
+> Keep expensive/static values (like `columns`, `_features`, and `_rowModels`) as stable references outside the initializer.
+
+### New Required Options: `_features` and `_rowModels`
+
+In v9, you must explicitly declare which features and row models your table uses:
+
+```ts
+// v8
+import { createAngularTable, getCoreRowModel } from '@tanstack/angular-table'
+
+const v8Table = createAngularTable(() => ({
+ columns,
+ data: data(),
+ getCoreRowModel: getCoreRowModel(),
+}))
+
+// v9
+import {
+ injectTable,
+ tableFeatures,
+} from '@tanstack/angular-table'
+
+const _features = tableFeatures({}) // Empty = core feaFtures only
+
+// Define stable references outside the initializer
+const v9Table = injectTable(() => ({
+ _features,
+ _rowModels: {}, // Core row model is automatic
+ columns: this.columns,
+ data: this.data(),
+}))
+```
+
+---
+
+## The `_features` Option
+
+Features control what table functionality is available. In v8, all features were bundled. In v9, you import only what you need.
+
+### Importing Individual Features
+
+```ts
+import {
+ tableFeatures,
+ // Import only the features you need
+ columnFilteringFeature,
+ rowSortingFeature,
+ rowPaginationFeature,
+ columnVisibilityFeature,
+ rowSelectionFeature,
+} from '@tanstack/angular-table'
+
+// Create a features object (define this outside your injectTable initializer for stable reference)
+const _features = tableFeatures({
+ columnFilteringFeature,
+ rowSortingFeature,
+ rowPaginationFeature,
+ columnVisibilityFeature,
+ rowSelectionFeature,
+})
+```
+
+### Using `stockFeatures` for v8-like Behavior
+
+If you want all features without thinking about it (like v8), import `stockFeatures`:
+
+```ts
+import { injectTable, stockFeatures } from '@tanstack/angular-table'
+
+class TableCmp {
+ readonly table = injectTable(() => ({
+ _features: stockFeatures, // All features included
+ _rowModels: { /* ... */ },
+ columns: this.columns,
+ data: this.data(),
+ }))
+}
+```
+
+### Available Features
+
+| Feature | Import Name |
+|---------|-------------|
+| Column Filtering | `columnFilteringFeature` |
+| Global Filtering | `globalFilteringFeature` |
+| Row Sorting | `rowSortingFeature` |
+| Row Pagination | `rowPaginationFeature` |
+| Row Selection | `rowSelectionFeature` |
+| Row Expanding | `rowExpandingFeature` |
+| Row Pinning | `rowPinningFeature` |
+| Column Pinning | `columnPinningFeature` |
+| Column Visibility | `columnVisibilityFeature` |
+| Column Ordering | `columnOrderingFeature` |
+| Column Sizing | `columnSizingFeature` |
+| Column Resizing | `columnResizingFeature` |
+| Column Grouping | `columnGroupingFeature` |
+| Column Faceting | `columnFacetingFeature` |
+
+---
+
+## The `_rowModels` Option
+
+Row models are the functions that process your data (filtering, sorting, pagination, etc.). In v9, they're configured via `_rowModels` instead of `get*RowModel` options.
+
+### Migration Mapping
+
+| v8 Option | v9 `_rowModels` Key | v9 Factory Function |
+|-----------|---------------------|---------------------|
+| `getCoreRowModel()` | (automatic) | Not needed — always included |
+| `getFilteredRowModel()` | `filteredRowModel` | `createFilteredRowModel(filterFns)` |
+| `getSortedRowModel()` | `sortedRowModel` | `createSortedRowModel(sortFns)` |
+| `getPaginationRowModel()` | `paginatedRowModel` | `createPaginatedRowModel()` |
+| `getExpandedRowModel()` | `expandedRowModel` | `createExpandedRowModel()` |
+| `getGroupedRowModel()` | `groupedRowModel` | `createGroupedRowModel(aggregationFns)` |
+| `getFacetedRowModel()` | `facetedRowModel` | `createFacetedRowModel()` |
+| `getFacetedMinMaxValues()` | `facetedMinMaxValues` | `createFacetedMinMaxValues()` |
+| `getFacetedUniqueValues()` | `facetedUniqueValues` | `createFacetedUniqueValues()` |
+
+### Key Change: Row Model Functions Now Accept Parameters
+
+Several row model factories now accept their processing functions as parameters. This enables better tree-shaking and explicit configuration:
+
+```ts
+import {
+ injectTable,
+ createFilteredRowModel,
+ createSortedRowModel,
+ createGroupedRowModel,
+ createPaginatedRowModel,
+ filterFns, // Built-in filter functions
+ sortFns, // Built-in sort functions
+ aggregationFns, // Built-in aggregation functions
+} from '@tanstack/angular-table'
+
+class TableCmp {
+ readonly table = injectTable(() => ({
+ _features,
+ _rowModels: {
+ filteredRowModel: createFilteredRowModel(filterFns),
+ sortedRowModel: createSortedRowModel(sortFns),
+ groupedRowModel: createGroupedRowModel(aggregationFns),
+ paginatedRowModel: createPaginatedRowModel(),
+ },
+ columns: this.columns,
+ data: this.data(),
+ }))
+}
+```
+
+### Full Migration Example
+
+```ts
+// v8
+import {
+ injectTable,
+ getCoreRowModel,
+ getFilteredRowModel,
+ getSortedRowModel,
+ getPaginationRowModel,
+ filterFns,
+ sortingFns,
+} from '@tanstack/angular-table'
+
+const v8Table = createAngularTable(() => ({
+ columns,
+ data: data(),
+ getCoreRowModel: getCoreRowModel(), // used to be called "get*RowModel()"
+ getFilteredRowModel: getFilteredRowModel(),
+ getSortedRowModel: getSortedRowModel(),
+ getPaginationRowModel: getPaginationRowModel(),
+ filterFns, // used to be passed in as a root option
+ sortingFns,
+}))
+
+// v9
+import {
+ injectTable,
+ tableFeatures,
+ columnFilteringFeature,
+ rowSortingFeature,
+ rowPaginationFeature,
+ createFilteredRowModel,
+ createSortedRowModel,
+ createPaginatedRowModel,
+ filterFns,
+ sortFns,
+} from '@tanstack/angular-table'
+
+const _features = tableFeatures({
+ columnFilteringFeature,
+ rowSortingFeature,
+ rowPaginationFeature,
+})
+
+const v9Table = injectTable(() => ({
+ _features,
+ _rowModels: {
+ filteredRowModel: createFilteredRowModel(filterFns),
+ sortedRowModel: createSortedRowModel(sortFns),
+ paginatedRowModel: createPaginatedRowModel(),
+ },
+ columns,
+ data: data(),
+}))
+```
+
+---
+
+## State Management Changes
+
+### Accessing State
+
+In v8, you accessed state via `table.getState()`. In v9, state is accessed via the store:
+
+```ts
+// v8
+const state = table.getState()
+const v8 = table.getState()
+const { sorting, pagination } = v8
+
+// v9 - via the store
+const fullState = table.store.state
+const v9 = table.store.state
+const { sorting: v9Sorting, pagination: v9Pagination } = v9
+```
+
+### Optimizing Reads with Angular Signals
+
+In Angular, you have a few good options for consuming table state.
+
+#### Option 1: Prefer `table.store.subscribe(...)` for a sliced signal
+
+TanStack Store lets you subscribe to (and derive) a slice of state. With the Angular adapter, you can use that to create a signal-like value that only updates when the selected slice changes.
+
+This is the closest equivalent to the fine-grained subscription examples you might see in other frameworks.
+
+```ts
+import { computed, effect } from '@angular/core'
+
+class TableCmp {
+ readonly table = injectTable(() => ({
+ _features,
+ _rowModels: { /* ... */ },
+ columns: this.columns,
+ data: this.data(),
+ }))
+
+ // Create a computed to a slice of state.
+ // The store will only emit when this selected value changes.
+ private readonly pagination = this.table.computed(
+ state => state.pagination,
+ )
+
+ constructor() {
+ effect(() => {
+ const { pageIndex, pageSize } = this.pagination()
+ console.log('Page', pageIndex, 'Size', pageSize)
+ })
+ }
+}
+```
+
+#### Option 2: Use `computed(...)` and read from `table.store.state`
+
+You can also use Angular `computed(...)` and directly read from `table.store.state`. This is simple and works well, but for object/array slices you should provide an equality function to avoid unnecessary downstream work when the slice is recreated with the same values.
+
+```ts
+import { computed, effect } from '@angular/core'
+
+class TableCmp {
+ readonly table = injectTable(() => ({
+ _features,
+ _rowModels: { /* ... */ },
+ columns: this.columns,
+ data: this.data(),
+ }))
+
+ // Provide an equality function for object slices
+ readonly pagination = computed(
+ () => this.table.store.state.pagination,
+ {
+ equal: (a, b) => a.pageIndex === b.pageIndex && a.pageSize === b.pageSize,
+ },
+ )
+
+ constructor() {
+ effect(() => {
+ // This effect only re-runs when pagination changes
+ const { pageIndex, pageSize } = this.pagination()
+ console.log('Page', pageIndex, 'Size', pageSize)
+ })
+ }
+}
+```
+
+### Controlled State
+
+Controlled state patterns are pretty the same as v8:
+
+```ts
+import { signal } from '@angular/core'
+import type { SortingState, PaginationState } from '@tanstack/angular-table'
+
+class TableCmp {
+ readonly sorting = signal([])
+ readonly pagination = signal({ pageIndex: 0, pageSize: 10 })
+
+ readonly table = injectTable(() => ({
+ _features,
+ _rowModels: { /* ... */ },
+ columns: this.columns,
+ data: this.data(),
+ state: {
+ sorting: this.sorting(),
+ pagination: this.pagination(),
+ },
+ onSortingChange: (updater) => {
+ updater instanceof Function
+ ? this.sorting.update(updater)
+ : this.sorting.set(updater)
+ },
+ onPaginationChange: (updater) => {
+ updater instanceof Function
+ ? this.pagination.update(updater)
+ : this.pagination.set(updater)
+ },
+ }))
+}
+```
+
+---
+
+## Column Helper Changes
+
+The `createColumnHelper` function now requires a `TFeatures` type parameter in addition to `TData`:
+
+```ts
+// v8
+import { createColumnHelper } from '@tanstack/angular-table'
+
+const columnHelperV8 = createColumnHelper()
+
+// v9
+import { createColumnHelper, tableFeatures, rowSortingFeature } from '@tanstack/angular-table'
+
+const _features = tableFeatures({ rowSortingFeature })
+const columnHelperV9 = createColumnHelper()
+```
+
+### New `columns()` Helper Method
+
+v9 adds a `columns()` helper for better type inference when wrapping column arrays.
+
+```ts
+const columnHelper = createColumnHelper()
+
+// Wrap your columns array for better type inference
+const columns = columnHelper.columns([
+ columnHelper.accessor('firstName', {
+ header: 'First Name',
+ cell: (info) => info.getValue(),
+ }),
+ columnHelper.accessor('lastName', {
+ id: 'lastName',
+ header: () => 'Last Name',
+ cell: (info) => info.getValue(),
+ }),
+ columnHelper.display({
+ id: 'actions',
+ header: 'Actions',
+ cell: () => 'Edit',
+ }),
+])
+```
+
+### Using with `createTableHook`
+
+When using `createTableHook`, you get a pre-bound `createAppColumnHelper` that only requires `TData`:
+
+```ts
+import { createTableHook, tableFeatures, rowSortingFeature } from '@tanstack/angular-table'
+
+const { injectAppTable, createAppColumnHelper } = createTableHook({
+ _features: tableFeatures({ rowSortingFeature }),
+ _rowModels: { /* ... */ },
+})
+
+// TFeatures is already bound — only need TData!
+const columnHelper = createAppColumnHelper()
+```
+
+---
+
+## Rendering Changes
+
+### `FlexRender`
+
+The rendering primitives in the Angular adapter are `FlexRender` and the `*flexRender` directives.
+
+In v9, you can continue to render header/cell/footer content using the Angular adapter rendering utilities, but there are a few important improvements and helper APIs to be aware of.
+
+#### Structural directive rendering
+
+Angular rendering is directive-based:
+
+- `FlexRender` / `*flexRender` renders arbitrary render content (primitives, `TemplateRef`, component types, or `flexRenderComponent(...)` wrappers)
+- The directive is responsible for mounting embedded views or components via `ViewContainerRef`
+
+#### Shorthand directives
+
+If you're rendering standard table content, prefer the shorthand helpers:
+
+- `*flexRenderCell="cell; let value"`
+- `*flexRenderHeader="header; let value"`
+- `*flexRenderFooter="footer; let value"`
+
+These automatically select the correct column definition (`columnDef.cell` / `header` / `footer`) and the right props (`cell.getContext()` / `header.getContext()`), so you don't need to manually provide `props:`.
+
+#### DI-aware render functions + context injection
+
+Column definition render functions (`header`, `cell`, `footer`) run inside an Angular injection context, so they can safely call `inject()` and use signals.
+
+When a component is rendered through the FlexRender directives, you can also access the full render props object via DI using `injectFlexRenderContext()`.
+
+#### Component rendering helper: `flexRenderComponent`
+
+If you need to render an Angular component with explicit configuration (custom `inputs`, `outputs`, `injector`, and Angular v20+ creation-time `bindings`/`directives`), return a `flexRenderComponent(Component, options)` wrapper from your column definition.
+
+For complete rendering details (including component rendering, `TemplateRef`, `flexRenderComponent`, and context helpers), see the [Rendering components Guide](./rendering.md).
+
+---
+
+## The `tableOptions()` Utility
+
+The `tableOptions()` helper provides type-safe composition of table options. It's useful for creating reusable partial configurations that can be spread into your table setup.
+
+### Basic Usage
+
+```ts
+import { injectTable, tableOptions, tableFeatures, rowSortingFeature } from '@tanstack/angular-table'
+import { isDevMode } from '@angular/core';
+
+// Create a reusable options object with features pre-configured
+const baseOptions = tableOptions({
+ _features: tableFeatures({ rowSortingFeature }),
+ debugTable: isDevMode()
+})
+
+class TableCmp {
+ readonly table = injectTable(() => ({
+ ...baseOptions,
+ columns: this.columns,
+ data: this.data(),
+ _rowModels: {},
+ }))
+}
+```
+
+### Composing Partial Options
+
+`tableOptions()` allows you to omit certain required fields (like `data`, `columns`, or `_features`) when creating partial configurations:
+
+```ts
+import {
+ tableOptions,
+ tableFeatures,
+ rowSortingFeature,
+ columnFilteringFeature,
+ createSortedRowModel,
+ createFilteredRowModel,
+ filterFns,
+ sortFns,
+} from '@tanstack/angular-table'
+
+// Partial options without data or columns
+const featureOptions = tableOptions({
+ _features: tableFeatures({
+ rowSortingFeature,
+ columnFilteringFeature,
+ }),
+ _rowModels: {
+ sortedRowModel: createSortedRowModel(sortFns),
+ filteredRowModel: createFilteredRowModel(filterFns),
+ },
+})
+```
+
+```ts
+import { injectTable, tableOptions, createPaginatedRowModel } from '@tanstack/angular-table'
+
+// Another partial without _features (inherits from spread)
+const paginationDefaults = tableOptions({
+ _rowModels: {
+ paginatedRowModel: createPaginatedRowModel(),
+ },
+ initialState: {
+ pagination: { pageIndex: 0, pageSize: 25 },
+ },
+})
+
+class TableCmp {
+ readonly table = injectTable(() => ({
+ ...featureOptions,
+ ...paginationDefaults,
+ columns: this.columns,
+ data: this.data(),
+ }))
+}
+```
+
+### Using with `createTableHook`
+
+`tableOptions()` pairs well with `createTableHook` for building composable table factories:
+
+```ts
+import {
+ createTableHook,
+ tableOptions,
+ tableFeatures,
+ rowSortingFeature,
+ rowPaginationFeature,
+ createSortedRowModel,
+ createPaginatedRowModel,
+ sortFns,
+} from '@tanstack/angular-table'
+
+const sharedOptions = tableOptions({
+ _features: tableFeatures({ rowSortingFeature, rowPaginationFeature }),
+ _rowModels: {
+ sortedRowModel: createSortedRowModel(sortFns),
+ paginatedRowModel: createPaginatedRowModel(),
+ },
+})
+
+const { injectAppTable } = createTableHook(sharedOptions)
+```
+
+---
+
+## `createTableHook`: Composable Table Patterns
+
+**This is an advanced, optional feature.** You don't need to use `createTableHook`—`injectTable` is sufficient for most use cases.
+
+For applications with multiple tables sharing the same configuration, `createTableHook` lets you define features, row models, and reusable components once.
+
+For full setup and patterns, see the [Table composition Guide](./table-composition.md).
+
+---
+
+## Other Breaking Changes
+
+### Column Pinning Option Split
+
+The `enablePinning` option has been split into separate options:
+
+```ts
+// v8
+enablePinning: true
+
+// v9
+enableColumnPinning: true
+enableRowPinning: true
+```
+
+### Removed Internal APIs
+
+All internal APIs prefixed with `_` have been removed. If you were using any of these, use their public equivalents.
+
+### Column Sizing vs. Column Resizing Split
+
+In v8, column sizing and resizing were combined in a single feature. In v9, they've been split into separate features for better tree-shaking.
+
+| v8 | v9 |
+|----|-----|
+| `ColumnSizing` (combined feature) | `columnSizingFeature` + `columnResizingFeature` |
+| `columnSizingInfo` state | `columnResizing` state |
+| `setColumnSizingInfo()` | `setColumnResizing()` |
+| `onColumnSizingInfoChange` option | `onColumnResizingChange` option |
+
+If you only need column sizing (fixed widths) without interactive resizing, you can import just `columnSizingFeature`. If you need drag-to-resize functionality, import both.
+
+### Sorting API Renames
+
+Sorting-related APIs have been renamed for consistency:
+
+| v8 | v9 |
+|----|-----|
+| `sortingFn` (column def option) | `sortFn` |
+| `column.getSortingFn()` | `column.getSortFn()` |
+| `column.getAutoSortingFn()` | `column.getAutoSortFn()` |
+| `SortingFn` type | `SortFn` type |
+| `SortingFns` interface | `SortFns` interface |
+| `sortingFns` (built-in functions) | `sortFns` |
+
+Update your column definitions.
+
+### Row API Changes
+
+Some row APIs have changed from private to public:
+
+| v8 | v9 |
+|----|-----|
+| `row._getAllCellsByColumnId()` (private) | `row.getAllCellsByColumnId()` (public) |
+
+---
+
+## TypeScript Changes Summary
+
+### Type Generics
+
+Most types now require a `TFeatures` parameter:
+
+```txt
+// v8
+type Column
+type ColumnDef
+type Table
+type Row
+type Cell
+
+// v9
+type Column
+type ColumnDef
+type Table
+type Row
+type Cell
+```
+
+### Using `typeof _features`
+
+The easiest way to get the `TFeatures` type is with `typeof`:
+
+```ts
+const _features = tableFeatures({
+ rowSortingFeature,
+ columnFilteringFeature,
+})
+
+type MyFeatures = typeof _features
+
+const columns: ColumnDef[] = [...]
+```
+
+### Using `StockFeatures`
+
+If using `stockFeatures`, use the `StockFeatures` type:
+
+```ts
+import type { StockFeatures, ColumnDef } from '@tanstack/angular-table'
+
+const columns: ColumnDef[] = [...]
+```
+
+### `ColumnMeta` Generic Change
+
+If you're using module augmentation to extend `ColumnMeta`, note that it now requires a `TFeatures` parameter.
+
+### `RowData` Type Restriction
+
+The `RowData` type is now more restrictive.
+
+---
+
+## Migration Checklist
+
+- [ ] Update your table setup to v9 and define `_features` using `tableFeatures()` (or use `stockFeatures`)
+- [ ] Migrate `get*RowModel()` options to `_rowModels`
+- [ ] Update row model factories to include `Fns` parameters where needed
+- [ ] Update TypeScript types to include `TFeatures` generic
+- [ ] Update state access: `table.getState()` → `table.store.state`
+- [ ] Update `createColumnHelper()` → `createColumnHelper()`
+- [ ] Replace `enablePinning` with `enableColumnPinning`/`enableRowPinning` if used
+- [ ] Rename `sortingFn` → `sortFn` in column definitions
+- [ ] Split column sizing/resizing: use both `columnSizingFeature` and `columnResizingFeature` if needed
+- [ ] Rename `columnSizingInfo` state → `columnResizing` (and related options)
+- [ ] Update `ColumnMeta` module augmentation to include `TFeatures` generic (if used)
+- [ ] (Optional) Use `tableOptions()` for composable configurations
+- [ ] (Optional) Use `createTableHook` for reusable table patterns
+
+---
+
+## Examples
+
+Check out these examples to see v9 patterns in action:
+- [Basic](../examples/basic)
+- [Basic (App Table)](../examples/basic-app-table)
+- [Filters](../examples/filters)
+- [Column Ordering](../examples/column-ordering)
+- [Column Pinning](../examples/column-pinning)
+- [Column Visibility](../examples/column-visibility)
+- [Expanding](../examples/expanding)
+- [Grouping](../examples/grouping)
+- [Row Selection](../examples/row-selection)
+- [Composable Tables](../examples/composable-tables)
+
+
+
diff --git a/docs/framework/angular/guide/rendering.md b/docs/framework/angular/guide/rendering.md
new file mode 100644
index 0000000000..d4f5e9190b
--- /dev/null
+++ b/docs/framework/angular/guide/rendering.md
@@ -0,0 +1,421 @@
+---
+title: Rendering components
+---
+
+The `@tanstack/angular-table` adapter provides structural directives and dependency injection primitives for rendering table content in Angular templates.
+
+## FlexRender
+
+[`FlexRender`](../reference/variables/FlexRender) is the rendering primitive.
+It is exported as a tuple of two directives:
+
+- [`FlexRenderDirective`](../reference/classes/FlexRenderDirective) — the base structural directive (`*flexRender`)
+- [`FlexRenderCell`](../reference/classes/FlexRenderCell.md) — shorthand directives (`*flexRenderCell`, `*flexRenderHeader`, `*flexRenderFooter`)
+
+Import `FlexRender` to get both:
+
+```ts
+import { Component } from '@angular/core'
+import { FlexRender } from '@tanstack/angular-table'
+
+@Component({
+ imports: [FlexRender],
+ templateUrl: './app.html',
+})
+export class AppComponent {}
+```
+
+### How it works
+
+`FlexRender` is an Angular **structural directive**. Internally, it resolves the column definition's `header`, `cell`, or `footer` function and renders the result using [`ViewContainerRef`](https://angular.dev/api/core/ViewContainerRef):
+
+- **Primitives** (`string`, `number`): rendered via `createEmbeddedView` into the host `ng-template`. The value is exposed as the template's implicit context (`let value`).
+- **`TemplateRef`**: rendered via `createEmbeddedView`. The render context (`CellContext`, `HeaderContext`) is passed as `$implicit`.
+- **`flexRenderComponent(...)`**: rendered via `createComponent` with explicit `inputs`, `outputs`, `bindings`, `directives`, and `injector`.
+- **Component type** (`Type`): rendered via [`createComponent`](https://angular.dev/api/core/ViewContainerRef#createComponent). All properties from the render context are set as component inputs through [`ComponentRef.setInput`](https://angular.dev/api/core/ComponentRef#setInput).
+
+Column definition functions (`header`, `cell`, `footer`) are called inside [`runInInjectionContext`](https://angular.dev/api/core/runInInjectionContext), which means you can call `inject()`, use signals, and access DI tokens directly in your render logic.
+
+## Cell rendering
+
+Prefer the shorthand directives for standard rendering:
+
+| Directive | Input | Column definition |
+|---|---|---|
+| `*flexRenderCell` | `Cell` | `columnDef.cell` |
+| `*flexRenderHeader` | `Header` | `columnDef.header` |
+| `*flexRenderFooter` | `Header` | `columnDef.footer` |
+
+Each shorthand resolves the correct column definition function and render context automatically through a `computed` signal, so no manual `props` mapping is needed.
+
+### Example
+
+```html
+
+ @for (headerGroup of table.getHeaderGroups(); track headerGroup.id) {
+
+ @for (header of headerGroup.headers; track header.id) {
+
+ @for (cell of row.getVisibleCells(); track cell.id) {
+
+
+ {{ value }}
+
+
+ }
+
+ }
+
+```
+
+## Cell rendering with custom props
+
+When you need full control over the `props` passed to the render function, use `*flexRender` directly.
+
+`FlexRenderDirective` accepts two inputs:
+
+- `flexRender` — the render definition (a column def function, a string, a `TemplateRef`, a component type, or a `flexRenderComponent(...)` wrapper)
+- `flexRenderProps` — the props object passed to the render function and used as the implicit template context
+
+Standard usage:
+
+```html
+
+ {{ rendered }}
+
+```
+
+You can pass a custom props object to override the default context shape:
+
+```html
+
+ {{ rendered }}
+
+```
+
+Inside rendered components, the full props object is available via [`injectFlexRenderContext()`](#injectflexrendercontext).
+
+## Component rendering
+
+You can render Angular components from column definitions in two ways:
+
+### Using `flexRenderComponent`
+
+[`flexRenderComponent(component, options?)`](../reference/functions/flexRenderComponent) wraps a component type with explicit options for `inputs`, `outputs`, `injector`, `bindings`, and `directives`.
+
+Use this when you need to:
+
+- pass custom inputs not derived from the render context
+- subscribe to component outputs
+- provide a custom `Injector`
+- use creation-time `bindings` (Angular v20+)
+- apply host directives and binding values at runtime
+
+```ts
+import { flexRenderComponent, type ColumnDef } from '@tanstack/angular-table'
+
+const columns: ColumnDef[] = [
+ {
+ id: 'custom-cell',
+ cell: ctx =>
+ flexRenderComponent(CustomCellComponent, {
+ inputs: {
+ content: ctx.row.original.firstName,
+ },
+ outputs: {
+ clicked: value => {
+ console.log(value)
+ },
+ },
+ }),
+ },
+]
+```
+
+#### How inputs and outputs work
+
+**Inputs** are applied through [`ComponentRef.setInput(key, value)`](https://angular.dev/api/core/ComponentRef#setInput). This works with both `input()` signals and `@Input()` decorators. Inputs are diffed on every change detection cycle using `KeyValueDiffers` — only changed values trigger `setInput`.
+
+For object-like inputs, updates are reference-based: if the object reference is stable, Angular's default input equality semantics prevent unnecessary updates.
+
+**Outputs** work through `OutputEmitterRef` subscriptions. The factory reads the component instance property by name, checks that it is an `OutputEmitterRef`, and subscribes to it. When the output emits, the corresponding callback from `outputs` is invoked. Subscriptions are cleaned up automatically when the component is destroyed.
+
+#### `bindings` API (Angular v20+)
+
+`flexRenderComponent` also accepts `bindings` and `directives`, forwarded directly to [`ViewContainerRef.createComponent`](https://angular.dev/api/core/ViewContainerRef#createComponent) at creation time.
+
+This supports Angular programmatic rendering APIs for passing host directives and binding values at runtime.
+
+Unlike `inputs`/`outputs` (which are applied imperatively after creation), `bindings` are applied **at creation time** — they participate in the component's initial change detection cycle.
+
+```ts
+import {
+ inputBinding,
+ outputBinding,
+ twoWayBinding,
+ signal,
+} from '@angular/core'
+import { flexRenderComponent } from '@tanstack/angular-table'
+
+readonly name = signal('Ada')
+
+cell: () =>
+ flexRenderComponent(EditableNameCellComponent, {
+ bindings: [
+ inputBinding('value', this.name),
+ outputBinding('valueChange', value => {
+ console.log('changed', value)
+ }),
+ twoWayBinding('value', this.name),
+ ],
+ })
+```
+
+> Avoid mixing `bindings` with `inputs`/`outputs` on the same property. `bindings` are applied at creation, while `inputs`/`outputs` are applied post-creation — mixing them can lead to double initialization or conflicting values.
+
+See the Angular docs for details:
+
+- [Programmatic rendering — Binding inputs/outputs/directives](https://angular.dev/guide/components/programmatic-rendering#binding-inputs-outputs-and-setting-host-directives-at-creation)
+- [`inputBinding`](https://angular.dev/api/core/inputBinding), [`outputBinding`](https://angular.dev/api/core/outputBinding), [`twoWayBinding`](https://angular.dev/api/core/twoWayBinding)
+
+### Returning a component class
+
+Return a component class from `header`, `cell`, or `footer`.
+
+The render context properties (`table`, `column`, `header`, `cell`, `row`, `getValue`, etc.) are automatically set as component inputs via `ComponentRef.setInput(...)`.
+
+Define input signals matching the context property names you need:
+
+```ts
+import { Component, input } from '@angular/core'
+import type { ColumnDef, Table, CellContext } from '@tanstack/angular-table'
+
+const columns: ColumnDef[] = [
+ {
+ id: 'select',
+ header: () => TableHeadSelectionComponent,
+ cell: () => TableRowSelectionComponent,
+ },
+]
+
+@Component({
+ template: `
+
+ `,
+})
+export class TableHeadSelectionComponent {
+ readonly table = input.required
>();
+ // column = input.required>()
+ // header = input.required>()
+}
+```
+
+Only properties declared with `input()` / `input.required()` are set — other context properties are silently ignored. You can also access the full context via [`injectFlexRenderContext()`](#injectflexrendercontext).
+
+## TemplateRef rendering
+
+You can return a `TemplateRef` from column definitions. The render context is passed as the template's `$implicit` context.
+
+Use `viewChild(...)` to capture template references:
+
+```ts
+import { Component, TemplateRef, viewChild } from '@angular/core'
+import type {
+ CellContext,
+ ColumnDef,
+ HeaderContext,
+} from '@tanstack/angular-table'
+
+@Component({
+ template: `
+
+ {{ context.column.id }}
+
+
+
+ {{ context.getValue() }}
+
+ `,
+})
+export class AppComponent {
+ readonly customHeader =
+ viewChild.required }>>(
+ 'customHeader',
+ )
+ readonly customCell =
+ viewChild.required }>>(
+ 'customCell',
+ )
+
+ readonly columns: ColumnDef[] = [
+ {
+ id: 'templated',
+ header: () => this.customHeader(),
+ cell: () => this.customCell(),
+ },
+ ]
+}
+```
+
+`TemplateRef` rendering uses `createEmbeddedView` with an injector that includes the [DI context tokens](#dependency-injection). For reusable render blocks shared across multiple screens, prefer standalone components over `TemplateRef`.
+
+## Dependency injection
+
+`FlexRender` automatically provides DI tokens when rendering components and templates. These tokens are created in the `#getInjector` method of the renderer, which builds a child `Injector` with the render context properties.
+
+### `injectFlexRenderContext`
+
+[`injectFlexRenderContext()`](../reference/functions/injectFlexRenderContext) returns the full props object passed to `*flexRender`. The return type depends on the column definition slot:
+
+- In a `cell` definition: `CellContext`
+- In a `header`/`footer` definition: `HeaderContext`
+
+```ts
+import { Component } from '@angular/core'
+import {
+ injectFlexRenderContext,
+ type CellContext,
+} from '@tanstack/angular-table'
+
+@Component({
+ template: `
+ {{ context.getValue() }}
+
+ `,
+})
+export class InteractiveCellComponent {
+ readonly context = injectFlexRenderContext>()
+}
+```
+
+Internally, the renderer wraps the context in a `Proxy` so that property access always reflects the latest values, even after re-renders.
+
+### Context directives
+
+Three optional directives let you expose table, header, and cell context to **any descendant** in the template — not just components rendered by `*flexRender`.
+
+This eliminates prop drilling: instead of passing data through multiple `input()` layers, any nested component or directive can inject the context directly.
+
+| Directive | Selector | Token | Inject helper |
+|---|---|---|---|
+| [`TanStackTable`](../reference/functions/injectTableContext) | `[tanStackTable]` | `TanStackTableToken` | `injectTableContext()` |
+| [`TanStackTableHeader`](../reference/functions/injectTableHeaderContext) | `[tanStackTableHeader]` | `TanStackTableHeaderToken` | `injectTableHeaderContext()` |
+| [`TanStackTableCell`](../reference/functions/injectTableCellContext) | `[tanStackTableCell]` | `TanStackTableCellToken` | `injectTableCellContext()` |
+
+Import them alongside `FlexRender`:
+
+```ts
+import {
+ FlexRender,
+ TanStackTable,
+ TanStackTableHeader,
+ TanStackTableCell,
+} from '@tanstack/angular-table'
+
+@Component({
+ imports: [FlexRender, TanStackTable, TanStackTableHeader, TanStackTableCell],
+ templateUrl: './app.html',
+})
+export class AppComponent {}
+```
+
+Apply them in the template to establish injection scopes:
+
+```html
+
+```
+
+Each directive uses Angular's `providers` array to register a factory that reads its own input signal.
+
+This means the token is scoped to the directive's host element and its descendants. Multiple `[tanStackTableCell]` directives on different elements provide independent contexts.
+
+### Automatic token injection in FlexRender
+
+When `FlexRender` renders a component or template, it also provides DI tokens automatically based on the render context shape. In the renderer's `#getInjector` method, if the context object contains `table`, `cell`, or `header` properties, the corresponding `TanStackTableToken`, `TanStackTableCellToken`, or `TanStackTableHeaderToken` tokens are provided in the child injector.
+
+This means that even **without** the context directives, components rendered via `*flexRender` can use `injectTableContext()`, `injectTableCellContext()`, and `injectTableHeaderContext()`. The context directives are only needed for components that live **outside** the `*flexRender` rendering tree (e.g. sibling components in the same `
`).
diff --git a/docs/framework/angular/guide/table-composition.md b/docs/framework/angular/guide/table-composition.md
new file mode 100644
index 0000000000..f56e9a8520
--- /dev/null
+++ b/docs/framework/angular/guide/table-composition.md
@@ -0,0 +1,295 @@
+---
+title: Table Composition (createTableHook)
+---
+
+`createTableHook` is a convenience API for creating reusable, type-safe table configurations with pre-bound components. It is inspired by [TanStack Form's `createFormHook`](https://tanstack.com/form/latest/docs/framework/react/guides/form-composition) — a pattern where you define shared infrastructure once and consume it across your application with minimal boilerplate.
+
+> **When to use it:** Use `createTableHook` when you have multiple tables that share the same configuration (features, row models, and reusable components). For a single table, `injectTable` is sufficient.
+
+## Examples
+
+- [Composable Tables](../examples/composable-tables) — Two tables (Users and Products) sharing the same `createTableHook` configuration, with table/cell/header components, sorting, filtering, and pagination.
+- [Basic App Table](../examples/basic-app-table) — Minimal example using `createTableHook` with no pre-bound components.
+
+### createTableHook
+
+`createTableHook` centralizes your table configuration into a single factory call. It returns a set of typed functions — `injectAppTable`, `createAppColumnHelper`, and pre-typed injection helpers — that you use instead of the base APIs.
+
+## Setup
+
+Call `createTableHook` with your shared configuration and destructure the returned utilities:
+
+```ts
+// table.ts — shared table infrastructure
+
+import {
+ createTableHook,
+ tableFeatures,
+ columnFilteringFeature,
+ createFilteredRowModel,
+ createPaginatedRowModel,
+ createSortedRowModel,
+ filterFns,
+ rowPaginationFeature,
+ rowSortingFeature,
+ sortFns,
+} from '@tanstack/angular-table'
+
+import { PaginationControls, RowCount, TableToolbar } from './components/table-components'
+import { TextCell, NumberCell, StatusCell, ProgressCell } from './components/cell-components'
+import { SortIndicator, ColumnFilter } from './components/header-components'
+
+export const {
+ createAppColumnHelper,
+ injectAppTable,
+ injectTableContext,
+ injectTableCellContext,
+ injectTableHeaderContext,
+} = createTableHook({
+ // Features and row models are shared across all tables
+ _features: tableFeatures({
+ columnFilteringFeature,
+ rowPaginationFeature,
+ rowSortingFeature,
+ }),
+ _rowModels: {
+ sortedRowModel: createSortedRowModel(sortFns),
+ filteredRowModel: createFilteredRowModel(filterFns),
+ paginatedRowModel: createPaginatedRowModel(),
+ },
+ // Default table options applied to every table
+ getRowId: (row) => row.id,
+
+ // Pre-bound component registries
+ tableComponents: {
+ PaginationControls,
+ RowCount,
+ TableToolbar,
+ },
+ cellComponents: {
+ TextCell,
+ NumberCell,
+ StatusCell,
+ ProgressCell,
+ },
+ headerComponents: {
+ SortIndicator,
+ ColumnFilter,
+ },
+})
+```
+
+This single file becomes the source of truth for your application's table infrastructure.
+
+## What `createTableHook` returns
+
+| Export | Description |
+|---|---|
+| `injectAppTable` | A wrapper around `injectTable` that merges default options and attaches component registries. Returns an `AppAngularTable` with table/cell/header components available directly on the instance. |
+| `createAppColumnHelper` | A typed column helper where `cell`, `header`, and `footer` definitions receive enhanced context types with the registered components. |
+| `injectTableContext` | Pre-typed `injectTableContext()` bound to your feature set. |
+| `injectTableCellContext` | Pre-typed `injectTableCellContext()` bound to your feature set. |
+| `injectTableHeaderContext` | Pre-typed `injectTableHeaderContext()` bound to your feature set. |
+| `injectFlexRenderCellContext` | Pre-typed `injectFlexRenderContext()` for cell context. |
+| `injectFlexRenderHeaderContext` | Pre-typed `injectFlexRenderContext()` for header context. |
+
+## Component registries
+
+`createTableHook` accepts three component registries that map string keys to Angular components or render functions:
+
+### `tableComponents`
+
+Components that need access to the **table instance**. These are attached directly to the `AppAngularTable` object returned by `injectAppTable`, so you can reference them in templates as `table.PaginationControls`, `table.RowCount`, etc.
+
+Use `injectTableContext()` inside these components to access the table:
+
+```ts
+@Component({
+ selector: 'app-pagination-controls',
+ template: `
+
+
+
+
+ `,
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class PaginationControls {
+ readonly table = injectTableContext()
+}
+```
+
+Render table components via Angular `NgComponentOutlet`:
+
+```html
+
+
+
+```
+
+### `cellComponents`
+
+Components that render **cell content**. These are attached to the `Cell` prototype, so they are available in column definitions through the enhanced `AppCellContext`:
+
+```ts
+const columnHelper = createAppColumnHelper()
+
+const columns = columnHelper.columns([
+ columnHelper.accessor('firstName', {
+ header: 'First Name',
+ cell: ({ cell }) => cell.TextCell,
+ }),
+ columnHelper.accessor('age', {
+ header: 'Age',
+ cell: ({ cell }) => cell.NumberCell,
+ }),
+ columnHelper.accessor('status', {
+ header: 'Status',
+ cell: ({ cell }) => cell.StatusCell,
+ }),
+])
+```
+
+Use `injectTableCellContext()` or `injectFlexRenderContext()` inside cell components:
+
+```ts
+@Component({
+ selector: 'span',
+ template: `{{ cell().getValue() }}`,
+})
+export class TextCell {
+ readonly cell = injectTableCellContext()
+}
+```
+
+### `headerComponents`
+
+Components or render functions that render **header/footer content**. These are attached to the `Header` prototype and available through the enhanced `AppHeaderContext`:
+
+```ts
+// Render functions work too — they run in injection context
+export function SortIndicator(): string | null {
+ const header = injectTableHeaderContext()
+ const sorted = header().column.getIsSorted()
+ if (!sorted) return null
+ return sorted === 'asc' ? '🔼' : '🔽'
+}
+```
+
+Access header components in the template via `table.appHeader(header)`:
+
+```html
+@for (_header of headerGroup.headers; track _header.id) {
+ @let header = table.appHeader(_header);
+
+ {{ value }}
+
+
+
+
+}
+```
+
+## Using `injectAppTable`
+
+`injectAppTable` is a wrapper around `injectTable`. It merges the default options from `createTableHook` with the per-table options, and returns an `AppAngularTable` — the standard table instance augmented with:
+
+- **Table components** directly on the table object (`table.PaginationControls`, `table.TableToolbar`, etc.)
+- **`table.appCell(cell)`** — utility type functions for templates that wraps a `Cell` with the registered `cellComponents`
+- **`table.appHeader(header)`** — utility type functions for templates that wraps a `Header` with the registered `headerComponents`
+- **`table.appFooter(footer)`** — utility type functions for templates that wraps a `Header` (footer) with the registered `headerComponents`
+
+You do not need to pass `_features` or `_rowModels` — they are inherited from the hook configuration:
+
+```ts
+@Component({
+ selector: 'users-table',
+ templateUrl: './users-table.html',
+ imports: [FlexRender, TanStackTable, TanStackTableHeader, TanStackTableCell, NgComponentOutlet],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class UsersTable {
+ readonly data = signal(makeData(100))
+
+ readonly columns = columnHelper.columns([
+ columnHelper.accessor('firstName', {
+ header: 'First Name',
+ cell: ({ cell }) => cell.TextCell,
+ }),
+ // ...
+ ])
+
+ // No need to specify _features, _rowModels, ... — they come from createTableHook
+ table = injectAppTable(() => ({
+ columns: this.columns,
+ data: this.data(),
+ }))
+}
+```
+
+## Using `createAppColumnHelper`
+
+`createAppColumnHelper()` returns a column helper identical to `createColumnHelper` at runtime, but with enhanced types: the `cell`, `header`, and `footer` definition callbacks receive `AppCellContext` / `AppHeaderContext` instead of the base context types.
+
+This means TypeScript knows about your registered components and provides autocompletion:
+
+```ts
+const columnHelper = createAppColumnHelper()
+
+columnHelper.accessor('firstName', {
+ cell: ({ cell }) => {
+ // ✅ TypeScript knows about TextCell, NumberCell, StatusCell, etc.
+ return cell.TextCell
+ },
+ header: ({ header }) => {
+ // ✅ TypeScript knows about SortIndicator, ColumnFilter, etc.
+ return flexRenderComponent(header.SortIndicator)
+ },
+})
+```
+
+You can also use `flexRenderComponent(...)` to wrap the component with custom inputs/outputs:
+
+```ts
+columnHelper.accessor('firstName', {
+ cell: ({ cell }) => flexRenderComponent(cell.TextCell),
+ footer: ({ header }) => flexRenderComponent(header.FooterColumnId),
+})
+```
+
+## Multiple table configurations
+
+You can call `createTableHook` multiple times to create different table configurations for different parts of your application. Each call returns an independent set of utilities with its own feature set and component registries:
+
+```ts
+// admin-table.ts — tables with editing capabilities
+export const {
+ injectAppTable: injectAdminTable,
+ createAppColumnHelper: createAdminColumnHelper,
+} = createTableHook({
+ _features: tableFeatures({ rowSortingFeature, columnFilteringFeature }),
+ _rowModels: { /* ... */ },
+ cellComponents: { EditableCell, DeleteButton },
+})
+
+// readonly-table.ts — simpler read-only tables
+export const {
+ injectAppTable: injectReadonlyTable,
+ createAppColumnHelper: createReadonlyColumnHelper,
+} = createTableHook({
+ _features: tableFeatures({ rowSortingFeature }),
+ _rowModels: { /* ... */ },
+ cellComponents: { TextCell, NumberCell },
+})
+```
+
+## Examples
+
+- [Composable Tables](../examples/composable-tables) — Full example with two tables sharing the same `createTableHook` configuration.
+- [Basic App Table](../examples/basic-app-table) — Minimal example with no pre-bound components.
diff --git a/docs/framework/angular/guide/table-state.md b/docs/framework/angular/guide/table-state.md
new file mode 100644
index 0000000000..100e5fd601
--- /dev/null
+++ b/docs/framework/angular/guide/table-state.md
@@ -0,0 +1,217 @@
+---
+title: Table State (Angular) Guide
+---
+
+## Table State (Angular) Guide
+
+TanStack Table has a simple underlying internal state management system to store and manage the state of the table. It also lets you selectively pull out any state that you need to manage in your own state management. This guide will walk you through the different ways in which you can interact with and manage the state of the table.
+
+### Accessing Table State
+
+You do not need to set up anything special in order for the table state to work. If you pass nothing into either `state`, `initialState`, or any of the `on[State]Change` table options, the table will manage its own state internally. You can access any part of this internal state by using the `table.store.state` table instance API.
+
+```ts
+table = injectTable(() => ({
+ columns: this.columns,
+ data: this.data(),
+ //...
+}))
+
+someHandler() {
+ console.log(this.table.store.state) //access the entire internal state
+ console.log(this.table.store.state.rowSelection) //access just the row selection state
+}
+```
+
+### Custom Initial State
+
+If all you need to do for certain states is customize their initial default values, you still do not need to manage any of the state yourself. You can simply set values in the `initialState` option of the table instance.
+
+```jsx
+table = injectTable(() => ({
+ columns: this.columns,
+ data: this.data(),
+ initialState: {
+ columnOrder: ['age', 'firstName', 'lastName'], //customize the initial column order
+ columnVisibility: {
+ id: false //hide the id column by default
+ },
+ expanded: true, //expand all rows by default
+ sorting: [
+ {
+ id: 'age',
+ desc: true //sort by age in descending order by default
+ }
+ ]
+ },
+ //...
+}))
+```
+
+> **Note**: Only specify each particular state in either `initialState` or `state`, but not both. If you pass in a particular state value to both `initialState` and `state`, the initialized state in `state` will take overwrite any corresponding value in `initialState`.
+
+### Controlled State
+
+If you need easy access to the table state in other areas of your application, TanStack Table makes it easy to control and manage any or all of the table state in your own state management system. You can do this by passing in your own state and state management functions to the `state` and `on[State]Change` table options.
+
+#### Individual Controlled State
+
+You can control just the state that you need easy access to. You do NOT have to control all of the table state if you do not need to. It is recommended to only control the state that you need on a case-by-case basis.
+
+In order to control a particular state, you need to both pass in the corresponding `state` value and the `on[State]Change` function to the table instance.
+
+Let's take filtering, sorting, and pagination as an example in a "manual" server-side data fetching scenario. You can store the filtering, sorting, and pagination state in your own state management, but leave out any other state like column order, column visibility, etc. if your API does not care about those values.
+
+```ts
+import {signal} from '@angular/core';
+import {SortingState, ColumnFiltersState, PaginationState} from '@tanstack/angular-table'
+import {toObservable} from "@angular/core/rxjs-interop";
+import {combineLatest, switchMap} from 'rxjs';
+
+class TableComponent {
+ readonly columnFilters = signal([]) //no default filters
+ readonly sorting = signal([
+ {
+ id: 'age',
+ desc: true, //sort by age in descending order by default
+ }
+ ])
+ readonly pagination = signal({
+ pageIndex: 0,
+ pageSize: 15
+ })
+
+ //Use our controlled state values to fetch data
+ readonly data$ = combineLatest({
+ filters: toObservable(this.columnFilters),
+ sorting: toObservable(this.sorting),
+ pagination: toObservable(this.pagination)
+ }).pipe(
+ switchMap(({filters, sorting, pagination}) => fetchData(filters, sorting, pagination))
+ )
+ readonly data = toSignal(this.data$);
+
+ readonly table = injectTable(() => ({
+ columns: this.columns,
+ data: this.data(),
+ //...
+ state: {
+ columnFilters: this.columnFilters(), //pass controlled state back to the table (overrides internal state)
+ sorting: this.sorting(),
+ pagination: this.pagination(),
+ },
+ onColumnFiltersChange: updater => { //hoist columnFilters state into our own state management
+ updater instanceof Function
+ ? this.columnFilters.update(updater)
+ : this.columnFilters.set(updater)
+ },
+ onSortingChange: updater => {
+ updater instanceof Function
+ ? this.sorting.update(updater)
+ : this.sorting.set(updater)
+ },
+ onPaginationChange: updater => {
+ updater instanceof Function
+ ? this.pagination.update(updater)
+ : this.pagination.set(updater)
+ },
+ }))
+}
+
+//...
+```
+
+#### Fully Controlled State
+
+Alternatively, you can control the entire table state with the `onStateChange` table option. It will hoist out the entire table state into your own state management system. Be careful with this approach, as you might find that raising some frequently changing state values up a component tree, like `columnSizingInfo` state`, might cause bad performance issues.
+
+A couple of more tricks may be needed to make this work. If you use the `onStateChange` table option, the initial values of the `state` must be populated with all of the relevant state values for all of the features that you want to use. You can either manually type out all of the initial state values, or use a constructor in a special way as shown below.
+
+```ts
+
+
+class TableComponent {
+ // create an empty table state, we'll override it later
+ readonly state = signal({} as TableState);
+
+ // create a table instance with default state values
+ readonly table = injectTable(() => ({
+ columns: this.columns,
+ data: this.data(),
+ // our fully controlled state overrides the internal state
+ state: this.state(),
+ onStateChange: updater => {
+ // any state changes will be pushed up to our own state management
+ this.state.set(
+ updater instanceof Function ? updater(this.state()) : updater
+ )
+ }
+ }))
+
+ constructor() {
+ // set the initial table state
+ this.state.set({
+ // populate the initial state with all of the default state values
+ // from the table instance
+ ...this.table.initialState,
+ pagination: {
+ pageIndex: 0,
+ pageSize: 15, // optionally customize the initial pagination state.
+ },
+ })
+ }
+}
+```
+
+### On State Change Callbacks
+
+So far, we have seen the `on[State]Change` and `onStateChange` table options work to "hoist" the table state changes into our own state management. However, there are a few things about these using these options that you should be aware of.
+
+#### 1. **State Change Callbacks MUST have their corresponding state value in the `state` option**.
+
+Specifying an `on[State]Change` callback tells the table instance that this will be a controlled state. If you do not specify the corresponding `state` value, that state will be "frozen" with its initial value.
+
+```ts
+class TableComponent {
+ sorting = signal([])
+
+ table = injectTable(() => ({
+ columns: this.columns,
+ data: this.data(),
+ //...
+ state: {
+ sorting: this.sorting(), // required because we are using `onSortingChange`
+ },
+ onSortingChange: updater => { // makes the `state.sorting` controlled
+ updater instanceof Function
+ ? this.sorting.update(updater)
+ : this.sorting.set(updater)
+ }
+ }))
+}
+```
+
+#### 2. **Updaters can either be raw values or callback functions**.
+
+The `on[State]Change` and `onStateChange` callbacks work exactly like the `setState` functions in React. The updater values can either be a new state value or a callback function that takes the previous state value and returns the new state value.
+
+What implications does this have? It means that if you want to add in some extra logic in any of the `on[State]Change` callbacks, you can do so, but you need to check whether or not the new incoming updater value is a function or value.
+
+This is why you will see the `updater instanceof Function ? this.state.update(updater) : this.state.set(updater)` pattern in the examples above. This pattern checks if the updater is a function, and if it is, it calls the function with the previous state value to get the new state value, or the signal will require `signal.update` to be called with the updater instead of `signal.set`.
+
+### State Types
+
+All complex states in TanStack Table have their own TypeScript types that you can import and use. This can be handy for ensuring that you are using the correct data structures and properties for the state values that you are controlling.
+
+```ts
+import {injectTable, type SortingState} from '@tanstack/angular-table'
+
+class TableComponent {
+ readonly sorting = signal([
+ {
+ id: 'age', // you should get autocomplete for the `id` and `desc` properties
+ desc: true,
+ }
+ ])
+}
+```
diff --git a/docs/framework/angular/reference/classes/FlexRenderCell.md b/docs/framework/angular/reference/classes/FlexRenderCell.md
new file mode 100644
index 0000000000..b54ae8577e
--- /dev/null
+++ b/docs/framework/angular/reference/classes/FlexRenderCell.md
@@ -0,0 +1,101 @@
+---
+id: FlexRenderCell
+title: FlexRenderCell
+---
+
+# Class: FlexRenderCell\
+
+Defined in: [helpers/flexRenderCell.ts:62](https://github.com/TanStack/table/blob/main/packages/angular-table/src/helpers/flexRenderCell.ts#L62)
+
+Simplified directive wrapper of `*flexRender`.
+
+Use this utility component to render headers, cells, or footers with custom markup.
+
+Only one prop (`cell`, `header`, or `footer`) may be passed based on the used selector.
+
+## Examples
+
+```html
+