ArtisanPack UI
DocsDesign SystemUpgrading @artisanpack-ui/react 1.x to 2.0

Upgrading @artisanpack-ui/react 1.x to 2.0

Design System v1.0.0

2.0 is rebuilt on the ArtisanPack UI design system. The components keep their purpose, and three things change underneath them: - DaisyUI is gone...

@artisanpack-ui/react 2.0 is rebuilt on the ArtisanPack UI design system. The components keep their purpose, and three things change underneath them:

  • DaisyUI is gone. Components are styled by @artisanpack-ui/css: plain CSS in cascade layers, driven by design tokens. Tailwind is optional.
  • One API across Livewire, React and Vue. Some components, props and events were renamed to match. Most 1.x exports still work in 2.0, marked @deprecated, so an app keeps building while you migrate, but they go in the next major.
  • Theming is tokens. @artisanpack-ui/tokens 2.0 replaces the DaisyUI color maps with presets built on the tokens.

The codemod does the mechanical part. This guide covers it and what is left for you.

How long the old version is supported

The previous major gets security fixes only for 12 months after the new one ships. No features or component fixes are backported. Each repository's SECURITY.md carries the end date from launch day.

The old and new majors can't be mixed in one app: the new CSS replaces DaisyUI, and the two style the same components differently. Upgrade a whole app at once, on a branch.

1. Update the packages

Commit your work first, then:

npm install @artisanpack-ui/react@^2 @artisanpack-ui/css
npm uninstall daisyui

2. Run the codemod

Preview the changes:

npx @artisanpack-ui/codemods react src --dry-run

The dry run prints a diff of every file it would change and writes nothing. Run it without --dry-run to apply them:

npx @artisanpack-ui/codemods react src
Option What it does
paths Files or directories to migrate. Default: src
--dry-run Show the changes as a diff without writing
--no-classes Leave raw DaisyUI classes out of the report
--json Print the report as JSON

It reads .tsx, .jsx, .ts and .js files and skips node_modules, build output and dot-directories. It edits in place and keeps your formatting and comments; run your formatter afterwards if a rewritten line runs long.

What it rewrites. Imports from @artisanpack-ui/react and its entry points (/form, /layout, …), with every reference to a renamed component and its Props type; renamed props and events on JSX elements; flags that became values (vertical to orientation="vertical"); values that changed with the name (position="left" to placement="start"); and per-part class props, merged into classes. It follows aliases (Modal as Sheet) and namespace imports (UI.Modal).

What it reports and leaves alone. Each line of the report has the file, line and column:

Label Meaning
manual A prop, event or component with no automatic replacement. The message says what to do
review A change that was made and deserves a look: a renamed event whose payload changed, or a dynamic value the codemod couldn't read
removed A prop that was deleted because 2.0 has no equivalent
class A raw DaisyUI class, theme variable or DaisyUI configuration. See step 3

It can't see through a spread (<Modal {...props} />) or a dynamic value (vertical={isTall}); both are reported. You can run the codemod again after you fix things by hand: migrated code has nothing left for it to rewrite. The exception is a 1.x name that 2.0 reuses with a new meaning, such as label on Code. Preview a second run with --dry-run.

3. Remove DaisyUI and adopt the new CSS

Load the CSS once. Without Tailwind, import it in your app's entry:

import '@artisanpack-ui/css';

With Tailwind v4, import it from your Tailwind entry stylesheet instead. Declare the layer order first, so Preflight never restyles a component and a utility on a component still wins. The last import is optional: it adds utilities generated from the tokens (bg-surface-page, text-muted, rounded-control).

@layer theme, base, apui, components, utilities;
@import 'tailwindcss';
@import '@artisanpack-ui/css';
@import '@artisanpack-ui/css/tailwind';

Then delete @plugin "daisyui"; and any @plugin "daisyui/theme" block from your CSS.

To bring your own styles, import @artisanpack-ui/css/headless instead: the same markup, ARIA and behavior with no component CSS.

Replace raw DaisyUI classes

