Docs
CodeRabbit
Cloudflare
Railway
SerpAPI
OpenRouter
WorkOS
Netlify
AG Grid
Clerk
Sentry
Unkey
Prisma
Electric
CodeRabbit
Cloudflare
Railway
SerpAPI
OpenRouter
WorkOS
Netlify
AG Grid
Clerk
Sentry
Unkey
Prisma
Electric
Table API Reference
Column API Reference
Row API Reference
Cell API Reference
Header API Reference
Features API Reference
Static Functions API Reference
Feature Guides

Faceting (Lit) Guide

Examples

Want to skip to the implementation? Check out these Lit examples:

Lit Setup

ts
import { LitElement, html } from 'lit'
import { customElement, state } from 'lit/decorators.js'
import { TableController, tableFeatures, columnFacetingFeature, columnFilteringFeature, createFacetedRowModel, createFacetedUniqueValues, createFacetedMinMaxValues, createFilteredRowModel, filterFns } from '@tanstack/lit-table'

const features = tableFeatures({
  columnFacetingFeature,
  columnFilteringFeature,
  filteredRowModel: createFilteredRowModel(),
  facetedRowModel: createFacetedRowModel(),
  facetedUniqueValues: createFacetedUniqueValues(),
  facetedMinMaxValues: createFacetedMinMaxValues(),
  filterFns,
})

@customElement('my-table')
class MyTable extends LitElement {
  @state()
  private data = defaultData

  private tableController = new TableController(this)

  protected render() {
    const table = this.tableController.table({
      features,
      columns,
      data: this.data,
    })

    return html`...`
  }
}
import { LitElement, html } from 'lit'
import { customElement, state } from 'lit/decorators.js'
import { TableController, tableFeatures, columnFacetingFeature, columnFilteringFeature, createFacetedRowModel, createFacetedUniqueValues, createFacetedMinMaxValues, createFilteredRowModel, filterFns } from '@tanstack/lit-table'

const features = tableFeatures({
  columnFacetingFeature,
  columnFilteringFeature,
  filteredRowModel: createFilteredRowModel(),
  facetedRowModel: createFacetedRowModel(),
  facetedUniqueValues: createFacetedUniqueValues(),
  facetedMinMaxValues: createFacetedMinMaxValues(),
  filterFns,
})

@customElement('my-table')
class MyTable extends LitElement {
  @state()
  private data = defaultData

  private tableController = new TableController(this)

  protected render() {
    const table = this.tableController.table({
      features,
      columns,
      data: this.data,
    })

    return html`...`
  }
}

Faceting (Lit) Guide

Faceting is a feature that generates lists of values from your table's data, either for a single column (column faceting) or across the entire table (global faceting). For example, a list of unique values can be used as search suggestions in an autocomplete filter component, or a tuple of minimum and maximum values from a column of numbers can drive a range slider filter component. The same row models power both the per-column and table-wide APIs.

Column Faceting Row Models

In order to use any of the column faceting features, add the columnFacetingFeature to your features and register the appropriate faceted row model factories as slots on tableFeatures. Faceting exists to power filter UIs, so in practice you will also register the columnFilteringFeature and a filteredRowModel slot. Without a filtered row model, the faceted row models fall back to the pre-filtered rows and the facet values will not react to other columns' filters.

ts
import {
  TableController,
  tableFeatures,
  columnFacetingFeature,
  columnFilteringFeature,
  createFacetedRowModel,
  createFacetedMinMaxValues,
  createFacetedUniqueValues,
  createFilteredRowModel,
  filterFns,
} from '@tanstack/lit-table'

const features = tableFeatures({
  columnFacetingFeature,
  columnFilteringFeature,
  filteredRowModel: createFilteredRowModel(), // facet values react to other columns' filters
  facetedRowModel: createFacetedRowModel(), // required for faceting (other faceted row models depend on this)
  facetedMinMaxValues: createFacetedMinMaxValues(), // if you need min/max values
  facetedUniqueValues: createFacetedUniqueValues(), // if you need a list of unique values
  filterFns,
})

const table = this.tableController.table({
  features,
  columns,
  data: this.data,
})
import {
  TableController,
  tableFeatures,
  columnFacetingFeature,
  columnFilteringFeature,
  createFacetedRowModel,
  createFacetedMinMaxValues,
  createFacetedUniqueValues,
  createFilteredRowModel,
  filterFns,
} from '@tanstack/lit-table'

const features = tableFeatures({
  columnFacetingFeature,
  columnFilteringFeature,
  filteredRowModel: createFilteredRowModel(), // facet values react to other columns' filters
  facetedRowModel: createFacetedRowModel(), // required for faceting (other faceted row models depend on this)
  facetedMinMaxValues: createFacetedMinMaxValues(), // if you need min/max values
  facetedUniqueValues: createFacetedUniqueValues(), // if you need a list of unique values
  filterFns,
})

