npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@uxf/data-grid

v12.4.0

Published

UXF DataGrid

Readme

@uxf/data-grid

A schema-driven, server-side data grid: one compound DataGrid component with a built-in toolbar, tabs, filters, sorting, column visibility, row selection, pagination and CSV export.

When to use

Reach for @uxf/data-grid when you have a paginated, filterable, server-fetched table driven by a generated schema (columns, filters, tabs). The grid owns fetching, filtering, sorting and pagination state; you provide a schema and a loader.

It is not a headless table primitive and not a spreadsheet — layout, toolbar and footer are opinionated. For a plain, fully custom table render the internal parts yourself (see Custom composition) rather than reaching for a different library.

Note: This package ships translations. Wrap your app in the TranslationsProvider from @uxf/core-react/translations for correct labels.

Installation

yarn add @uxf/data-grid

Peer dependencies (install if not already present):

yarn add @uxf/core @uxf/core-react @uxf/localize @uxf/styles @uxf/ui \
  @dnd-kit/core @dnd-kit/sortable @dnd-kit/utilities dayjs react react-dom

CSS

Import the dependency stylesheets first, then the grid's own stylesheet last. The @uxf/ui component styles are published under the flattened @uxf/ui/css/ directory (the @uxf/ui/<component>/<file>.css source paths do not exist in the published package).

@import url("tailwindcss/components.css");
@import url("@uxf/ui/css/button.css");
@import url("@uxf/ui/css/button-list.css");
@import url("@uxf/ui/css/calendar.css");
@import url("@uxf/ui/css/checkbox.css");
@import url("@uxf/ui/css/chip.css");
@import url("@uxf/ui/css/dialog.css");
@import url("@uxf/ui/css/dropdown.css");
@import url("@uxf/ui/css/icon.css");
@import url("@uxf/ui/css/label.css");
@import url("@uxf/ui/css/form-component.css");
@import url("@uxf/ui/css/input-basic.css");
@import url("@uxf/ui/css/input.css");
@import url("@uxf/ui/css/input-with-popover.css");
@import url("@uxf/ui/css/combobox.css");
@import url("@uxf/ui/css/modal-header.css");
@import url("@uxf/ui/css/multi-select.css");
@import url("@uxf/ui/css/multi-combobox.css");
@import url("@uxf/ui/css/pagination.css");
@import url("@uxf/ui/css/select.css");
@import url("@uxf/ui/css/toggle.css");
@import url("@uxf/ui/css/tabs.css");

/* must be after the component css files */
@import url("@uxf/data-grid/styles.css");

Quick start

useDataGridControl owns grid state and actions; useDataGridFetching runs the loader whenever the request changes. Feed both into DataGrid.

"use client";

import { DataGrid } from "@uxf/data-grid";
import { useDataGridControl } from "@uxf/data-grid/use-data-grid-control";
import { useDataGridFetching } from "@uxf/data-grid/use-data-grid-fetching";
import { schema } from "@generated/data-grid/schema/example";

export function ExampleGrid() {
    const { state, actions } = useDataGridControl({ schema });

    const { isLoading, error, data, reload } = useDataGridFetching({ schema, state });

    return (
        <DataGrid
            actions={actions}
            data={data}
            error={error}
            isLoading={isLoading}
            reload={reload}
            schema={schema}
            state={state}
        />
    );
}

useDataGridFetching uses a default loader that calls the conventional grid endpoint. To fetch yourself, pass a loader:

const { ... } = useDataGridFetching({
    schema,
    state,
    loader: (gridName, request, encodedRequest) => myFetch(gridName, request),
});

Persisting user config

useUserConfigLocalStorageAdapter returns a middleware (persists column visibility, widths, order, etc. to localStorage) and a useUserConfig hook that restores it. Wire the middleware into useDataGridControl and call useUserConfig(actions).

import { useDataGridControl } from "@uxf/data-grid/use-data-grid-control";
import { useUserConfigLocalStorageAdapter } from "@uxf/data-grid/user-config-storage-adapters/local-storage";

