The changelog lists slotsmith’s release notes, newest first: what each release added, changed and fixed. The notes come from the README on GitHub, so this page and the package always say the same thing. Each version heading is a link you can share.

Unreleased

In the repository, not published to npm yet.

  • Each item in the shadcn/ui registry now installs a ready component next to its parts (<DatePicker /> from @/components/slotsmith/date-picker), and the new @slotsmith/provider item applies all four adapters to the app through one SlotsmithProvider; see Adapters.
  • The ready-made date-picker and autocomplete adapters lay out the calendar grid and the field themselves, so they hold together without slotsmith’s stylesheet: Tailwind classes in shadcn/ui, sx in MUI, css in Chakra UI and a scoped style in Ant Design and Radix Themes. Copy them again from slotsmith-ai.

1.10.0

  • The date picker’s trigger is now a text field: a date can be typed in the locale’s numeric order (MM/DD/YYYY in en-US, DD.MM.YYYY in German), read on Enter or when focus leaves, or picked from the calendar, which opens from a button beside the field. openOnFocus opens the calendar when the field is focused or clicked. trigger="button" keeps the single button that shows the formatted date. New slots Input and Toggle, the openCalendar label in every language, and getInputProps, getToggleProps, inputText, inputPattern and focusTrigger on useDatePicker; every adapter renders its library’s own input and icon button. See Typing a date.
  • The date picker’s value is an ISO instant by default: valueFormat now defaults to "iso-datetime", the instant the day starts in timeZone ("2026-03-12T00:00:00+03:00"), instead of "YYYY-MM-DD". A day without a zone, such as "2026-03-12", is still read as that day, so stored values keep loading; set valueFormat="YYYY-MM-DD" to keep getting the calendar day alone. The spreadsheet’s date cells keep the calendar day.
  • slotsmith has no dependencies of its own: popups are placed by its own positioner instead of @floating-ui/react-dom, which is no longer installed with it. They still flip above the trigger when there is no room below, stay inside the viewport and follow every scroll and resize.
  • The date picker and the autocomplete take portal: true renders the popup in document.body, or pass an element of your own. A popup in a portal, theirs or a UI library’s, still counts as part of the control, so a press or focus inside it does not close it.
  • The date-picker and autocomplete adapters open in their library’s own popover: MUI’s Popper, shadcn/ui’s and Radix Themes’ Popover, Chakra’s Popover and Ant Design’s Popover, anchored to the trigger through useDatePickerContext().position.anchor (or the autocomplete’s). Every adapter part that has a component in its library now renders it: the shadcn/ui date picker’s and autocomplete’s triggers are InputGroups, with the date picker’s field an InputGroupInput and its clear and calendar controls InputGroupButtons, its days, navigation and presets are Buttons and its month and year are NativeSelect; the Radix Themes and Ant Design date pickers’ month and year are their library’s Select, with the menu kept inside the calendar, and Radix’s field is TextField; the shadcn/ui table’s pagination is Pagination and its empty and error states are Empty. The shadcn/ui adapters now need shadcn/ui’s popover, plus button, input-group and native-select for the date picker and empty and pagination for the data table; copy the adapters again from slotsmith-ai.
  • The date picker takes name: a hidden input per picked day carries the value as valueFormat writes it, so a plain HTML form submits it.
  • The file uploader takes name, so a plain HTML form submits its items: a file picked or dropped in the browser as the file, and a file already on a server (restored from a URL, or uploaded) as its URL.
  • Fixes: the autocomplete keeps the highlighted option in view by scrolling its list alone; it scrolled the page as well, which made the page jump when a popover was still being placed. The MUI autocomplete adapter’s tag remove button is the size of a Chip’s own delete icon, and the MUI, Chakra and Ant Design toggles sit inside the field.