const table = this.tableController.table({
  features,
  columns,
  data: this.data,
})

First, you must include the facetedRowModel. This row model will generate a list of values for a given column. If you need a list of unique values, include the facetedUniqueValues row model. If you need a tuple of minimum and maximum values, include the facetedMinMaxValues row model.

Use Faceted Row Models

Once you have included the appropriate row models in your table options, you will be able to use the faceting column instance APIs to access the lists of values generated by the faceted row models.

ts
// list of unique values for autocomplete filter
const autoCompleteSuggestions = 
 Array.from(column.getFacetedUniqueValues().keys())
  .sort()
  .slice(0, 5000);
// list of unique values for autocomplete filter
const autoCompleteSuggestions = 
 Array.from(column.getFacetedUniqueValues().keys())
  .sort()
  .slice(0, 5000);
ts
// tuple of min and max values for range filter
const [min, max] = column.getFacetedMinMaxValues() ?? [0, 1];
// tuple of min and max values for range filter
const [min, max] = column.getFacetedMinMaxValues() ?? [0, 1];

Global Faceting

The same columnFacetingFeature and faceted row models also power table-wide (global) faceting. Where column faceting derives values from a single column, global faceting derives values across all columns, which is useful for populating a global filter's autocomplete suggestions or a global range slider. If your table also uses global filtering, register the globalFilteringFeature so global facet values react to the active global filter.

Use the global faceting table instance APIs to read the values:

ts
const globalFacetedRows = table.getGlobalFacetedRowModel().flatRows
const globalFacetedRows = table.getGlobalFacetedRowModel().flatRows
ts
// list of unique values for autocomplete filter
const autoCompleteSuggestions =
 Array.from(table.getGlobalFacetedUniqueValues().keys())
  .sort()
  .slice(0, 5000);
// list of unique values for autocomplete filter
const autoCompleteSuggestions =
 Array.from(table.getGlobalFacetedUniqueValues().keys())
  .sort()
  .slice(0, 5000);
ts
// tuple of min and max values for range filter
const [min, max] = table.getGlobalFacetedMinMaxValues() ?? [0, 1];
// tuple of min and max values for range filter
const [min, max] = table.getGlobalFacetedMinMaxValues() ?? [0, 1];

Custom (Server-Side) Faceting

Instead of using the built-in client-side faceting features, you can implement your own faceting logic on the server-side and pass the faceted values to the client-side. Supply custom facetedUniqueValues and facetedMinMaxValues factories as slots on tableFeatures. Each factory receives the table and a column ID and returns a thunk that resolves the faceted values. The column instance APIs (column.getFacetedUniqueValues() and column.getFacetedMinMaxValues()) will then return your server-provided values.

ts
const serverFacets = await fetch('/api/faceting').then((res) => res.json())

const features = tableFeatures({
  columnFacetingFeature,
  columnFilteringFeature,
  facetedUniqueValues: (_table, columnId) => () => {
    const uniqueValueMap = new Map<string, number>()
    //... populate from serverFacets data for this columnId
    return uniqueValueMap
  },
  facetedMinMaxValues: (_table, columnId) => () => {
    //... read from serverFacets data for this columnId
    return [min, max]
  },
})

const table = this.tableController.table({
  features,
  columns,
  data: this.data,
  //...
})
const serverFacets = await fetch('/api/faceting').then((res) => res.json())

const features = tableFeatures({
  columnFacetingFeature,
  columnFilteringFeature,
  facetedUniqueValues: (_table, columnId) => () => {
    const uniqueValueMap = new Map<string, number>()
    //... populate from serverFacets data for this columnId
    return uniqueValueMap
  },
  facetedMinMaxValues: (_table, columnId) => () => {
    //... read from serverFacets data for this columnId
    return [min, max]
  },
})

const table = this.tableController.table({
  features,
  columns,
  data: this.data,
  //...
})

The same factories also serve global faceting. Global faceting requests values with the internal __global__ column ID, so you can branch on it inside the same facetedUniqueValues and facetedMinMaxValues factories to return table-wide facet values:

ts
// In the tableFeatures call:
facetedUniqueValues: (_table, columnId) => () => {
  if (columnId !== '__global__') return new Map() // per-column facets
  return new Map(globalFacets.uniqueValues) // global facets
},
// In the tableFeatures call:
facetedUniqueValues: (_table, columnId) => () => {
  if (columnId !== '__global__') return new Map() // per-column facets
  return new Map(globalFacets.uniqueValues) // global facets
},

Alternatively, you don't have to put any of your faceting logic through the TanStack Table APIs at all. Just fetch your lists and pass them to your filter components directly.