const { middleware, useUserConfig } = useUserConfigLocalStorageAdapter(schema);

const { state, actions } = useDataGridControl({
    schema,
    middleware,
    // optional base64-encoded request or a Request object to hydrate initial state
    initialState: encodedRequest,
    // optional default column config
    initialUserConfig: {
        columns: {
            id: { isHidden: true },
        },
    },
});

useUserConfig(actions);

API

Only exports listed below are part of the public surface. Import paths resolve by filesystem (there is no exports map).

Root export — @uxf/data-grid

| Name | Kind | Description | | ---------------------------------------------------------------------------- | --------- | ------------------------------------------------------------------------------------------------------- | | DataGrid | component | The compound grid. Props: DataGridProps<GridType>. | | DataGridProps<GridType> | type | Grid props = base props + control (state, actions) + data (isLoading, error, data, reload). | | InferDataGridRow<Schema> | type | Derives the row shape from a schema via ColumnTypes. | | BaseGridType, Schema, ChangeTabFilterBehavior, DataGridActionCell, … | types | Re-exported from ./types. | | mergeSchemaWithConfig(schema, config) | fn | Returns a new schema with a frontend config applied (see below). | | encodeFilter(request) / decodeFilter(string) | fn | Base64 (de)serialize a grid request. |

Deep imports

| Import path | Export | Description | | ----------------------------------------------------------- | ------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- | | @uxf/data-grid/use-data-grid-control | useDataGridControl(config) | Owns grid state + actions. Config: { schema, initialState?, initialUserConfig?, middleware? }. | | @uxf/data-grid/use-data-grid-fetching | useDataGridFetching(config) | Runs the loader on request changes. Config: { schema, state, loader?, isWithTabCounts? }. Returns { isLoading, error, data, reload }. | | @uxf/data-grid/user-config-storage-adapters/local-storage | useUserConfigLocalStorageAdapter(schema), useClearLocalStorageUserConfig(schema) | localStorage persistence of user column config. | | @uxf/data-grid/column-types | ColumnTypes (interface) | Augment to register custom column value types. | | @uxf/data-grid/use-data-grid-filter-draft | useDataGridFilterDraft(control) | Holds filter edits aside until apply() is called. Returns { state, actions, apply } in the useDataGridControl shape. |

Notable optional DataGrid props: actionCell, bodyCells, changeTabFilterBehavior, customActions, filterApplyMode, filterHandlers, fulltextInputPlaceholder, getCsvDownloadUrl, hasStickyHeader, isRowsSelectable, isRowSelectDisabled, isWithTabCounts, keyExtractor, rowAccent, rowClassName, rowHeight, tabsVariant, HiddenColumnsComponent, NoRowsFallback, SelectedRowsToolbarActions, isDebug.

Column types

A column's type (from the schema) maps to a value type via the ColumnTypes interface, which drives InferDataGridRow. Built-in types:

boolean, chip, chips, date, datetime, email, id, int, money, phone, string, url, uuid.

Register a custom column type with module augmentation. Create a column-types.d.ts at the project root (next to tsconfig.json) — it must be an interface:

declare module "@uxf/data-grid/column-types" {
    export interface ColumnTypes {
        "my-custom-type": MyCustomType;
    }
}

Then infer the row type from a schema:

import { InferDataGridRow } from "@uxf/data-grid";
import { schema } from "@generated/data-grid/schema/example";

type Row = InferDataGridRow<typeof schema>;

Filter handlers

Each filter is rendered by looking up filterHandlers[filter.type]. The grid uses defaultFilterHandlers unless you pass a filterHandlers prop. Built-in handler keys:

| filter.type | Renders | | ------------------- | ------------------------------------------ | | checkbox | Checkbox (value is sent only when checked) | | date | Date range (from / to date pickers) | | datetime | Datetime range | | entitySelect | Single-select combobox (async entity) | | entityMultiSelect | Multi-select combobox (async entity) | | interval | Numeric range (min / max inputs) | | multiSelect | Multi-select | | select | Select | | string | Text input |