1.9.0

  • Spreadsheet mode for the data table; see Spreadsheet mode. Pass spreadsheet, the rows as value (or data) and onValueChange(value, change); columns opt in with edit (true, or editable, editor, component, options, parse, format, validate, setValue and empty), typed by the value of the field their accessorKey reads. Cells are an ARIA grid with one tab stop: the arrow keys, Home, End, PageUp and PageDown move, Shift extends, Ctrl jumps to the edge of the data, a click or Enter opens the editor over the cell, typing replaces the value, Delete clears, Ctrl+C, Ctrl+X and Ctrl+V copy and paste tab-separated text, the fill handle and Ctrl+D or Ctrl+R fill, and Ctrl+Z and Ctrl+Y undo and redo. The editors are an input for text and numbers, the slotsmith Autocomplete for a select, and for a date (ISO YYYY-MM-DD) the slotsmith DatePicker passed as spreadsheet.datePicker, or the browser’s <input type="date"> without it, so a table that edits no dates does not ship the picker, and a checkbox column shows the table’s Checkbox. change names its source and every changed row (rowId, previous, next, columnIds). New slots CellEditor and FillHandle, twelve new labels in every locale pack (editCell, invalidNumber, invalidOption, invalidDate, invalidBoolean, cellsCopied, cellsCut, cellsPasted, cellsCleared, cellsFilled, undone, redone), an optional tabIndex on the Checkbox slot (pass it on), and useDataTableContext().spreadsheet with undo() and redo(). Every ready-made data-table adapter gains a cell editor, a fill handle and selection styles in its library’s look; copy them again from slotsmith-ai, and add shadcn/ui’s input.
  • Faster rendering: a body row re-renders only when its own data, its own state (selected, expanded, dragged), the columns, the slots, the labels or slotProps change, so an edit re-renders one row and a change elsewhere on the page re-renders none. An inline labels or components object no longer counts as a change, and an inline onRowClick no longer re-renders the rows. A cell renderer that reads state from outside its row (table.getState() or a value it closes over) should get it through columns, which re-render every row when they change.
  • Fixes: a row drag no longer ends when React re-creates the rows, as it does on every render when a Row slot is defined during render: the drag finds its row and handle again and a keyboard drag keeps focus on the handle. Such a slot is still slow, so a development warning now asks for it to be defined outside the component.
  • A row dropped while a column is sorted now stays where it was dropped: the order on screen, with the row moved, is reported as the new data (and siblings), and the sort is cleared through onSortingChange. The new reorderWhileSorted ("clearSort", the default, or "block") can disable the handles while sorted instead. canReorder takes sorted and whileSorted, and ReorderWhileSorted is exported.
  • DataTable, Autocomplete and FileUploader take virtual: pass the virtualizer hook as use (useVirtualizer from @tanstack/react-virtual) with the sizes, and only the rows or options in view are rendered. The hook is passed in, so slotsmith never imports the optional peer, and the windowed parts now come from the main entries too: DataTable.VirtualBody, Autocomplete.VirtualPopup and .VirtualList, FileUploader.VirtualList, with the UseVirtualizer type. slotsmith/virtual keeps VirtualDataTable, VirtualAutocomplete, VirtualFileUploader and the virtual lists, now thin wrappers that pass useVirtualizer for you.
  • Rows of a virtual data table can now be reordered (enableRowReorder was ignored there): the dragged row stays rendered while it is scrolled out of the window, the view scrolls under it near the edges, and the keyboard scrolls to the row it moves to. The development warning is gone. useDataTable’s reorder has setGeometry, which a windowed body uses to give the engine every row’s place.
  • Rows of a tree can move to another parent and another depth with reorderAcrossParents: drag up or down to pick the gap and sideways to pick the depth (one indent per level), or press the left and right arrow keys while a row is lifted. The rows make room as before, and the lifted row is drawn at its new depth. The table builds the new tree with the new setSubRows(row, subRows) option (which spreadsheet mode also reads, for sub-row edits), so a drop reports the whole tree as data. canDrop(move) refuses places, and a row never lands inside its own sub-rows. RowOrderChange gains index, fromParentId and fromParent for every drop, and position can be "inside". A new reorderLevel label in every locale pack announces the depth. New exports moveInTree, RowMove, DropPlace, Placement and TreeNode.
  • Dragging a selected row now drags every selected sibling with it: the group closes up under the pointer, the other rows make room, and it lands together, in its own order. RowOrderChange gains rows and rowIds, every row a drop moved (the dragged row alone when it is not selected).
  • Rows can be dragged between tables: tables with the same reorderGroup exchange top-level rows, by pointer (a copy of the rows follows the pointer over the other table, and a line marks where they would land) or from the keyboard (past a table’s last row into the next table, before its first into the previous one). onRowTransfer(transfer) fires on both tables with rows, rowIds, from, to and data, the receiving table’s own new rows; reorderTableId names a table in transfers, and the receiving table’s canDrop sees fromTableId. New export RowTransfer; rows carry data-drop-incoming where incoming rows would land, and the ready-made adapters draw that line in their library’s look.
  • The date picker takes valueFormat and timeZone. valueFormat writes every day it exchanges with you (value, defaultValue, onChange, minDate, maxDate, the presets and the day disabledDates is asked about) as a pattern such as "DD/MM/YYYY" (the default stays "YYYY-MM-DD"), as "iso-datetime", the instant the day starts in the time zone, or with a { format, parse } codec of your own. timeZone (an IANA name, "UTC" or an offset such as "+09:00") sets the zone the calendar works in, as in react-day-picker: today is today there, and instants are read and written there. useDatePicker takes both; new exports createValueCodec, todayIn, dayInZone, DateValueFormat and DateValueCodec, and buildMonth takes the day to mark as today.
  • data is now optional when value is given.
  • Fixes: the shadcn/ui date-picker adapter’s month and year lists open dark in dark mode (they opened white); copy it again from slotsmith-ai. createDataTableColumnHelper returns a helper whose accessor also takes edit, typed by the value it infers. DataTableColumnDef types edit per accessorKey; an accessorFn column written inside a DataTableColumnDef[] array with TanStack’s own createColumnHelper (not slotsmith’s) no longer infers its accessor’s parameter, so annotate it or use createDataTableColumnHelper.

