Skip to content

Latest commit

 

History

History
462 lines (371 loc) · 22.4 KB

File metadata and controls

462 lines (371 loc) · 22.4 KB

API reference

Reference to all exports of tailwind-merge.

twMerge

function twMerge(
    ...classLists: Array<string | undefined | null | false | 0 | typeof classLists>
): string

Default function to use if you're using the default Tailwind config or are close enough to the default config. Check out basic usage for more info.

If twMerge doesn't work for you, you can create your own custom merge function with extendTailwindMerge.

twJoin

function twJoin(
    ...classLists: Array<string | undefined | null | false | 0 | typeof classLists>
): string

Function to join className strings conditionally without resolving conflicts.

twJoin(
    'border border-red-500',
    hasBackground && 'bg-red-100',
    hasLargeText && 'text-lg',
    hasLargeSpacing && ['p-2', hasLargeText ? 'leading-8' : 'leading-7'],
)

It is used internally within twMerge and a direct subset of clsx. If you use clsx or classnames to apply Tailwind classes conditionally and don't need support for object arguments, you can use twJoin instead, it is a little faster and will save you a few hundred bytes in bundle size.

Why no object support? Read here.

getDefaultConfig

function getDefaultConfig(): satisfies Config<DefaultClassGroupIds, DefaultThemeGroupIds>

Function which returns the default config used by tailwind-merge. The tailwind-merge config is different from the Tailwind config. It is optimized for small bundle size and fast runtime performance because it is expected to run in the browser.

fromTheme

function fromTheme<
    AdditionalThemeGroupIds extends string = never,
    DefaultThemeGroupIdsInner extends string = DefaultThemeGroupIds,
>(key: NoInfer<DefaultThemeGroupIdsInner | AdditionalThemeGroupIds>): ThemeGetter

Function to retrieve values from a theme scale, to be used in class groups.

fromTheme doesn't return the values from the theme scale, but rather another function which is used by tailwind-merge internally to retrieve the theme values. tailwind-merge can differentiate the theme getter function from a validator because it has a isThemeGetter property set to true.

When using TypeScript, the function only allows passing the default theme group IDs as the key argument. If you use custom theme group IDs, you need to pass them as the generic type argument AdditionalThemeGroupIds. In case you aren't using the default tailwind-merge config and use a different set of theme group IDs entirely, you can also pass them as the generic type argument DefaultThemeGroupIdsInner. If you want to allow any keys, you can call it as fromTheme<string>('anything-goes-here').

fromTheme can be used like this:

type AdditionalClassGroupIds = 'badge' | 'badge-color'
type AdditionalThemeGroupIds = 'custom-color'

extendTailwindMerge<AdditionalClassGroupIds, AdditionalThemeGroupIds>({
    extend: {
        theme: {
            'custom-color': ['primary', 'secondary'],
        },
        classGroups: {
            badge: [{ badge: [fromTheme('text')] }],
            'badge-color': [{ badge: [fromTheme<AdditionalThemeGroupIds>('custom-color')] }],
        },
    },
})

extendTailwindMerge

function extendTailwindMerge<
    AdditionalClassGroupIds extends string = never,
    AdditionalThemeGroupIds extends string = never,
>(
    configExtension: ConfigExtension<
        DefaultClassGroupIds | AdditionalClassGroupIds,
        DefaultThemeGroupIds | AdditionalThemeGroupIds
    >,
    ...createConfig: ((config: GenericConfig) => GenericConfig)[]
): TailwindMerge
function extendTailwindMerge<
    AdditionalClassGroupIds extends string = never,
    AdditionalThemeGroupIds extends string = never,
>(...createConfig: ((config: GenericConfig) => GenericConfig)[]): TailwindMerge

Function to create merge function with custom config which extends the default config. Use this if you use the default Tailwind config and just modified it in some places.

Note

The function extendTailwindMerge computes a large data structure based on the config passed to it. I recommend to call it only once and store the result in a top-level variable instead of calling it inline within another repeatedly called function.

You provide it a configExtension object which gets merged with the default config.

When using TypeScript and you use custom class group IDs or theme group IDs, you need to pass them as the generic type arguments AdditionalClassGroupIds and AdditionalThemeGroupIds. This is enforced to prevent accidental use of non-existing class group IDs accidentally. If you want to allow any custom keys without explicitly defining them, you can pass as string to both arguments.