Markup that used DaisyUI classes directly (btn, card, badge, bg-base-200) has no styles once DaisyUI is gone. The upgrade tool lists every one with its file and line and never rewrites them, because the right replacement depends on the element. For each:

You have Use instead
A component class: btn, badge, card, alert, tab, modal, tooltip, … The component. <a class="btn btn-primary"> is a Button with href. Where you need the bare markup, the apui- class and data attributes: class="apui-button" data-color="primary"
A size or color modifier: btn-sm, badge-success, alert-warning The component's size and color props (data-size, data-color on bare markup)
input, select, textarea, checkbox, toggle, label on native controls The form components, which draw the label, hint and error too
bg-base-100 bg-surface-default with the Tailwind preset, or background: var(--apui-surface-default)
bg-base-200 bg-surface-sunken · var(--apui-surface-sunken)
bg-base-300, border-base-300 border-default · var(--apui-border-default); as a background, bg-surface-sunken
text-base-content text-default · var(--apui-text-default)
text-base-content/70, /60, /50 text-muted · var(--apui-text-muted). text-subtle for the faintest text. Opacity on text is gone: the tokens are checked for contrast, an opacity isn't
text-primary-content, text-error-content, … The color's own foreground: text-primary-foreground, text-error-foreground · var(--apui-color-primary-foreground), var(--apui-color-error-foreground)
bg-primary, text-error, border-success Unchanged with the Tailwind preset. Without Tailwind, the color's own token: var(--apui-color-primary), var(--apui-color-error), var(--apui-color-success)
rounded-box, rounded-btn rounded-surface, rounded-control · var(--apui-radius-surface), var(--apui-radius-control)

The tool marks names such as input, table, link and label as "may be": they are DaisyUI classes and also common class names of your own. Check each.

Left and right are logical now. Use ms-*, me-*, ps-*, pe-*, start-* and end-* next to components so layouts mirror in right-to-left languages.

4. Migrate your theme

@artisanpack-ui/tokens 1.x shipped DaisyUI color maps, cn(), resolveColor(), glass helpers and a Tailwind plugin. 2.0 replaces all of them with the design tokens as --apui-* custom properties, which @artisanpack-ui/css already includes. Remove the 1.x Tailwind plugin from your config and any imports of those helpers.

A brand is a preset. The generator ships with the tokens package, so install it, then generate a preset from your colors:

npm install --save-dev @artisanpack-ui/tokens@^2
npx apui-generate-preset --name acme --primary "#7c3aed" > acme.css

The generator refuses a preset that fails WCAG 2.2 AA contrast in any theme. Import the file after @artisanpack-ui/css and switch it on with <html data-preset="acme">.

The theme lives in data-theme on <html>. ThemeToggle sets and stores it and reports a change as onValueChange; its modes prop is now choices. Render ThemeScript in the document head to apply the stored theme before first paint.

Repoint theme variables

DaisyUI's custom properties are gone, and nothing aliases them. The tool lists each reference:

DaisyUI Token
--color-base-100 --apui-surface-default
--color-base-200 --apui-surface-sunken
--color-base-300 --apui-border-default
--color-base-content --apui-text-default
--color-primary, --color-error, … --apui-color-primary, --apui-color-error, …
--color-primary-content --apui-color-primary-foreground
--radius-box --apui-radius-surface
--radius-field --apui-radius-field
--radius-selector --apui-radius-control
var(--p), var(--b1), var(--bc) (DaisyUI 4) The same tokens as above

--color-* is also a Tailwind theme namespace, so some of these may be yours and not DaisyUI's. The tool can't tell; you can.

Themes and presets

Before Now
data-theme="light" or a DaisyUI theme name data-theme="light", "dark" or "high-contrast". With no attribute the page follows the system setting
A custom DaisyUI theme, or a generated theme file A preset: a named set of token values switched with data-preset. ledger and harbor ship with the CSS
Glass props with tints and opacities surface="glass" on the component; the look comes from the glass tokens
Per-component color overrides Tokens. Set --apui-color-primary and the rest once, in a preset

Theme, preset and density (data-density="compact") combine, and any element can set them for its subtree.

5. Renamed and consolidated components