1.8.1

  • Fixes in the ready-made autocomplete adapters; copy them again from slotsmith-ai (Ready-made adapters). The library itself is unchanged from 1.8.0.
  • Fixes: every autocomplete adapter draws its toggle and clear buttons with its library’s own icons and sizes, and raises its popup above the page the way its library does (MUI’s on the theme’s modal layer); the Ant Design adapter keeps its inner input borderless, with options sized like its Select; the Chakra UI adapter’s tags show their remove icon.

1.8.0

  • The autocomplete gains option groups, a search input inside its trigger, autoHighlight and an invalid state (the date picker gains invalid too), and every ready-made adapter follows, rendered with each library’s own parts; SlotsmithProvider can now apply a design system’s slots to every component at once (Once, for the whole app); and --ss-* tokens set on any wrapper now reach the components inside it (Theming). Copy your adapters again from slotsmith-ai (Ready-made adapters).
  • Behaviour changes: component tokens (--sdt-*, --sac-*, --sdp-*, --sfu-*) are inputs only. The stylesheets no longer declare them, so read var(--ss-accent, <default>) instead of var(--sdt-accent) in your own CSS. --ss-* set on any wrapper now applies to the components inside it, and a component token still beats it wherever each is set (a --sdt-accent on :root beats a --ss-accent on a closer wrapper). A component token set on an inner part (for example .sdt__row) is no longer read; set it on the component or above. A popup portalled outside the wrapper does not see the wrapper’s --ss-*; see Theming.
  • SlotsmithProvider takes components: slot overrides for every component below it, one map per component (dataTable, autocomplete, datePicker, fileUploader), so a design system is applied once instead of on every component. A component’s own components prop still wins slot by slot, and nested providers merge; see Once, for the whole app. components.autocomplete also reaches the data table’s built-in page-size select, so its slots are typed for any option and must not assume one option shape. New types SlotsmithComponents and SlotsmithComponentName (the four keys), from slotsmith/provider.
  • Fixes: the type declarations of slotsmith/provider, slotsmith/locale, the locale packs and every component but the data table no longer import @tanstack/react-table, so an app without that optional peer type-checks with skipLibCheck: false; and the main entry’s and slotsmith/locale’s re-exports resolve to their declarations under moduleResolution: "bundler" instead of to the built scripts beside them, which had left them untyped.
  • Fixes: the type declarations name the extension of every relative import (./context.js, ./provider/index.js), so they also resolve under moduleResolution: "node16" and "nodenext", where they had failed to load (or, with skipLibCheck, left every export typed as any). The require condition now points at .d.cts declarations, so a CommonJS app types the CommonJS build instead of being told it imports an ES module.
  • Fixes: the autocomplete’s and the date picker’s clear buttons and the autocomplete’s tag remove buttons are now 24px pointer targets (WCAG 2.5.8), with the trigger the same height and nothing beside them moved; tags are 2px taller, so wrapped rows keep their targets apart. The Ant Design date-picker adapter keeps keyboard focus on a day as it is picked (copy it again from slotsmith-ai). In the Arabic, Persian and Hebrew packs the file uploader’s hint wraps each accepted type in invisible left-to-right isolates (U+2066 … U+2069), so image/* no longer shows as */image, and the messages that name a file (its upload progress, its preview and the wrong-type, too-large and too-small refusals) wrap the name in a first-strong isolate, so a Latin name such as report.v2.pdf keeps its order; a test that compares those hints or messages as strings needs the isolates. VirtualFileUploader scrolls through the whole list with the default stylesheet: its spacer rows no longer shrink, and they allow for the list’s row gap, so estimateSize is a row’s height plus that gap.
  • The autocomplete takes autoHighlight (default false): the first option that can be picked is highlighted whenever the list opens and whenever its contents change, as the search narrows or a page arrives, so Enter picks the top match. A highlight moved with the arrow keys or the pointer is kept until the search changes. useAutocomplete takes it too.
  • The autocomplete and the date picker take invalid (default false) for a value that failed validation. It sets aria-invalid="true" on the combobox, the element that holds the value and takes focus (not on the autocomplete’s search box, whose text is only a filter), and data-invalid on the root and the trigger. The default stylesheet draws the trigger’s border and focus ring in the shared --ss-danger token (#dc2626 light, #f87171 dark); the date picker now reads it too, through a new --sdp-danger component token. Point the combobox at your message with aria-errormessage or aria-describedby on the component, as before. useAutocomplete and useDatePicker take invalid and return it. The ready-made autocomplete and date-picker adapters show each library’s own error style on the trigger (shadcn/ui’s aria-invalid: destructive border and ring, MUI’s error colour and Mui-error class, Chakra UI’s _invalid condition, Ant Design’s status="error" colours, Radix Themes’ red accent); copy them again from slotsmith-ai.
  • Fixes: autoHighlight keeps working after the list is opened from the keyboard while it is still loading or empty, and it follows the search when a caller filtering remotely hands back the same options array; while a first page loads it highlights nothing, so the previous search’s rows are never picked. A popup closed through a controlled open starts its next opening with nothing highlighted. Option ids now come from each option’s value instead of its position, so aria-activedescendant changes when the search brings a new option to the top and screen readers announce it.
  • The autocomplete groups its options with getOptionGroup(option), which returns a group’s name (also its label) or undefined. The options stay one flat list: groups appear in the order their first option does, an option joins its group wherever it sits, so a group whose options arrive across several pages stays together, and the options without a group form one unlabelled section where the first of them appeared. A group the search empties disappears with its label. The keyboard, autoHighlight, typeahead, Home, End, PageUp and PageDown follow the order on screen, and labels and separators are never highlighted. Each group is a role="group" labelled by its label (role="presentation"); the line between sections is presentational and hidden from screen readers, since a listbox may own only options and groups. New slots Group, GroupLabel and Separator, with fallbacks and styles (a small muted label, a hairline between sections), and compound parts Autocomplete.Group, .GroupLabel and .Separator. VirtualAutocomplete windows the labels and separators as rows of their own, sized by the new virtual.groupLabelSize (28) and virtual.separatorSize (9), and now scrolls the highlighted option into view however far the keyboard jumps. useAutocomplete takes getOptionGroup and returns sections, grouped, getGroupProps, getGroupLabelProps and getSeparatorProps; groupOptions does the grouping on its own. Without getOptionGroup the markup is unchanged. The ready-made autocomplete adapters render groups with each library’s own parts (MUI’s ListSubheader and Divider, Chakra UI’s Separator with its combobox group-label look, Ant Design’s group-title look and Divider, Radix Themes’ Select.Label look and Separator, shadcn/ui’s Separator and command-group heading); copy them again from slotsmith-ai, and add shadcn/ui’s separator.
  • The autocomplete takes searchIn ("popup", the default, or "trigger"). With "trigger" the search box is an input inside the trigger, and that input is the combobox (the WAI-ARIA 1.2 editable combobox): it carries role="combobox", aria-expanded, aria-controls, aria-autocomplete="list", aria-activedescendant and aria-invalid, keeps focus while the list is open, and takes the id and aria-* naming props, so a <label for> names it. In single mode it shows the picked label while closed, typing replaces the label with the search (focusing it selects the label), and leaving with Escape, Tab or a click elsewhere puts the label back; the placeholder shows only when nothing is picked. In multiple mode the tags sit before the input on the same wrapping line, Backspace in the empty input removes the last one, and a pick clears the search and keeps the list open. The keyboard follows the editable combobox: typing and ArrowDown open the list, Alt+ArrowDown opens it without moving the highlight and Alt+ArrowUp closes it, Space, Home and End stay in the text, PageUp and PageDown move through the list, and the first Escape both closes the list and clears the search (with a list the parent holds shut, Escape still clears the search); nothing is picked while an input method is composing. The trigger becomes a field that is not a tab stop: a press anywhere on it focuses the input, the clear control stays in the tab order and returns focus to the input, and the chevron is a button outside the tab order that opens and closes the list without taking focus. With searchable={false} it is ignored and the trigger stays a button. New slots TriggerInput (the input) and Toggle (the chevron button, holding the Indicator), a new slotProps.triggerInput, a new toggle label (“Show options”) in every locale pack, and data-search-in="trigger" on the Trigger for styling; the Search and Value parts do not render in this mode. useAutocomplete takes searchIn and returns it with inputValue, getTriggerInputProps, getToggleProps and getPopupProps. Enter no longer picks an option while an input method is composing in the popup’s search box either. Without searchIn the markup is unchanged.
  • The ready-made autocomplete adapters render searchIn="trigger" with each library’s own parts: shadcn/ui’s InputGroup, InputGroupInput, InputGroupAddon and InputGroupButton with Badge tags; MUI’s InputBase in an outlined field with Chip tags and IconButtons; Chakra UI’s Input in a bordered field with Tags and a ghost IconButton; Ant Design’s borderless Input (status="error" when invalid) in the select’s field with Tags and a text Button; Radix Themes’ TextField in a surface field with Badges and a ghost IconButton. Each field shows its library’s focus ring while the input has focus and its error style when invalid. Copy them again from slotsmith-ai, and add shadcn/ui’s input-group.
  • Fixes for option groups: VirtualAutocomplete keeps its scroll position when a page arrives (with autoHighlight it had jumped back to the top), scrolls only when the highlighted option changes, and no longer scrolls a group’s label into view when the pointer, rather than the keyboard, highlights the group’s first option. The highlight stays on its option from the very render in which a page reorders the list or options leave it without the search changing, so aria-activedescendant never points at the wrong option. A group whose label has scrolled out of the virtual window keeps a hidden copy rendered through the GroupLabel slot, which now receives hidden (pass it on), so a custom label names the group the same way. The fallback group label is exactly 28px, the default virtual.groupLabelSize, and so are the MUI, Ant Design and Radix Themes adapters’ labels (copy them again from slotsmith-ai).
  • Fixes: the autocomplete’s list scrolls inside its popup in every ready-made adapter (the list had grown past the popup, so options below it were cut off), and the Ant Design and Radix Themes lists no longer scroll sideways; the shadcn/ui autocomplete adapter matches shadcn/ui’s combobox (field height, tag remove buttons, check mark, search icon) and gains a visible focus ring; the Ant Design and Radix Themes triggers keep their focus ring right when slotProps adds focus handlers; copy these adapters again from slotsmith-ai. In the stylesheet, the autocomplete’s search box and trigger input are 16px on touch screens (pointer: coarse), so phones do not zoom in when they are focused, and the list’s scrollbar is thin and drawn in the border colour.

1.7.0

  • Adapters for Ant Design and Radix Themes are now available through slotsmith-ai, alongside the existing MUI, shadcn/ui and Chakra UI ones — twenty ready-made adapters in all, copied into a project with npx slotsmith-ai add or buildable as a shadcn/ui registry; see Ready-made adapters. Fixes: the data table’s page-size menu follows --sdt-* token overrides, clicking its “Rows per page” label focuses it and its numbers use the table’s locale digits, and the file uploader’s size text no longer throws on a malformed locale tag.

1.6.0

  • Drag-to-reorder rows in the data table, by pointer, touch or keyboard; see Reordering rows. New props enableRowReorder, onRowOrderChange and reorderHandleColumn; a new DragHandle slot, DataTable.DragHandle for placing the handle in a cell of your own, and slotProps.dragHandle, whose handlers run alongside the table’s own; six new labels (reorderRow, reorderInstructions, reorderLifted, reorderMoved, reorderDropped, reorderCancelled) in every locale pack; and moveItem. The order is controlled: onRowOrderChange receives row, rowId, target, targetId, position and the new data, and the table does not reorder until you store it. The dragged row follows the pointer while the others slide out of its way; with reduced motion nothing slides, the row is dimmed and a line marks where it will land. Every step is announced to screen readers.
  • Tree tables reorder too: a row moves among its siblings only, together with its expanded sub-rows. RowOrderChange also carries parentId, parent and siblings; for a sub-row data is the unchanged top level, and siblings is the parent’s new list of children to store.
  • Row state for styling: every body row now carries data-row-id; during a drag the lifted row has data-dragging, its visible sub-rows data-dragging-child, the target data-drop-position (before / after), and the row to draw a drop line on data-drop-edge. A Row slot must keep its identity between renders (define it outside the component): a row that is re-created on every render cancels every drag.
  • While a column sort is active a drop still reorders data, but the view keeps its sorted order, so the row slides back to where the sort puts it. Turn sorting off on a table that is reordered by hand. VirtualDataTable ignores enableRowReorder with a development warning; dragging between tables, dragging several rows at once and moving a row to another parent are not supported.
  • The headless useDataTable returns reorder (the drag state, and the props for rows and handles), reorderable and reorderHandleColumn, and takes labels for the handle’s name and the announcements.
  • The shadcn/ui, MUI and Chakra UI adapters gain a drag handle, and style a lifted row themselves (an opaque background and a shadow), because the built-in drag styles apply to the fallback row only.
  • SlotsmithProvider and useSlotsmithLocale now come from slotsmith/provider, which will also carry shared settings beyond the language. slotsmith/locale still exports them, marked deprecated.
  • All four components now share one default palette and shape, read from the new shared --ss-* tokens; see Theming. Set --ss-accent (and the other --ss-* tokens) to restyle every component at once. The per-component tokens still work, and the date picker and the uploader still follow a theme written against the data table’s tokens.
  • The date picker’s month and year selects now use the component’s colours in dark mode.
  • The data table’s page-size control is now the slotsmith autocomplete, so it matches the other components. The data table’s bundle includes the autocomplete, and slotsmith/data-table.css includes the autocomplete’s styles.
  • The date picker’s footer control now reads “Go to today” and looks like navigation, so it isn’t mistaken for a preset.
  • Six ready-made themes, each with a light and a dark palette, setting only the shared --ss-* tokens: minimal, soft, ocean, forest, sunset and contrast; see Theming.

1.5.0

  • Locale packs and a SlotsmithProvider for translating every component, formatting numbers and plurals, and right-to-left text; see Languages. A string locale with no registered pack now logs one development-only warning instead of silently staying in English.
  • The autocomplete now turns dark with .dark or [data-theme="dark"] on the page, like the other components, instead of with the system setting.

Releases before 1.5.0 have no written notes. The commit history on GitHub has every change.