Extend or override by merging into defaultFilterHandlers and passing the result as the filterHandlers prop:

import { defaultFilterHandlers } from "@uxf/data-grid/filter-handler";

<DataGrid filterHandlers={{ ...defaultFilterHandlers, myType: myHandler }} {...rest} />;

Applying filters

By default a filter reaches the grid as soon as its value changes, in the filters drawer and anywhere else the filters are rendered. filterApplyMode="confirm" changes that for the drawer: edits are held back until the user presses the confirm button, and closing the drawer (the close button, ESC or a click outside) discards them, leaving the grid at the last confirmed state.

<DataGrid filterApplyMode="confirm" {...rest} />

Only the filters are deferred — fulltext, sorting, tabs and paging stay immediate, and so do the close buttons on the filter chips below the toolbar. The clear-all button in the drawer header follows the mode: in confirm it empties the drawer only, leaving the grid untouched until the filters are confirmed.

For a filters panel of your own, outside the drawer, useDataGridFilterDraft gives the same behaviour:

const draft = useDataGridFilterDraft({ state, actions });

<DataGridFilters actions={draft.actions} filterHandlers={handlers} schema={schema} state={draft.state} />
<Button onClick={draft.apply}>Apply</Button>;

The draft is seeded when the hook mounts and re-seeded whenever the applied filters change elsewhere, so unconfirmed edits are dropped when a filter chip is closed or a tab is switched.

Frontend config

mergeSchemaWithConfig returns a new schema with per-column and per-filter overrides and perPage applied — useful for tweaking a generated schema without regenerating it.

import { DataGrid } from "@uxf/data-grid";
import { mergeSchemaWithConfig } from "@uxf/data-grid";
import { useDataGridControl } from "@uxf/data-grid/use-data-grid-control";
import { useDataGridFetching } from "@uxf/data-grid/use-data-grid-fetching";
import { schema as baseSchema } from "@generated/data-grid/schema/example";

const schema = mergeSchemaWithConfig(baseSchema, {
    perPage: 100,
    columns: {
        id: { width: 100, isHidden: true },
    },
    filters: {
        id: { placeholder: "Search by ID..." },
    },
});

export function ConfiguredGrid() {
    const { state, actions } = useDataGridControl({ schema });
    const { isLoading, error, data, reload } = useDataGridFetching({ schema, state });

    return (
        <DataGrid
            actions={actions}
            data={data}
            error={error}
            isLoading={isLoading}
            reload={reload}
            schema={schema}
            state={state}
        />
    );
}

Custom composition

The DataGrid component is a fixed composition of internal parts. For a fully custom layout, import the parts individually and assemble them yourself, still driving them from useDataGridControl / useDataGridFetching:

@uxf/data-grid/root, @uxf/data-grid/toolbar, @uxf/data-grid/toolbar-tabs, @uxf/data-grid/toolbar-control, @uxf/data-grid/toolbar-customs, @uxf/data-grid/filter-list, @uxf/data-grid/table-v2, @uxf/data-grid/footer, @uxf/data-grid/pagination, @uxf/data-grid/row-counts, @uxf/data-grid/rows-per-page-select, @uxf/data-grid/selected-rows-toolbar, @uxf/data-grid/linear-progress, @uxf/data-grid/body-cell, @uxf/data-grid/filter-handler.

See data-grid.tsx (the default composition) and data-grid-custom-example.stories.tsx in the package for a worked example.

Gotchas

  • Client component only. The hooks use React state, effects and localStorage. Render the grid in a client component ("use client").
  • TranslationsProvider required. Labels come from bundled translations via @uxf/core-react/translations.
  • CSS order matters. Import @uxf/data-grid/styles.css after the @uxf/ui/css/* component styles.
  • ColumnTypes augmentation must be an interface, not a type, or the declaration merge will not apply.
  • initialState is a base64-encoded request string (or a Request object) — use encodeFilter / decodeFilter to (de)serialize, e.g. to persist the current view in the URL.
  • isDebug is a DataGrid prop, not a useDataGridControl config option.