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

kittle-tanstack

v0.1.0

Published

Reusable React and TanStack data tables, schema forms, query builders, and navigation components.

Readme

Shared TanStack UI components

kittle-tanstack provides reusable React components for projects that share the same application conventions without copying UI source code between repositories. It is a presentation package; authorization and server-side validation remain application and kittle-core responsibilities.

Install

npm install kittle-tanstack @tanstack/react-table zod

Import the shared styles once in the application entry point:

import "kittle-tanstack/styles.css"

Entry points

import { DataTable } from "kittle-tanstack/data-table"
import { SchemaForm } from "kittle-tanstack/schema-form"
import { QueryBuilder } from "kittle-tanstack/query-builder"
import { Sidebar } from "kittle-tanstack/sidebar"
import { createFetchDataSource } from "kittle-tanstack/adapters"
import { UiProvider } from "kittle-tanstack/ui"
import { withUi } from "kittle-tanstack/ui"

The components are intentionally configured through typed props. Project specific API clients, permission systems, custom form fields, icons, and navigation behavior are supplied by the consuming application rather than being hard-coded into the package.

For projects that prefer configured component wrappers instead of a provider, use withUi:

const appUi = {
  Button: AppButton,
  Input: AppInput,
  Select: AppSelect,
  Textarea: AppTextarea,
  Autocomplete: AppAutocomplete,
  MultiSelect: AppMultiSelect,
  Switch: AppSwitch,
  Checkbox: AppCheckbox,
  fieldRegistry: {
    customer: CustomerField,
    map: MapField,
  },
}

export const AppDataTable = withUi(DataTable, appUi)
export const AppSchemaForm = withUi(SchemaForm, appUi)
export const AppQueryBuilder = withUi(QueryBuilder, appUi)
export const AppSidebar = withUi(Sidebar, appUi)

Each wrapper can then be used throughout the application without a root provider. Component-level components props override the wrapper defaults.

Custom rendering

The package includes accessible defaults, but applications can replace the visual controls globally or per component:

<UiProvider
  components={{
    Button: MyButton,
    Select: MySelect,
    Input: MyInput,
    Textarea: MyTextarea,
  }}
>
  <App />
</UiProvider>

Every major component also accepts a local components override. The shared components retain state, validation, filtering, permissions, and accessibility contracts while the application owns the button, dropdown, input, and styling implementation. SchemaForm additionally supports a fieldRegistry for application-specific fields.

DataTable

DataTable supports local rows or a server-side data source:

<DataTable
  columns={columns}
  dataSource={{
    fetch: (query) => api.customers.list(query),
  }}
  onRowClick={(customer) => openCustomer(customer.id)}
/>

For Fetch-compatible endpoints, use the included adapter:

<DataTable
  columns={columns}
  dataSource={createFetchDataSource({ endpoint: "/api/customers" })}
/>

The table also supports client or server pagination, search, filter reset, grouped headers, sorting, column visibility, persisted visibility state, row selection, bulk-selection callbacks, refresh/retry actions, custom toolbar content, and application-provided export handlers:

<DataTable
  data={customers}
  columnsConfig={columns}
  searchable
  selectable
  persistStateKey="customers"
  exports={{ csv: { handler: ({ rows }) => downloadCsv(rows) } }}
  onSelectionChange={setSelectedCustomers}
  manualPagination={false}
  onRefresh={reload}
/>

SchemaForm

Forms use the same Zod schemas that can be validated at the server boundary:

<SchemaForm
  schema={customerSchema}
  fields={[
    { name: "name", label: "Name", type: "text", required: true },
    { name: "status", label: "Status", type: "select", options },
  ]}
  onSubmit={saveCustomer}
/>

SchemaForm also supports automatic Zod field generation, schema metadata and layouts, nested arrays, conditional visibility, change/blur/submit validation, server errors, dirty-state refs, submission guards, field slots, navigation unload protection, and custom renderers for application-owned fields:

const schema = withFormLayout(
  z.object({ name: withMeta(z.string(), { placeholder: "Customer name" }) }),
  { sections: [{ id: "main", title: "Customer", fields: ["name"] }] }
)

<SchemaForm
  schema={schema}
  validationMode="blur"
  enableNavigationGuard
  fieldSlots={{ name: ({ form }) => <small>{form.state.isDirty ? "Changed" : ""}</small> }}
  onSubmit={saveCustomer}
/>

File uploads, maps, markdown editors, autocomplete implementations, and domain fields remain application-provided through fieldRegistry or customFieldRenderer.

QueryBuilder

QueryBuilder emits a portable nested FilterGroup value. Applications can map that value to their API query contract or to the predicate model used by the runtime adapters. It supports simple and advanced modes, nested groups, operator labels, select and multiselect values, search, clear/apply actions, disabled state, and optional persisted query/mode state.

Sidebar

Sidebar accepts a generic navigation tree and an optional permission callback. It does not depend on CASL or a specific router:

<Sidebar
  items={menuItems}
  pathname={location.pathname}
  can={(permission) => ability.can(permission.action, permission.subject)}
  onNavigate={(item) => navigate(item.path)}
/>

Sidebar also supports recursive permission-aware filtering, active descendant paths, navigation search, collapsible groups, persisted collapsed state, keyboard activation, badges, hotkeys, and custom item rendering. Router navigation remains application-owned through onNavigate.

Extension policy

Application-specific fields, operations, permissions, API clients, and domain modules should be registered or passed as props. They should not be added to this package. This keeps one published implementation reusable across all projects while allowing each project to retain its own business behavior.