1.x Now Notes
<Dropdown> <DropdownMenu>
<DropdownItem> By hand Use DropdownMenuItem, which needs a value; the menu reports the choice through onSelect.
<Editor> <CodeEditor>
<ErrorDisplay> <ErrorState>
<Modal> <Dialog>
<Radio> <RadioGroup>
<Sidebar> By hand Use AppShell, the whole page frame, with a Menu in its sidebar slot.
<SpotlightSearch> <Spotlight>
<StatGroup> <Grid>
<ToastMessage> By hand Use Toast, which takes title, description and color instead of a toast object.
<ToastProvider> By hand Render one <Toaster /> instead of wrapping the app, and create toasts with toast.create().

6. Changes by component

Every 1.x prop and event that changed. "Automatic" says whether the codemod makes the change for you.

<Accordion>

1.x 2.0 Automatic
openIndices Now value. Yes
defaultOpenIndices Now defaultValue. Yes
join Remove join. If it was join={false} (separate panels), use variant="separated". By hand
onOpenChange The onOpenChange event is not in the 2.0 API. Check the component's docs for what replaces it. By hand

<Alert>

1.x 2.0 Automatic
visible Removed. Alert dismisses itself; listen to open-change. Yes
onDismiss Now onOpenChange. Check the handler: the payload is boolean. Yes, to review

<Avatar>

1.x 2.0 Automatic
image Now src. Yes
placeholder Now name. Yes
ring Removed. Avatar has no ring in 2.0. Yes
online Now status="online". Yes
offline Now status="offline". Yes
subtitle Avatar no longer renders a title or subtitle. Render the text beside it, for example in a ListItem (title, description, leading). By hand

<Badge>

1.x 2.0 Automatic
outline Now variant="outline". Yes

<Button>

1.x 2.0 Automatic
label Pass the text as children instead of label. By hand
link Now href. Yes
external Set target="_blank" and rel="noopener noreferrer" alongside href. By hand
badge Render a Badge inside the button's children. By hand
badgeClasses Button has no badge part. Style it with class, or a classes key: root, icon, iconEnd, label, spinner. By hand
responsive Removed. The label is no longer hidden on small screens; use iconOnly with an aria-label for an icon-only button. Yes
tooltip Wrap the button in <Tooltip content="…">. By hand
tooltipPosition Set placement on the wrapping Tooltip (logical values such as top, bottom, start, end). By hand
iconRight Now iconEnd. Yes

<Calendar>

1.x 2.0 Automatic
value Now date. Yes
weekStartsOnSunday Now weekStartsOn. Yes
highlightWeekends Removed. Yes
highlightToday Removed. Today is always marked. Yes
minDate Remove it: the calendar no longer limits dates (the prop is still accepted but ignored). By hand
maxDate Remove it: the calendar no longer limits dates (the prop is still accepted but ignored). By hand
color Set color on each entry in events instead (the prop is still accepted but ignored). By hand
renderDay Removed, with no replacement. By hand
1.x 2.0 Automatic
showIndicators Removed. Yes
showArrows Removed. Yes

<Chart>

1.x 2.0 Automatic
data Now series. Yes
color Remove it: Chart has no color prop; series colors come from the theme. By hand
options Removed, with no replacement. By hand
showLegend Removed. The legend shows for two or more series. Yes
animated Removed. Chart draws its own SVG and has no animation switch. Yes

<Checkbox>

1.x 2.0 Automatic
right Removed. The label position is fixed by the design system. Yes
hint Now description. Yes
color Remove it: Checkbox has no color prop in 2.0. By hand
card A single Checkbox has no card layout. Use CheckboxGroup with variant="cards". By hand
cardClass Gone with card. With CheckboxGroup variant="cards", style the cards through classes. By hand

<Clipboard>

1.x 2.0 Automatic
text Now value. Yes
successLabel Now copiedLabel. Yes
successDuration Now timeout. Yes
color Removed, with no replacement. By hand
onCopy Now onCopied. onCopied; onCopy stays a deprecated alias. Yes, to review

<Code>

