Library Utilities
Utility functions, constants, and helper functions for the data table.
The lib/ directory contains utility functions, constants, and helper functions used throughout the data table components.
Constants
Section titled “Constants”Centralized constants for join operators, filter operators, filter variants, and UI configuration.
JOIN_OPERATORS
Section titled “JOIN_OPERATORS”Join operators for combining multiple filters.
| Constant | Value | Description |
|---|---|---|
AND | "and" | Logical AND (all filters must match) |
OR | "or" | Logical OR (any filter can match) |
MIXED | "mixed" | Mixed logic (combination of AND/OR) |
FILTER_OPERATORS
Section titled “FILTER_OPERATORS”Filter operator constants defining the comparison logic. Naming follows SQL/PostgREST standards.
| Constant | Value | Description |
|---|---|---|
ILIKE | "ilike" | SQL ILIKE (Case-insensitive search) |
NOT_ILIKE | "not.ilike" | SQL NOT ILIKE |
EQ | "eq" | SQL EQUAL (=) |
NEQ | "neq" | SQL NOT EQUAL (!=) |
IN | "in" | SQL IN (one of) |
NOT_IN | "not.in" | SQL NOT IN (none of) |
EMPTY | "empty" | Value is null or empty string |
NOT_EMPTY | "not.empty" | Value is not null and not empty string |
LT | "lt" | SQL LESS THAN (<) |
LTE | "lte" | SQL LESS THAN OR EQUAL (<=) |
GT | "gt" | SQL GREATER THAN (>) |
GTE | "gte" | SQL GREATER THAN OR EQUAL (>=) |
BETWEEN | "between" | SQL BETWEEN (range) |
RELATIVE | "relative" | Relative date calculation (e.g., “today”) |
FILTER_VARIANTS
Section titled “FILTER_VARIANTS”Filter variant constants defining the UI control type.
| Constant | Value | Description |
|---|---|---|
TEXT | "text" | Standard text input |
NUMBER | "number" | Numeric input |
RANGE | "range" | Two-value range input |
DATE | "date" | Single date picker |
DATE_RANGE | "dateRange" | Date range picker |
SELECT | "select" | Single select dropdown |
MULTI_SELECT | "multiSelect" | Multi-select dropdown |
BOOLEAN | "boolean" | Checkbox or toggle |
DEFAULT_VALUES
Section titled “DEFAULT_VALUES”Global default values.
| Property | Value | Description |
|---|---|---|
JOIN_OPERATOR | JOIN_OPERATORS.AND | Default join operator |
PAGE_SIZE | 10 | Default page size |
PAGE_INDEX | 0 | Default page index |
UI_CONSTANTS
Section titled “UI_CONSTANTS”UI-related constraints and settings.
| Property | Value | Description |
|---|---|---|
FILTER_ID_MAX_LENGTH | 100 | Max characters allowed for a filter ID |
MAX_FILTER_DISPLAY_HEIGHT | 300 | Default max height for scrollable filter popovers |
DEBOUNCE_DELAY | 300 | Default debounce delay in milliseconds for search inputs |
KEYBOARD_SHORTCUTS
Section titled “KEYBOARD_SHORTCUTS”Default keyboard shortcut key mappings.
| Property | Value | Description |
|---|---|---|
FILTER_TOGGLE | "f" | Open/Toggle filter menu |
FILTER_REMOVE | "f" | Remove active filter (with Shift) |
ESCAPE | "escape" | Close active UI elements |
ENTER | "enter" | Confirm or submit active action |
BACKSPACE | "backspace" | Remove character or navigate back |
DELETE | "delete" | Item deletion |
Data Table Utilities
Section titled “Data Table Utilities”Helper functions for filter operations and validation.
getFilterOperators
Section titled “getFilterOperators”Get available filter operators for a given filter variant.
import { getFilterOperators } from "@/components/niko-table/lib/data-table"import { FILTER_VARIANTS } from "@/components/niko-table/lib/constants"
const textOperators = getFilterOperators(FILTER_VARIANTS.TEXT)// Returns: [{ label: "Contains", value: "ilike" }, ...]Parameters
Section titled “Parameters”| Name | Type | Description |
|---|---|---|
filterVariant | FilterVariant | The filter variant type |
Returns
Section titled “Returns”Array of operator objects with label and value properties.
Type: Array<{ label: string; value: FilterOperator }>
getDefaultFilterOperator
Section titled “getDefaultFilterOperator”Get the default filter operator for a given filter variant.
import { getDefaultFilterOperator } from "@/components/niko-table/lib/data-table"import { FILTER_VARIANTS } from "@/components/niko-table/lib/constants"
const defaultOp = getDefaultFilterOperator(FILTER_VARIANTS.TEXT)// Returns: "ilike"Parameters
Section titled “Parameters”| Name | Type | Description |
|---|---|---|
filterVariant | FilterVariant | The filter variant type |
Returns
Section titled “Returns”FilterOperator - The default operator for the variant.
getValidFilters
Section titled “getValidFilters”Filter out invalid filters from an array.
import { getValidFilters } from "@/components/niko-table/lib/data-table"
const validFilters = getValidFilters(filters)// Returns: ExtendedColumnFilter<TData>[] with only valid filtersParameters
Section titled “Parameters”| Name | Type | Description |
|---|---|---|
filters | ExtendedColumnFilter<TData>[] | Array of filters to validate |
Returns
Section titled “Returns”ExtendedColumnFilter<TData>[] - Array containing only valid filters.
Format Utilities
Section titled “Format Utilities”Formatting functions for dates, labels, and query strings.
formatDate
Section titled “formatDate”Format a date value into a human-readable string.
import { formatDate } from "@/components/niko-table/lib/format"
formatDate(new Date())// Returns: "January 1, 2024"
formatDate(new Date(), { month: "short", day: "numeric" })// Returns: "Jan 1"Parameters
Section titled “Parameters”| Name | Type | Default | Description |
|---|---|---|---|
date | Date | string | number | undefined | - | Date value to format |
opts | Intl.DateTimeFormatOptions | {} | Formatting options |
Returns
Section titled “Returns”string - Formatted date string, or empty string if date is invalid.
formatLabel
Section titled “formatLabel”Format a value into a human-readable label. Capitalizes first letter of each word and replaces hyphens/underscores with spaces.
import { formatLabel } from "@/components/niko-table/lib/format"
formatLabel("firstName") // "FirstName"formatLabel("first-name") // "First Name"formatLabel("first_name") // "First Name"formatLabel("true") // "Yes"formatLabel("false") // "No"Parameters
Section titled “Parameters”| Name | Type | Description |
|---|---|---|
value | string | Value to format |
Returns
Section titled “Returns”string - Formatted label string.
daysAgo
Section titled “daysAgo”Create a date relative to the current date by subtracting days.
import { daysAgo } from "@/components/niko-table/lib/format"
daysAgo(7) // 7 days agodaysAgo(30) // 30 days ago (1 month)daysAgo(365) // 365 days ago (1 year)Parameters
Section titled “Parameters”| Name | Type | Description |
|---|---|---|
days | number | Number of days to subtract |
Returns
Section titled “Returns”Date - Date object representing the date N days ago.
formatQueryString
Section titled “formatQueryString”Format URL query parameters into a human-readable query string for display.
import { formatQueryString } from "@/components/niko-table/lib/format"
const urlParams = { search: "i", globalFilter: { filters: [...], joinOperator: "mixed" } }const urlKeys = { search: "search", globalFilter: "global" }formatQueryString(urlParams, urlKeys)// Returns: "?search=i&global={"filters":[...], "joinOperator":"mixed"}"Parameters
Section titled “Parameters”| Name | Type | Description |
|---|---|---|
urlParams | Record<string, unknown> | The parsed URL parameters object |
urlKeys | Record<string, string> | Mapping of parameter keys to URL query keys |
Returns
Section titled “Returns”string - Formatted query string (e.g., ?search=i&global={"filters":[...]}).
Filter Functions
Section titled “Filter Functions”Custom filter functions for TanStack Table with performance optimizations.
The filter functions module provides optimized filter implementations for TanStack Table. It includes:
- Regex caching - Caches compiled regex patterns for 70-90% faster filter execution
- Operator support - Implements all filter operators (ilike, eq, neq, in, between, etc.)
- Performance optimizations - LRU-style cache eviction to prevent memory leaks
Performance Notes
Section titled “Performance Notes”The filter functions use regex caching to improve performance:
- Without caching: 1,000 rows × 10 columns = 10,000 regex creations per search keystroke (~100-500ms)
- With caching: First search creates regex once, subsequent searches reuse cached regex (~5-20ms)
The cache has a maximum size of 100 entries and uses LRU-like eviction to prevent memory leaks.
extendedFilter
Section titled “extendedFilter”Custom filter function for TanStack Table that supports all filter operators (ilike, eq, neq, in, not_in, between, lt, gt, lte, gte, empty, not_empty). Used as the default filterFn for columns with advanced filtering.
import { extendedFilter } from "@/components/niko-table/lib/filter-functions"
const columns = [ { accessorKey: "name", filterFn: extendedFilter, },]globalFilter
Section titled “globalFilter”Custom global filter function that searches across all visible columns. Supports debounced search input.
import { globalFilter } from "@/components/niko-table/lib/filter-functions"
const table = useReactTable({ globalFilterFn: globalFilter,})numberRangeFilter
Section titled “numberRangeFilter”Filter function for numeric range columns. Filters rows where the value is between a [min, max] range.
import { numberRangeFilter } from "@/components/niko-table/lib/filter-functions"
const columns = [ { accessorKey: "price", filterFn: numberRangeFilter, },]dateRangeFilter
Section titled “dateRangeFilter”Filter function for date range columns. Filters rows where the date falls between a start and end date.
import { dateRangeFilter } from "@/components/niko-table/lib/filter-functions"
const columns = [ { accessorKey: "createdAt", filterFn: dateRangeFilter, },]createFilterValue
Section titled “createFilterValue”Utility to create a properly typed filter value object from an ExtendedColumnFilter. Used internally by the filter menu and inline filter components.
import { createFilterValue } from "@/components/niko-table/lib/filter-functions"
const filterValue = createFilterValue(filter)processFiltersForLogic
Section titled “processFiltersForLogic”Processes an array of ExtendedColumnFilter objects and groups them by join operator (AND/OR) for TanStack Table’s column filter state. Returns processed filters and the join operator map.
import { processFiltersForLogic } from "@/components/niko-table/lib/data-table"
const { processedFilters, joinOperators } = processFiltersForLogic(filters)Style Utilities
Section titled “Style Utilities”Helper functions for styling table components, particularly for column pinning.
getCommonPinningStyles
Section titled “getCommonPinningStyles”Returns CSS styles for pinned columns (sticky positioning, z-index, shadows). Used internally by DataTable components, but available for custom implementations.
import { getCommonPinningStyles } from "@/components/niko-table/lib/styles"
// In a custom header cellconst pinningStyles = getCommonPinningStyles(column, true)
// In a custom body cellconst pinningStyles = getCommonPinningStyles(column, false)Parameters
Section titled “Parameters”| Name | Type | Default | Description |
|---|---|---|---|
column | Column<TData> | - | TanStack Table column instance |
isHeader | boolean | false | Whether this is a header cell (sets top: 0 for vertical stickiness and z-index 20) |
Returns
Section titled “Returns”React.CSSProperties - CSS styles object with sticky positioning, z-index, background, and shadow.
Style Properties Applied:
position: sticky- Keeps column fixed during horizontal scrolltop- Set to0for header cells so they stick when the table scrolls vertically (sticky header)leftorright- Calculated position based on pinning sidezIndex- 20 for headers, 10 for body cellsbackgroundColor- Usesvar(--background)for opaque overlayboxShadow- Visual separator usingvar(--border)for pinned column edges
Scroll Handler Factory
Section titled “Scroll Handler Factory”Shared scroll-listener body used by every data-table body component (DataTableBody, DataTableVirtualizedBody, DataTableVirtualizedDndBody, DataTableVirtualizedDndColumnBody). Extracted so scroll math changes propagate to all four bodies and so consumers writing custom bodies can reuse the canonical implementation.
createScrollHandler
Section titled “createScrollHandler”Builds a passive-listener-friendly scroll handler that reads container metrics on each scroll event and dispatches:
onScrollwith aScrollEventsnapshot (scrollTop,scrollHeight,clientHeight,isTop,isBottom,percentage)onScrolledTopwhenscrollTop === 0onScrolledBottomwhen withinscrollThresholdpixels of the end
Returned listener is intended to be attached with addEventListener("scroll", handler, { passive: true }) so the browser can keep scroll on its compositor thread (smoother on mobile and during virtual scroll).
import { createScrollHandler } from "@/components/niko-table/lib/create-scroll-handler"
React.useEffect(() => { const container = containerRef.current if (!container) return if (!onScroll && !onScrolledTop && !onScrolledBottom) return
const handleScroll = createScrollHandler({ onScroll, onScrolledTop, onScrolledBottom, scrollThreshold: 50, }) container.addEventListener("scroll", handleScroll, { passive: true }) return () => container.removeEventListener("scroll", handleScroll)}, [onScroll, onScrolledTop, onScrolledBottom])Parameters
Section titled “Parameters”Single options object:
| Name | Type | Default | Description |
|---|---|---|---|
onScroll | (event: ScrollEvent) => void (optional) | - | Called on every scroll event with a snapshot of container metrics |
onScrolledTop | () => void (optional) | - | Called when scrollTop === 0 |
onScrolledBottom | () => void (optional) | - | Called when within scrollThreshold of the bottom |
scrollThreshold | number | 50 | Pixel distance from the bottom that triggers onScrolledBottom (use a higher value for earlier prefetching) |
Returns
Section titled “Returns”(event: Event) => void — the listener to attach. Closure deps are stable across renders (the consumer-supplied callbacks are passed once at construction time), so wrap the construction in a useEffect keyed on the same callbacks.
- For virtualized infinite scroll, prefer
DataTableVirtualizedBody’sonNearEnd+prefetchThresholdprops overonScrolledBottom—onNearEndis virtualizer-index-driven (catches fast scrolls, scrollbar drag, programmaticscrollToIndex()jumps, and initial renders where the dataset doesn’t fill the viewport). - The handler reads
event.currentTarget, so it must be attached directly to the scroll container — not to a parent.
Row Click Guard
Section titled “Row Click Guard”Shared row-click guard used by every body component (DataTableBody, DataTableDndBody, DataTableDndColumnBody, DataTableVirtualizedBody, DataTableVirtualizedDndBody, DataTableVirtualizedDndColumnBody). Centralized so the interactive-element list (action buttons, checkboxes, links, radix collection items, etc.) stays in sync — adding a new data-slot to ignore means editing one file instead of six.
isInteractiveClickTarget
Section titled “isInteractiveClickTarget”Returns true when the click landed on (or inside) an interactive element that should suppress the row-level onRowClick. Used by the per-row variants (DataTableBody, DataTableVirtualizedBody) which already know which row was clicked from event.currentTarget.dataset.rowId.
import { isInteractiveClickTarget } from "@/components/niko-table/lib/row-click"
const handleRowClick = (event: React.MouseEvent<HTMLElement>) => { if (isInteractiveClickTarget(event.target as HTMLElement)) return const rowId = event.currentTarget.dataset.rowId if (!rowId) return const row = table.getRow(rowId) if (!row) return onRowClick(row.original, event)}resolveRowFromClick
Section titled “resolveRowFromClick”For delegated <tbody> handlers (used by the DnD bodies). Returns the matched row, or null if the click should be ignored — either it landed on an interactive descendant or no row could be resolved from the closest tr[data-row-id].
import { resolveRowFromClick } from "@/components/niko-table/lib/row-click"
const handleRowClick = (event: React.MouseEvent<HTMLElement>) => { const row = resolveRowFromClick(event.target as HTMLElement, table) if (!row) return onRowClick(row.original, event)}- The interactive-element list lives in
isInteractiveClickTarget— extend it there to add new ignored elements. - Rows must carry
data-row-id={row.id}for delegated resolution to work. All built-in bodies do this; custom bodies should follow suit.
Additional Constants
Section titled “Additional Constants”SYSTEM_COLUMN_IDS
Section titled “SYSTEM_COLUMN_IDS”System column IDs used for smart pinning and feature detection. These are the IDs for special columns like row selection and row expansion.
import { SYSTEM_COLUMN_IDS } from "@/components/niko-table/lib/constants"
// Available IDs:SYSTEM_COLUMN_IDS.SELECT // "select" - Row selection checkbox columnSYSTEM_COLUMN_IDS.EXPAND // "expand" - Row expansion toggle columnSYSTEM_COLUMN_ID_LIST
Section titled “SYSTEM_COLUMN_ID_LIST”Array version of SYSTEM_COLUMN_IDS values. Useful for filtering or checking if a column ID is a system column.
import { SYSTEM_COLUMN_ID_LIST } from "@/components/niko-table/lib/constants"
// ["select", "expand"]const isSystemColumn = SYSTEM_COLUMN_ID_LIST.includes(columnId)ERROR_MESSAGES
Section titled “ERROR_MESSAGES”Standard error messages used throughout the library for consistent error reporting.
import { ERROR_MESSAGES } from "@/components/niko-table/lib/constants"
// Available messages:ERROR_MESSAGES.DEPRECATED_GLOBAL_JOIN_OPERATORFaceted Options Builder
Section titled “Faceted Options Builder”Used internally by faceted-filter components to translate a raw column dataset into the { label, value, count } shape the filter UI consumes. Documented for completeness — most consumers won’t import it directly.
buildFacetedOptions
Section titled “buildFacetedOptions”Walks a TanStack Table column / row set and produces deduplicated { label, value, count } entries for the faceted-filter dropdown. Honors the dynamicCounts flag so counts update reactively when other filters change. Used internally by DataTableFacetedFilter and TableColumnFacetedFilter.
import { buildFacetedOptions } from "@/components/niko-table/lib/build-faceted-options"
const options = buildFacetedOptions({ rows: table.getCoreRowModel().rows, columnId: "category", dynamicCounts: true,})// → [{ label: "Electronics", value: "electronics", count: 12 }, ...]- This module is consumed by the faceted-filter wrappers; if you’re building a custom faceted UI,
useGeneratedOptions(inhooks/) is the consumer-facing surface that wraps this helper with React state. - Runs in O(n) over the column’s cell values; safe to call per render for typical column counts.
Row Filtering Helpers
Section titled “Row Filtering Helpers”Internal helpers that resolve globalFilter / column-filter combinations to a final visible row set. Used by DataTableRoot when wiring tableOptions.getFilteredRowModel.
filter-rows.ts
Section titled “filter-rows.ts”Provides the row-filtering logic that backs the table’s filtered row model — combines per-column filters with the global filter, applies the right extendedFilter / globalFilter / numberRangeFilter / dateRangeFilter based on column meta, and returns the visible row set.
This module is internal to the table’s filter pipeline. Consumers normally interact with it indirectly via tableOptions.filterFns (which DataTableRoot wires automatically). If you’re building a fully custom filter pipeline you can import the named helpers from this file directly — see the source for the current export surface.