Presets
Beyond a single global look (see global configuration), you'll often reuse a handful of roles for a component. (a primary button, or a close button for example.). Each with its own classNames and props like color and variants. Repeating those props and classes on every instance make your code easy to be inconsistent and require you to think about configuration on every component instance.
presets let you configure those roles once, then apply one with a single preset prop.
Setup
Declare named presets per component in the EasyUIProvider config, then activate one with the preset prop:
import { EasyUIProvider } from '@easy-ui-react/easy-ui-react'
export function App() {
return (
<EasyUIProvider config={{
presets: {
button: {
primary: {
props: { variant: 'solid', color: 'primary' },
classNames: { base: 'font-semibold' },
},
cancel: {
props: { variant: 'light', color: 'default' },
},
filter: {
props: { variant: 'outlined', color: 'default' },
classNames: { base: 'rounded-full' },
},
},
},
}}>
{/* ... */}
</EasyUIProvider>
)
}
<Button preset="primary">Save</Button>
<Button preset="cancel">Cancel</Button>
<Button preset="filter">Active only</Button>
If preset is not set, or set to a name that isn't configured, the component renders exactly as it would without
presets configured at all. In that case, the global wrapper still applies normally.
Preset shape
Each preset entry has up to three optional keys:
props: any prop the component accepts, exceptchildren,className,classNamesandpresetitself.className: extra classes for the component's root (base) element, just like the instanceclassNameprop.classNames: the same slots as the component'sclassNamesprop /wrappersentry.
{
presets: {
button: {
primary: {
props: { variant: 'solid', color: 'primary' },
className: 'font-semibold',
classNames: {
base: '...',
startContent: '...',
endContent: '...',
text: '...',
spinner: '...',
label: '...',
description: '...',
},
},
},
},
}
Precedence
Props
For each prop, the first of these that is defined wins:
- The prop set explicitly on the component instance.
- The preset's
props. - The component's default prop.
<EasyUIProvider config={{
presets: { button: { primary: { props: { variant: 'solid', color: 'primary' } } } },
}}>
{/* variant: solid, color: primary from the preset */}
<Button preset="primary">Save</Button>
{/* variant: outlined (instance wins), color: primary (from the preset) */}
<Button preset="primary" variant="outlined">Save</Button>
</EasyUIProvider>
Action buttons of Form and Modal
The submit and cancel buttons have their own default color and variant, which sit below a button preset
set through actions.submitProps / actions.cancelProps. For those two props only, the order from highest to
lowest priority is:
actions.submitProps/actions.cancelProps.- The
variant/colorof theFormorModal. - The
buttonpreset named byactions.submitProps.preset/actions.cancelProps.preset. - The default of the footer:
primary/solidfor submit,default/lightfor cancel.
classNames
When preset resolves to a configured entry, its className / classNames entirely replace
the global wrappers config for that instance, for every slot, including slots the preset
doesn't define. The global wrapper is not merged in at all. Instance className / classNames are following the same
class conflict rules as wrappers.
For the base slot specifically, the preset's className and classNames.base are combined the same way the
instance's className and classNames.base are:
- on a conflict,
classNamewins overclassNames.base.
The order, from lowest to highest priority, is:
- global
classNames.base< globalclassName< presetclassNames.base< presetclassName< instanceclassNames.base< instanceclassName.
<EasyUIProvider config={{
wrappers: { button: { base: 'bg-primary', spinner: 'animate-pulse' } },
presets: { button: { primary: { className: 'font-bold', classNames: { base: 'bg-accent' } } } },
}}>
{/* no preset: the global wrapper applies as usual */}
<Button>Save</Button>
{/* preset active: base uses both bg-accent and font-bold (from the preset, not bg-primary), and the
spinner slot gets no class at all since the global wrapper is ignored entirely */}
<Button preset="primary" loading>Save</Button>
{/* preset active with an instance override: base becomes bg-white (font-bold still applies), following
the "instance-wins-on-conflict" rule */}
<Button preset="primary" classNames={{ base: 'bg-white' }}>Save</Button>
</EasyUIProvider>
Extensibility
presets follows the same pattern as wrappers: as new components adopt the classNames slots pattern, they get
their own optional key (presets.input, presets.selector, ...), with props covering every prop of that component
except children, className, classNames and preset.