1.x 2.0 Automatic
label Now fileName. Yes
hint Remove it and render the text under the code block yourself. By hand
showLineNumbers Now numbered. Yes
maxHeight Removed, with no replacement. By hand

<Collapse>

1.x 2.0 Automatic
icon Remove it: the indicator icon is fixed. Style it through classes.icon. By hand
name Removed, with no replacement. By hand
bordered Removed. The look comes from variant (section or read-more). Yes
title Now label. Yes

<ColorPicker>

1.x 2.0 Automatic
clearable Removed. There is no clear button in 2.0. Yes
random Removed. There is no random-color button in 2.0. Yes
icon Remove it: the field shows its own swatch trigger. By hand
iconRight Remove it: the field has no trailing icon. By hand
randomIcon Remove it: the random-color button is gone. By hand
onClear The onClear event is not in the 2.0 API. Check the component's docs for what replaces it. By hand
onRandomColor The onRandomColor event is not in the 2.0 API. Check the component's docs for what replaces it. By hand

<DatePicker>

1.x 2.0 Automatic
inline Removed. The floating/inline label is gone; the label sits above the field. Yes
dateType dateType="datetime-local" becomes the time prop; for time, month and week use <Input type="…">; date is the default, so remove it. By hand
icon Remove it: DatePicker has no icon props. By hand
iconRight Remove it: DatePicker has no icon props. By hand

<Diff>

1.x 2.0 Automatic
oldLabel Removed, with no replacement. By hand
newLabel Removed, with no replacement. By hand
mode Use view (unified · split). By hand

<Divider>

1.x 2.0 Automatic
vertical Now orientation="vertical". Yes
color Use strength. By hand
labelPosition Now align. Yes

<Drawer>

1.x 2.0 Automatic
end Now side="end". Yes
persistent No equivalent: Escape and the backdrop always close the drawer. hideClose only hides the close button. By hand
onClose Now onOpenChange. Check the handler: the payload is boolean. Yes, to review
1.x 2.0 Automatic
label Unchanged. Still the trigger's text. Yes
end Now align="end". Yes
top Remove it: DropdownMenu has no prop to open above the trigger. By hand
hover Removed. The menu opens on click and from the keyboard only. Yes
trigger DropdownMenu renders its own trigger button. Pass the trigger's content as label. By hand

<EmptyState>

1.x 2.0 Automatic
heading Now title. Yes
headingAs Now headingLevel. Yes
action Now actions. Yes

<ErrorDisplay> → <ErrorState>

1.x 2.0 Automatic
message Now description. Yes
retryLabel Render the retry button yourself in actions, for example actions={<Button onClick={retry}>Try again</Button>}. By hand
onRetry The onRetry event is not in the 2.0 API. Check the component's docs for what replaces it. By hand

<File>

1.x 2.0 Automatic
withDragDrop Removed. The drop zone is always on. Use variant="compact" for the plain row. Yes
dragDropText Now title. Yes
dragDropClass Use classes={{ dropzone: "…" }}. By hand
progress Progress is per file: set progress on the entries you pass in value. By hand
hideProgress Removed. A progress bar shows only for value entries that have progress. Yes
onFilesSelected The onFilesSelected event is not in the 2.0 API. Check the component's docs for what replaces it. By hand

<Grid>

1.x 2.0 Automatic
cols Now columns. Yes
colsSm Use minItemWidth for responsive columns. By hand
colsMd Use minItemWidth. By hand
colsLg Use minItemWidth. By hand
colsXl Use minItemWidth. By hand
gapX One gap step. By hand
gapY One gap step. By hand

<Icon>

1.x 2.0 Automatic
path Removed, with no replacement. By hand
viewBox Removed, with no replacement. By hand

<Input>

1.x 2.0 Automatic
clearable Removed. Input has no clear button in 2.0. If you need one, render your own. Yes
inline Removed. The floating/inline label is gone; the label sits above the field. Yes
iconRight Now iconEnd. Yes
onClear The onClear event is not in the 2.0 API. Check the component's docs for what replaces it. By hand

<Markdown>

