Custom Variants
NextUI allows you to create new variants for components that better suit your project's needs. This can be done by extending the component and its properties, and customizing the styles.
You can create or override the component variants
, defaultVariants
and compoundVariants
.
Note: For one-off customizations, refer to the Override Styles documentation.
Creating new variants for non-slots components
The Button component is a non-slots component, meaning that it does not have any slots that can be customized.
For this example, we will be using the Button
component's styles source code as a
reference. Go here to view the styles source code.
Note: If you are not familiar with the variants concept, please refer to the Tailwind Variants documentation.
Extend the original component variants
To create or override variants you need to use the extendVariants
function. This function
allows you to create a new component based on the original component, and customize its variants.
// MyButton.tsximport {extendVariants, Button} from "@nextui-org/react";export const MyButton = extendVariants(Button, { variants: { // <- modify/add variants color: { olive: "text-[#000] bg-[#84cc16]", orange: "bg-[#ff8c00] text-[#fff]", violet: "bg-[#8b5cf6] text-[#fff]", }, isDisabled: { true: "bg-[#eaeaea] text-[#000] opacity-50 cursor-not-allowed", }, size: { xs: "px-2 min-w-12 h-6 text-tiny gap-1 rounded-small", md: "px-4 min-w-20 h-10 text-small gap-2 rounded-small", xl: "px-8 min-w-28 h-14 text-large gap-4 rounded-medium", }, }, defaultVariants: { // <- modify/add default variants color: "olive", size: "xl", }, compoundVariants: [ // <- modify/add compound variants { isDisabled: true, color: "olive", class: "bg-[#84cc16]/80 opacity-100", }, ],});
variants: {// <- modify/add variantscolor: {olive: "text-[#000] bg-[#84cc16]",orange: "bg-[#ff8c00] text-[#fff]",violet: "bg-[#8b5cf6] text-[#fff]",},isDisabled: {true: "bg-[#eaeaea] text-[#000] opacity-50 cursor-not-allowed",},size: {xs: "px-2 min-w-12 h-6 text-tiny gap-1 rounded-small",md: "px-4 min-w-20 h-10 text-small gap-2 rounded-small",xl: "px-8 min-w-28 h-14 text-large gap-4 rounded-medium",},},defaultVariants: { // <- modify/add default variantscolor: "olive",size: "xl",},compoundVariants: [ // <- modify/add compound variants{isDisabled: true,color: "olive",class: "bg-[#84cc16]/80 opacity-100",},],});
Use your custom component in your application
Then, you can now use your custom component in your application. Here, MyButton
is
used with the color set to olive
and the size set to xl
.
// App.tsximport {MyButton} from "./MyButton";const MyApp = () => { return ( <MyButton color="olive" size="md"> Press Me </MyButton> );};
return (<MyButton color="olive" size="md">Press Me</MyButton>);};
The new component will include the original props of the Button
component, plus the new
variants that you have created.
Creating new variants for slots components
It is also possible to use the extendVariants
function to add or override variants for
components that have slots.
The Input component is a slots component, meaning that it has slots that can be customized.
For this example, we will be using the Input
component's styles source code as a
reference. Go here to view the styles source code.
Note: If you are not familiar with the variants/slots concept, please refer to the Tailwind Variants documentation.
Extend the original component variants
To create or override variants you need to use the extendVariants
function. This function
allows you to create a new component based on the original component, and customize its variants.
// MyInput.tsximport {extendVariants, Input} from "@nextui-org/react";const MyInput = extendVariants(Input, { variants: { // <- modify/add variants color: { stone: { // <- add a new color variant inputWrapper: [ // <- Input wrapper slot "bg-zinc-100", "border", "shadow", "transition-colors", "focus-within:bg-zinc-100", "data-[hover=true]:border-zinc-600", "data-[hover=true]:bg-zinc-100", "group-data-[focus=true]:border-zinc-600", // dark theme "dark:bg-zinc-900", "dark:border-zinc-800", "dark:data-[hover=true]:bg-zinc-900", "dark:focus-within:bg-zinc-900", ], input: [ // <- Input element slot "text-zinc-800", "placeholder:text-zinc-600", // dark theme "dark:text-zinc-400", "dark:placeholder:text-zinc-600", ], }, }, size: { xs: { inputWrapper: "h-6 min-h-6 px-1", input: "text-tiny", }, md: { inputWrapper: "h-10 min-h-10", input: "text-small", }, xl: { inputWrapper: "h-14 min-h-14", input: "text-medium", }, }, radius: { xs: { inputWrapper: "rounded", }, sm: { inputWrapper: "rounded-[4px]", }, }, textSize: { base: { input: "text-base", }, }, removeLabel: { true: { label: "hidden", }, false: {}, }, }, defaultVariants: { color: "stone", textSize: "base", removeLabel: true, },});
variants: { // <- modify/add variantscolor: {stone: { // <- add a new color variantinputWrapper: [ // <- Input wrapper slot"bg-zinc-100","border","shadow","transition-colors","focus-within:bg-zinc-100","data-[hover=true]:border-zinc-600","data-[hover=true]:bg-zinc-100","group-data-[focus=true]:border-zinc-600",// dark theme"dark:bg-zinc-900","dark:border-zinc-800","dark:data-[hover=true]:bg-zinc-900","dark:focus-within:bg-zinc-900",],input: [ // <- Input element slot"text-zinc-800","placeholder:text-zinc-600",// dark theme"dark:text-zinc-400","dark:placeholder:text-zinc-600",],},},size: {xs: {inputWrapper: "h-6 min-h-6 px-1",input: "text-tiny",},md: {inputWrapper: "h-10 min-h-10",input: "text-small",},xl: {inputWrapper: "h-14 min-h-14",input: "text-medium",},},radius: {xs: {inputWrapper: "rounded",},sm: {inputWrapper: "rounded-[4px]",},},textSize: {base: {input: "text-base",},},removeLabel: {true: {label: "hidden",},false: {},},},defaultVariants: {color: "stone",textSize: "base",removeLabel: true,},});
Use your custom component in your application
Then, you can now use your custom component in your application. Here, MyInput
is
used with the color set to slate
and the size set to xl
.
// App.tsximport {MyInput} from "./MyInput";import {SearchIcon} from "your-icons-library";const MyApp = () => { return ( <MyInput isClearable placeholder="Search..." radius="md" size="md" startContent={<SearchIcon className="text-zinc-500" size={16} />} /> );};
return (<MyInputisClearableplaceholder="Search..."radius="md"size="md"startContent={<SearchIcon className="text-zinc-500" size={16} />}/>);};
The new component will include the original props of the Input component, plus the new variants that you have created.
All NextUI components have the
Styles source
link on top of the page. This link will take you to the styles source code of the component. You can use this as a reference when creating your own custom component.
Types
Main Function
const Component = extendVariants(BaseComponent, options, config);/*** BaseComponent -> NextUI component to extend* options -> the variants to add/modify* config -> config to extend the component*/
Options
type ExtendVariantsOptions = {variants?: Record<string, Record<string, ClassValue>>;defaultVariants?: Record<string, ClassValue>;compoundVariants?: Array<Record<string, string> & ClassProp>;};
Config
/*** Whether to merge the class names with `tailwind-merge` library.* It's avoid to have duplicate tailwind classes. (Recommended)* @see https://github.com/dcastil/tailwind-merge/blob/v1.8.1/README.md* @default true*/twMerge?: boolean;/*** The config object for `tailwind-merge` library.* @see https://github.com/dcastil/tailwind-merge/blob/v1.8.1/docs/configuration.md*/twMergeConfig?: TWMergeConfig;
Note: See the Tailwind Merge Config to learn more about it.