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.tsx
import {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 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",
},
],
});

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.tsx
import {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.tsx
import {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 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,
},
});

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.tsx
import {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 (
<MyInput
isClearable
placeholder="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.