1.x 2.0 Automatic
renderMarkdown Removed, with no replacement. By hand
1.x 2.0 Automatic
activeKey Mark the active item with current (on the items entry or on MenuItem). Sub-menus holding it open by default. By hand
horizontal Not in the 2.0 API (planned as orientation). Remove it, or check the component's docs for what replaces it. By hand
color Remove it: Menu has no color prop in 2.0. By hand
size Remove it: Menu has no size prop in 2.0. By hand
compact Removed. Menu spacing follows the design system. Yes
1.x 2.0 Automatic
subtitle Now description. Yes
persistent No single equivalent: role="alertdialog" stops backdrop clicks closing it and hideClose hides the close button, but Escape still closes it. By hand
glass Now surface="glass". Yes
bottom Dialog has no bottom-sheet mode. Use Drawer with side="bottom". By hand
actions Now footer. Yes
onClose Now onOpenChange. Check the handler: the payload is boolean. Yes, to review
1.x 2.0 Automatic
glass Now surface="glass". Yes
start Now brand. Yes
center Pass the links as links or NavbarItem children; a search field goes in search. By hand
end Now actions. Yes

<Pagination>

1.x 2.0 Automatic
currentPage Now page. Yes
totalPages Now pageCount. Yes
size Remove it: Pagination has no size prop in 2.0. By hand

<Password>

1.x 2.0 Automatic
inline Removed. The floating/inline label is gone; the label sits above the field. Yes
clearable Removed. Password has no clear button in 2.0. Yes
hideToggle The reveal button is always shown in 2.0. Use Input type="password" if you need a field without it. By hand
hiddenIcon Remove it: the reveal button's icons are built in. By hand
visibleIcon Remove it: the reveal button's icons are built in. By hand
onClear The onClear event is not in the 2.0 API. Check the component's docs for what replaces it. By hand

<Pin>

1.x 2.0 Automatic
numeric Now type="numeric". Yes
hide Now mask. Yes
color Remove it: Pin has no color prop in 2.0. By hand
onIncomplete The onIncomplete event is not in the 2.0 API. Check the component's docs for what replaces it. By hand

<Popover>

1.x 2.0 Automatic
triggerMode Popover opens on click; hover is Tooltip's job. By hand
position Now placement. Values: left is now start, right is now end. left and right are now start and end. Yes
showDelay Removed, with no replacement. By hand
hideDelay Removed, with no replacement. By hand
persistent Removed. Yes

<Progress>

1.x 2.0 Automatic
showValue Removed. Use label and valueText. Yes

<Radio> → <RadioGroup>

1.x 2.0 Automatic
color Remove it: RadioGroup has no color prop. By hand
optionValue Map your options to { value, label, description } objects; the key props are gone. By hand
optionLabel Map your options to { value, label, description } objects; the key props are gone. By hand
optionHint Map your options to { value, label, description } objects; the hint is each option's description. By hand
inline Now orientation="horizontal". Yes
card Now variant="cards". Yes
cardClass Use classes={{ item: "…" }} with variant="cards". By hand

<RichTextEditor>

1.x 2.0 Automatic
minHeight Remove it: minHeight is gone. Set a min-height in CSS through classes if you need one. By hand

<Select>

1.x 2.0 Automatic
placeholderValue Remove it: the placeholder option always has an empty value. By hand
inline Removed. The inline label is gone; the label sits above the field. Yes
optionValue Map your options to { value, label } objects (or strings); the key props are gone. By hand
optionLabel Map your options to { value, label } objects (or strings); the key props are gone. By hand
iconRight Remove it: Select only takes a leading icon; the end holds the built-in indicator. By hand

<Skeleton>

1.x 2.0 Automatic
circle Now shape="circle". Yes

<Sparkline>

1.x 2.0 Automatic
type Now variant. Yes
color Remove it: Sparkline has no color prop; style it through classes or CSS. By hand
strokeWidth Remove it: the stroke width is set by the design system. By hand
curve Removed. Lines are drawn by the shared geometry; there is no curve switch. Yes
fillOpacity Remove it: use variant="area" for a fill; its opacity is set by the design system. By hand
showTooltip Removed. Sparkline has no hover tooltip; it is described by label. Yes