type AdditionalClassGroupIds = 'aspect-w' | 'aspect-h' | 'aspect-reset'
type AdditionalThemeGroupIds = never

const twMerge = extendTailwindMerge<AdditionalClassGroupIds, AdditionalThemeGroupIds>({
    // ↓ Optional cache size
    //   Here we're disabling the cache
    cacheSize: 0,
    // ↓ Optional prefix from TaiLwind config
    prefix: 'tw',

    // ↓ Optional config overrides
    //   Only elements from the second level onwards are overridden
    override: {
        // ↓ Theme scales to override
        theme: {
            colors: ['black', 'white', 'yellow-500'],
        },
        // ↓ Class groups to override
        classGroups: {
            // ↓ The `shadow` key here is the class group ID
            //      ↓ Creates group of classes which have conflicting styles
            //        Classes here: shadow-100, shadow-200, shadow-300, shadow-400, shadow-500
            shadow: [{ shadow: ['100', '200', '300', '400', '500'] }],
        },
        // ↓ Conflicts across different groups to override
        conflictingClassGroups: {
            // ↓ ID of class group which creates a conflict with…
            //           ↓ …classes from groups with these IDs
            //   Here we remove the default conflict between the font-size and leading class
            //   groups.
            'font-size': [],
        },
        // ↓ Conflicts between the postfix modifier of a group and a different class group to
        //   override
        conflictingClassGroupModifiers: {
            // You probably won't need this, but it follows the same shape as
            // `conflictingClassGroups`.
        },
        // ↓ Modifiers whose order among multiple modifiers should be preserved because their
        //   order changes which element gets targeted. Overrides default value.
        orderSensitiveModifiers: ['before'],
    },

    // ↓ Optional config extensions
    //   Follows same shape as the `override` object.
    extend: {
        // ↓ Theme scales to extend or create
        theme: {
            spacing: ['sm', 'md', 'lg'],
        },
        // ↓ Class groups to extend or create
        classGroups: {
            // ↓ The `animate` key here is the class group ID
            //       ↓ Adds class animate-shimmer to existing group with ID `animate` or creates
            //         new class group if it doesn't exist.
            animate: ['animate-shimmer'],
            // ↓ Functions can also be used to match classes
            //   They take the class part value as argument and return a boolean defining whether
            //   it is a match.
            //   Here we accept all string classes starting with `aspec-w-` followed by a number.
            'aspect-w': [{ 'aspect-w': [(value) => Boolean(value) && !isNaN(value)] }],
            'aspect-h': [{ 'aspect-h': [(value) => Boolean(value) && !isNaN(value)] }],
            'aspect-reset': ['aspect-none'],
            // ↓ You can also use validators exported by tailwind-merge
            'prose-size': [{ prose: ['base', validators.isTshirtSize] }],
        },
        // ↓ Conflicts across different groups to extend or create
        conflictingClassGroups: {
            // ↓ ID of class group which creates a conflict with…
            //              ↓ …classes from groups with these IDs
            //   In this case `twMerge('aspect-w-5 aspect-none') → 'aspect-none'`
            'aspect-reset': ['aspect-w', 'aspect-h'],
        },
        // ↓ Conflicts between the postfix modifier of a group and a different class group to
        //   extend or create
        conflictingClassGroupModifiers: {
            // You probably won't need this, but it follows the same shape as
            // `conflictingClassGroups`.
        },
        // ↓ Modifiers whose order among multiple modifiers should be preserved because their
        //   order changes which element gets targeted. Extends default value.
        orderSensitiveModifiers: ['before'],
    },
})

Additionally, you can pass multiple createConfig functions (more to that in createTailwindMerge) which is convenient if you want to combine your config with third-party plugins.

const twMerge = extendTailwindMerge({}, withSomePlugin)

If you only use plugins, you can omit the configExtension object as well.

const twMerge = extendTailwindMerge(withSomePlugin)

createTailwindMerge

function createTailwindMerge(
    ...createConfig: [() => Config, ...Array<(config: Config) => Config>]
): TailwindMerge

Function to create merge function with custom config. Use this function instead of extendTailwindMerge if you don't need the default config or want more control over the config.

Note

The function createTailwindMerge computes a large data structure based on the config passed to it. I recommend to call it only once and store the result in a top-level variable instead of calling it inline within another repeatedly called function.

You need to provide a function which resolves to the config tailwind-merge should use for the new merge function. You can either extend from the default config or create a new one from scratch.

