Skip to content

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.

Centralized constants for join operators, filter operators, filter variants, and UI configuration.

Join operators for combining multiple filters.

ConstantValueDescription
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 operator constants defining the comparison logic. Naming follows SQL/PostgREST standards.

ConstantValueDescription
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 variant constants defining the UI control type.

ConstantValueDescription
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

Global default values.

PropertyValueDescription
JOIN_OPERATORJOIN_OPERATORS.ANDDefault join operator
PAGE_SIZE10Default page size
PAGE_INDEX0Default page index

UI-related constraints and settings.

PropertyValueDescription
FILTER_ID_MAX_LENGTH100Max characters allowed for a filter ID
MAX_FILTER_DISPLAY_HEIGHT300Default max height for scrollable filter popovers
DEBOUNCE_DELAY300Default debounce delay in milliseconds for search inputs

Default keyboard shortcut key mappings.

PropertyValueDescription
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

Helper functions for filter operations and validation.

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" }, ...]
NameTypeDescription
filterVariantFilterVariantThe filter variant type

Array of operator objects with label and value properties.

Type: Array<{ label: string; value: FilterOperator }>

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"
NameTypeDescription
filterVariantFilterVariantThe filter variant type

FilterOperator - The default operator for the variant.

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 filters
NameTypeDescription
filtersExtendedColumnFilter<TData>[]Array of filters to validate

ExtendedColumnFilter<TData>[] - Array containing only valid filters.

Formatting functions for dates, labels, and query strings.

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"
NameTypeDefaultDescription
dateDate | string | number | undefined-Date value to format
optsIntl.DateTimeFormatOptions{}Formatting options

string - Formatted date string, or empty string if date is invalid.

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"
NameTypeDescription
valuestringValue to format

string - Formatted label string.

Create a date relative to the current date by subtracting days.

import { daysAgo } from "@/components/niko-table/lib/format"
daysAgo(7) // 7 days ago
daysAgo(30) // 30 days ago (1 month)
daysAgo(365) // 365 days ago (1 year)
NameTypeDescription
daysnumberNumber of days to subtract

Date - Date object representing the date N days ago.

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"}"
NameTypeDescription
urlParamsRecord<string, unknown>The parsed URL parameters object
urlKeysRecord<string, string>Mapping of parameter keys to URL query keys

string - Formatted query string (e.g., ?search=i&global={"filters":[...]}).

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

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.

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,
},
]

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,
})

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,
},
]

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,
},
]

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)

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)

Helper functions for styling table components, particularly for column pinning.

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 cell
const pinningStyles = getCommonPinningStyles(column, true)
// In a custom body cell
const pinningStyles = getCommonPinningStyles(column, false)
NameTypeDefaultDescription
columnColumn<TData>-TanStack Table column instance
isHeaderbooleanfalseWhether this is a header cell (sets top: 0 for vertical stickiness and z-index 20)

React.CSSProperties - CSS styles object with sticky positioning, z-index, background, and shadow.

Style Properties Applied:

  • position: sticky - Keeps column fixed during horizontal scroll
  • top - Set to 0 for header cells so they stick when the table scrolls vertically (sticky header)
  • left or right - Calculated position based on pinning side
  • zIndex - 20 for headers, 10 for body cells
  • backgroundColor - Uses var(--background) for opaque overlay
  • boxShadow - Visual separator using var(--border) for pinned column edges

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.

Builds a passive-listener-friendly scroll handler that reads container metrics on each scroll event and dispatches:

  • onScroll with a ScrollEvent snapshot (scrollTop, scrollHeight, clientHeight, isTop, isBottom, percentage)
  • onScrolledTop when scrollTop === 0
  • onScrolledBottom when within scrollThreshold pixels 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])

Single options object:

NameTypeDefaultDescription
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
scrollThresholdnumber50Pixel distance from the bottom that triggers onScrolledBottom (use a higher value for earlier prefetching)

(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’s onNearEnd + prefetchThreshold props over onScrolledBottomonNearEnd is virtualizer-index-driven (catches fast scrolls, scrollbar drag, programmatic scrollToIndex() 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.

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.

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)
}

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.

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 column
SYSTEM_COLUMN_IDS.EXPAND // "expand" - Row expansion toggle column

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)

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_OPERATOR

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.

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 (in hooks/) 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.

Internal helpers that resolve globalFilter / column-filter combinations to a final visible row set. Used by DataTableRoot when wiring tableOptions.getFilteredRowModel.

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.