<SpotlightSearch> → <Spotlight>

1.x 2.0 Automatic
items Use groups: wrap the items as [{ label, items }] and rename item fields to the SpotlightResult shape (label, description, href, icon, keywords). By hand
filterFn No custom filter. Set remote, track the query with search/onSearchChange, and pass already-filtered groups. By hand
maxResults Remove it: there is no result cap. Limit the items you pass (with remote if you filter yourself). By hand
emptyMessage Now emptyLabel. Yes
onClose Now onOpenChange. Check the handler: the payload is boolean. Yes, to review

<Stack>

1.x 2.0 Automatic
direction Now orientation. Yes

<Stat>

1.x 2.0 Automatic
changeLabel Now description. Yes
sparklineData Use KpiCard's sparkline. By hand
sparklineType Removed, with no replacement. By hand
sparklineColor Removed, with no replacement. By hand
title Now label. Yes
actions Remove it: Stat has no actions area. Put the controls beside the Stat, for example in a Card. By hand

<StatGroup> → <Grid>

1.x 2.0 Automatic
horizontal Removed. React StatGroup: a row of stats is a Grid with columns. Yes

<Steps>

1.x 2.0 Automatic
currentStep Use value, which counts from 1: currentStep={n} becomes value={n + 1}. By hand
vertical Now orientation="vertical". Yes
color Remove it: Steps has no color prop. Use a step's state for an error step. By hand
size Remove it: Steps has no size prop in 2.0. By hand

<Table>

1.x 2.0 Automatic
striped Removed. Yes
hoverable Removed. Yes
compact Now density="compact". Yes
size Use density (compact, default or comfortable). size is still accepted but ignored. By hand
noHeaders Now hideHeaders. Yes
selectedKeys Now value. Yes
selectableKey Now keyBy. Yes
expandableKey Now keyBy. Yes
containerClassName Now the root key of classes. Yes
onSort Now onSortChange. Check the handler: the payload is { column: string; direction: 'asc' | 'desc' }. Yes, to review
onSelectionChange Now onValueChange. Check the handler: the payload is Array<string | number>. Yes, to review

<Tabs>

1.x 2.0 Automatic
activeTab Now value. Yes
defaultTab Now defaultValue. Yes
vertical Now orientation="vertical". Yes
verticalRight Removed. Covered by orientation and logical placement. Yes
color Remove it: Tabs has no color prop. Choose the look with variant (segmented or underline). By hand
tabListClassName Now the list key of classes. Yes
panelClassName Now the content key of classes. Yes
activeTabClassName Tabs has no activeTab part. Style it with class, or a classes key: root, list, trigger, icon, count, content. By hand
onChange Now onValueChange. Check the handler: the payload is string. Yes, to review

<Textarea>

1.x 2.0 Automatic
inline Removed. The inline label is gone; the label sits above the field. Yes

<ThemeToggle>

1.x 2.0 Automatic
modes Now choices. Yes

<Timeline>

1.x 2.0 Automatic
vertical Removed. Timelines are vertical. Yes
compact Removed. Yes
snap Removed. Yes

<Toggle>

1.x 2.0 Automatic
right Removed. The label position is fixed by the design system. Yes
hint Now description. Yes

<Tooltip>

1.x 2.0 Automatic
tip Now content. Yes
position Now placement. Values: left is now start, right is now end. left and right are now start and end. Yes
color Tooltips use the inverse surface; tooltip-<color> is removed. By hand

7. After the codemod

  • Work through the manual lines of the report, then the class lines.
  • Events. Value and open changes are onValueChange and onOpenChange. The codemod renames handlers and marks each for review, because the payload is now the value and not a DOM event.
  • Toasts. ToastProvider no longer wraps the app. Render one Toaster and create toasts with toast.create().
  • Left and right. Placement and side props take start and end, so layouts mirror in right-to-left languages.
  • Run the type checker: every prop the codemod left behind is a type error that points at the line.
  • Look at each page in light, dark and high-contrast themes.