// ↓ Callback passed to `createTailwindMerge` is called when
//   `twMerge` gets called the first time.
const twMerge = createTailwindMerge(() => {
    const defaultConfig = getDefaultConfig()

    return {
        cacheSize: 0,
        classGroups: {
            ...defaultConfig.classGroups,
            badge: ['badge', 'badge-pill', { 'badge-dot': ['', 'sm', 'lg'] }],
            'icon-size': [{ icon: ['auto', (value) => Number(value) >= 16] }],
            card: ['card-sm', 'card-md', 'card-lg'],
        },
        conflictingClassGroups: {
            ...defaultConfig.conflictingClassGroups,
            badge: ['icon-size'],
        },
        conflictingClassGroupModifiers: {
            ...defaultConfig.conflictingClassGroupModifiers,
            card: ['icon-size'],
        },
        orderSensitiveModifiers: [...defaultConfig.orderSensitiveModifiers, 'before'],
    }
})

Same as in extendTailwindMerge you can use multiple createConfig functions which is convenient if you want to combine your config with third-party plugins. Just keep in mind that the first createConfig function does not get passed any arguments, whereas the subsequent functions get each passed the config from the previous function.

const twMerge = createTailwindMerge(getDefaultConfig, withSomePlugin, (config) => ({
    // ↓ Config returned by `withSomePlugin`
    ...config,
    classGroups: {
        ...config.classGroups,
        mySpecialClassGroup: [{ special: ['1', '2'] }],
    },
}))

But don't merge configs like that. Use mergeConfigs instead.

mergeConfigs

function mergeConfigs<ClassGroupIds extends string, ThemeGroupIds extends string = never>(
    baseConfig: GenericConfig,
    configExtension: ConfigExtension<ClassGroupIds, ThemeGroupIds>,
): GenericConfig

Helper function to merge multiple tailwind-merge configs. Properties with the value undefined are skipped.

When using TypeScript, you need to pass a union of all class group IDs and theme group IDs used in configExtension as generic arguments to mergeConfigs or pass string to both arguments to allow any IDs.

const twMerge = createTailwindMerge(getDefaultConfig, (config) =>
    mergeConfigs<'shadow' | 'animate' | 'prose'>(config, {
        override: {
            classGroups: {
                // ↓ Overriding existing class group
                shadow: [{ shadow: ['100', '200', '300', '400', '500'] }],
            },
        },
        extend: {
            classGroups: {
                // ↓ Adding value to existing class group
                animate: ['animate-shimmer'],
                // ↓ Adding new class group
                prose: [{ prose: ['', validators.isTshirtSize] }],
            },
        },
    }),
)

validators

interface Validators {
    isAny(value: string): boolean
    isAnyNonArbitrary(value: string): boolean
    isArbitraryFamilyName(value: string): boolean
    isArbitraryImage(value: string): boolean
    isArbitraryLength(value: string): boolean
    isArbitraryNumber(value: string): boolean
    isArbitraryPosition(value: string): boolean
    isArbitraryShadow(value: string): boolean
    isArbitrarySize(value: string): boolean
    isArbitraryValue(value: string): boolean
    isArbitraryVariable(value: string): boolean
    isArbitraryVariableFamilyName(value: string): boolean
    isArbitraryVariableImage(value: string): boolean
    isArbitraryVariableLength(value: string): boolean
    isArbitraryVariablePosition(value: string): boolean
    isArbitraryVariableShadow(value: string): boolean
    isArbitraryVariableSize(value: string): boolean
    isArbitraryVariableWeight(value: string): boolean
    isArbitraryWeight(value: string): boolean
    isFraction(value: string): boolean
    isInteger(value: string): boolean
    isNumber(value: string): boolean
    isPercent(value: string): boolean
    isTshirtSize(value: string): boolean
}

An object containing all the validators used in tailwind-merge. They are useful if you want to use a custom config with extendTailwindMerge or createTailwindMerge.

Example usage:

const paddingClassGroup = [{ p: [validators.isNumber] }]
const customImageGroup = [{ 'custom-img': [validators.isArbitraryImage] }]

Simple Type Validators

These validators check for basic patterns and types:

