Finished data table, combobox, date picker and file uploader for React that drop into any design system: shadcn/ui, MUI, Chakra, Ant Design or your own.

Every part is a slot; what you don't replace still looks finished.

npm i slotsmith
1042Lena Park3Shipped
1043Omar Haddad1Packing
1044Sofia Reyes5Shipped
1045Yuki Tanaka2Paid
1046Amir Nasser4Packing

Swap the design system, keep the table

The same table in a few well-known design systems; yours works the same way. Only the components prop changes.

Lena ParkEngineerPlatformActive2021-03-08
Omar HaddadDesignerProductActive2020-11-16
Sofia ReyesEngineerPaymentsAway2022-01-24
Yuki TanakaManagerPlatformActive2019-06-03
Amir NasserAnalystGrowthActive2023-02-13

landing/swap-fallback.tsx

import { DataTable } from "slotsmith/data-table";
import { columns, people } from "../shared/people";

export default function Swap() {
  return (
    <DataTable
      data={people}
      columns={columns}
      enableRowSelection
      defaultPagination={{ pageIndex: 0, pageSize: 5 }}
    />
  );
}
import { DataTable } from "slotsmith/data-table";
import { shadcnComponents } from "../adapters/data-table/shadcn"; changed line
import { columns, people } from "../shared/people";

export default function Swap() {
  return (
    <DataTable
      data={people}
      columns={columns}
      enableRowSelection
      defaultPagination={{ pageIndex: 0, pageSize: 5 }}
      components={shadcnComponents} changed line
    />
  );
}
import { DataTable } from "slotsmith/data-table";
import { muiComponents } from "../adapters/data-table/mui"; changed line
import { columns, people } from "../shared/people";

export default function Swap() {
  return (
    <DataTable
      data={people}
      columns={columns}
      enableRowSelection
      defaultPagination={{ pageIndex: 0, pageSize: 5 }}
      components={muiComponents} changed line
    />
  );
}
import { DataTable } from "slotsmith/data-table";
import { chakraComponents } from "../adapters/data-table/chakra"; changed line
import { columns, people } from "../shared/people";

export default function Swap() {
  return (
    <DataTable
      data={people}
      columns={columns}
      enableRowSelection
      defaultPagination={{ pageIndex: 0, pageSize: 5 }}
      components={chakraComponents} changed line
    />
  );
}
import { DataTable } from "slotsmith/data-table";
import { antdComponents } from "../adapters/data-table/antd"; changed line
import { columns, people } from "../shared/people";

export default function Swap() {
  return (
    <DataTable
      data={people}
      columns={columns}
      enableRowSelection
      defaultPagination={{ pageIndex: 0, pageSize: 5 }}
      components={antdComponents} changed line
    />
  );
}

Two kinds of parts

Every part of every component is one of two kinds. The kind tells you what your replacement receives.

<Row>Element parts

An element part renders one DOM element. It gets plain DOM props, with state as data-* attributes, so a library's primitive drops in as it is.

import TableRow from "@mui/material/TableRow";
import { DataTable } from "slotsmith/data-table";
import { columns, people } from "../shared/people";

// Row is an element part: TableRow takes the <tr> props as is.
export default function ElementPart() {
  return (
    <DataTable
      data={people}
      columns={columns}
      components={{ Row: TableRow }}
    />
  );
}

{Checkbox}Widget parts

A widget part is told what is true: checked, indeterminate, onCheckedChange. A few lines map that to your component's props.

import Checkbox from "@mui/material/Checkbox";
import { DataTable, type CheckboxSlotProps } from "slotsmith/data-table";
import { columns, people } from "../shared/people";

// Checkbox is a widget part: told what is true, it maps
// that to MUI's own props.
function MuiCheckbox(props: CheckboxSlotProps) {
  return (
    <Checkbox
      checked={props.checked}
      indeterminate={props.indeterminate}
      disabled={props.disabled}
      onChange={(_, checked) => props.onCheckedChange(checked)}
      slotProps={{ input: { "aria-label": props["aria-label"] } }}
    />
  );
}

export default function WidgetPart() {
  return (
    <DataTable
      data={people}
      columns={columns}
      enableRowSelection
      components={{ Checkbox: MuiCheckbox }}
    />
  );
}
What each kind of part receives
KindReceivesDrop-in for
Element parts<Root><Table><Row><Cell>Plain DOM props, with state as data-* attributesAny library's primitives, unchanged: shadcn/ui, MUI or Chakra, for example
Widget parts{Checkbox}{Pagination}{Empty}Semantic props (checked, pageIndex, setPageSize)Your components, through a few-line adapter

Read the guides

Four components

Each panel is the component itself, with its built-in parts. Open one for its guides and API.

Data table

A table with sorting, selection, pagination, server data, tree rows, virtual rows and row reorder.

Due
Ship v2LenaOct 3
Fix exportOmarOct 6
Audit formsSofiaOct 9
Plan Q4YukiOct 14

Autocomplete

Combobox and select in one component.

File uploader

A file picker with uploads, tiles, validation, record editing and virtual lists.

Drag and drop, or browse

Date picker

A date picker for single days, ranges and multiple dates, with bounds, blocked days and presets.

Works with what you have

Keep your design system, whichever it is, and your data layer. Here are a few well-known libraries; each opens the page that shows it working.

Languages and right-to-left

Every label comes from a locale pack. Switch the language: the labels change, and the table mirrors for right-to-left.

Lena ParkBerlinPaid
Omar HaddadAmmanShipped
Sofia ReyesMadridPacking
Yuki TanakaOsakaPaid
Amir NasserTehranShipped

Read the languages guide

Use with AI agents

slotsmith-ai is an MCP server. It gives your coding agent every prop, slot and label, and copies ready-made adapters into your project. Claude Code adds it with one command; Cursor reads .cursor/mcp.json; VS Code reads .vscode/mcp.json.

claude mcp add slotsmith -- npx -y slotsmith-ai mcp

Read the AI tools guide

Tests, size and license

{ tests: 1761, suites: 20, gzip: "38.5 KB", license: "ISC", react: ">=18" }

FAQ

Does slotsmith work with the Next.js App Router?

Yes. Every entry point starts with "use client", so you can import a component straight into a Server Component file. It renders on the server and hydrates on the client.

See it in the docs

Do I need Tailwind?

No. Each component ships a plain CSS stylesheet, themed with CSS custom properties. Tailwind is only needed if your own design system uses it.

See it in the docs

Does it work with my existing design system?

Yes. Every part is a slot: pass your design system's button, checkbox or select and it replaces the fallback. The Adapters pages show shadcn/ui, MUI, Chakra, Ant Design and Radix Themes as examples.

See it in the docs

How much does one component add to my bundle?

Each component has its own entry point. The data table entry is 38.5 KB gzipped, not counting React and TanStack Table, which it imports; slotsmith has no dependencies of its own. The Trust page lists every entry.

See it in the docs

Does it work with React 18?

Yes. The peer dependency range is react >=18, so React 18 and React 19 both work.

See it in the docs

What is the license?

ISC. You can use slotsmith in commercial and closed-source projects.

See it in the docs

Install slotsmith

npm i slotsmith