Validator Description Example Match
isAny Always returns true. Use carefully - matches everything. Best when certain no other class groups exist in a namespace. Matches any value
isAnyNonArbitrary Checks if class part is NOT an arbitrary value or variable red, lg, 4
isInteger Matches integer values 3, 100
isNumber Matches any number (integer or decimal) 3, 1.5, 0.25
isFraction Matches fraction patterns 1/2, 127/256
isPercent Matches percentage values 12.5%, 50%
isTshirtSize Matches T-shirt sizes, optionally with number prefix sm, xl, 2xl

Arbitrary Value Validators

These validators check arbitrary values (values in square brackets [...]):

Validator Description Example Match Common Use
isArbitraryValue Checks if value is enclosed in brackets [something] Generic arbitrary value detection
isArbitraryLength Checks for arbitrary length values [3%], [4px], [length:var(--my-var)] Width, height, spacing
isArbitraryNumber Checks for arbitrary numbers or number: labeled values [450], [number:var(--value)] Font-weight, z-index
isArbitraryWeight Checks for arbitrary font weight values or weight:/number: labeled values [400], [bold], [weight:var(--fw)] Font-weight
isArbitraryFamilyName Checks for family-name: labeled values [family-name:Open_Sans] Font-family
isArbitraryPosition Checks for position: labeled values [position:200px_100px] Background-position
isArbitrarySize Checks for size: labeled values [size:200px_100px] Background-size
isArbitraryImage Checks for image-like values (starts with image:, url:, linear-gradient(, etc.) [url('/proxy?url=https%3A%2F%2Fgithub.com%2Fpath.png')], [image:var(--img)] Background-image
isArbitraryShadow Checks for shadow patterns (two lengths separated by underscore, optionally with inset) [0_35px_60px_-15px_rgba(0,0,0,0.3)], [inset_0_4px_8px_rgba(0,0,0,0.1)] Box-shadow, text-shadow

Arbitrary Variable Validators

These validators check arbitrary CSS variables (values in parentheses (...)):

Validator Description Example Match Common Use
isArbitraryVariable Checks if value is a CSS variable in parentheses (--my-var) Generic CSS variable
isArbitraryVariableLength Checks for variables with length label (length:--my-length) Length properties
isArbitraryVariableSize Checks for variables with size, length, or percentage label (size:--my-size) Size properties
isArbitraryVariablePosition Checks for variables with position label (position:--my-position) Position properties
isArbitraryVariableImage Checks for variables with image or url label (image:--my-image) Image properties
isArbitraryVariableFamilyName Checks for variables with family-name label (family-name:--my-font) Font-family
isArbitraryVariableWeight Checks for variables with weight or number label, or no label (weight:--my-fw), (--my-weight) Font-weight
isArbitraryVariableShadow Checks for variables with shadow label or no label (shadow:--my-shadow), (--my-shadow) Shadow properties

Usage Examples

import { extendTailwindMerge, validators } from 'tailwind-merge'

const twMerge = extendTailwindMerge({
    extend: {
        classGroups: {
            // Custom size classes that accept numbers or fractions
            'custom-size': [{ size: [validators.isNumber, validators.isFraction] }],

            // Custom image classes
            'hero-image': [{ 'hero-img': [validators.isArbitraryImage] }],

            // Custom spacing with T-shirt sizes
            'custom-gap': [{ gap: [validators.isTshirtSize] }],

            // Accept any value (use with caution)
            'theme-color': [{ theme: [validators.isAny] }],
        },
    },
})

Config

interface Config<ClassGroupIds extends string, ThemeGroupIds extends string> {  }

TypeScript type for config object. Useful if you want to build a createConfig function but don't want to define it inline in extendTailwindMerge or createTailwindMerge.

DefaultClassGroupIds

type DefaultClassGroupIds = 'accent' | 'align-content' | 'align-items' | 

TypeScript type for all class group IDs defined in the default config of tailwind-merge.

DefaultThemeGroupIds

type DefaultThemeGroupIds = 'blur' | 'borderColor' | 'borderRadius' | 

TypeScript type for all theme group IDs defined in the default config of tailwind-merge.

ClassNameValue

type ClassNameValue = string | null | undefined | 0 | false | ClassNameValue[]

TypeScript type for arguments accepted by twMerge and twJoin. You might want to use it if you wrap any of those with your own function.

function myWrappedTwMerge(...args: ClassNameValue[]) {
    doSomething()
    return twMerge(...args)
}

ClassValidator

type ClassValidator = (value: string) => boolean

TypeScript type for class validators accepted in class definitions within extendTailwindMerge and createTailwindMerge.


Next: Writing plugins

Previous: Recipes

Back to overview