tailwind-merge

repository·main·Indexed 26 days ago

https://github.com/dcastil/tailwind-merge

A utility for merging Tailwind CSS classes in JavaScript without style conflicts. It provides the twMerge function to resolve overlapping utility classes and twJoin for conditional class joining. The library includes tools for custom configurations via extendTailwindMerge and createTailwindMerge, as well as a suite of validators for identifying Tailwind class patterns.

Tokens
18.1K
Snippets
55
Records
82
Agent score
89%

What's inside tailwind-merge

  1. Evaluate trade-offs of using `tailwind-merge`

    main

    Before integrating tailwind-merge, consider these trade-offs:

    Disadvantages

    • Bundle Size: The package includes a large config (~5 kB out of ~7 kB minified/gzipped) to handle conflict resolution.
    • Loss of Control: It gives component users significant freedom to override styles, which can make maintaining public or large-scale components harder.
    • Refactoring Difficulty: Allowing arbitrary className overrides can break consumer styles if you refactor the component's internal classes.
    • Requirement: It is specifically designed for Tailwind CSS and component composition.

    Advantages

    • Deep Composition: Ideal for design systems where styles are modified through multiple layers (e.g., BaseOptionMenuOptionContextMenuOption).
    • Development Velocity: Allows supporting wide styling use cases (like custom widths or colors) without explicitly defining every possible prop.
    • Preventing Premature Abstractions: Allows you to defer creating complex props (like variant='destructive') by simply allowing a className override for one-off cases.
  2. Understand tailwind-merge performance and caching

    main

    tailwind-merge is optimized for browser performance:

    • LRU Cache: Results are cached by default using a lightweight Least Recently Used (LRU) cache (up to 500 results). The cache is applied after arguments are joined into a single string.
    • Lazy Initialization: Computations are deferred until the first call to twMerge to improve app startup time.
    • Data Reuse: Expensive computations are performed upfront to keep subsequent calls fast.
  3. Install the latest development release

    main

    A non-production-ready version of every commit on the main branch is released under the dev tag for testing. These versions follow the format [version]-dev.[git-sha]. You can install the latest development build using npm.

    npm install tailwind-merge@dev
  4. Compose `validators.isInteger` with arbitrary value checks

    main

    In v2, validators.isInteger does not check for arbitrary values. If your class groups require matching both standard integers and arbitrary values (like px-[10]), you must manually compose the validator.

    Since tailwind-merge does not export isArbitraryInteger, you can use a regex or compose it with isArbitraryValue if the context is unambiguous.

      import { validators } from 'tailwind-merge'
    
    + function isIntegerOrArbitraryInteger(value: string) {
    +     return validators.isInteger(value) || /^\[(number:.+|-?\d+)\]$/.test(value)
    + }
    
    - validators.isInteger
    + isIntegerOrArbitraryInteger
  5. Configure Postfix Lookup Class Groups

    main

    By default, tailwind-merge treats the part after a slash (/) as a postfix modifier. If a slash is actually part of a full class name (e.g., @container-size/sidebar), add that class group ID to postfixLookupClassGroups. This tells the library to attempt resolving the full class name after resolving the part before the slash.

    const postfixLookupClassGroups = ['container-type']
  6. Remove custom separators from extendTailwindMerge and createTailwindMerge

    main

    Tailwind CSS v4 only supports the default : separator for modifiers. Consequently, the separator key is no longer supported in extendTailwindMerge or createTailwindMerge configurations. Remove the separator key to avoid TypeScript errors.

      import { extendTailwindMerge } from 'tailwind-merge'
    
      const twMerge = extendTailwindMerge({
    -     separator: '_',
          …
      })
  7. Merge component defaults with `className` prop using `twMerge`

    main

    The primary use case for twMerge is to merge a component's default internal classes with an incoming className prop. This allows consumers of your component to override specific styles (like colors or padding) without creating conflicts.

    twMerge results are cached, so calling it during re-renders with the same props is computationally lightweight. If you use a custom Tailwind CSS configuration, ensure you configure tailwind-merge to match your setup.

    import { twMerge } from 'tailwind-merge'
    
    function MyComponent({ forceHover, disabled, isMuted, className }) {
        return (
            <div
                className={twMerge(
                    TYPOGRAPHY_STYLES_LABEL_SMALL,
                    'grid w-max gap-2',
                    forceHover ? 'bg-gray-200' : ['bg-white', !disabled && 'hover:bg-gray-200'],
                    isMuted && 'text-gray-600',
                    className,
                )}
            >
                {/* More code… */}
            </div>
        )
    }
  8. Migrate from tailwind-merge v2 to v3

    main
    When upgrading to tailwind-merge v3, you must also upgrade to Tailwind CSS v4. tailwind-merge v3 drops support for Tailwind CSS v3. The primary breaking changes are driven by the transition to Tailwind CSS v4 support, specifically regarding how twMerge handles classes and how theme scales are mapped to match the Tailwind CSS v4 theme variable namespace.
  9. Migrate from tailwind-merge v2 to v3 (Tailwind CSS v4 compatibility)

    main
    tailwind-merge v3.0.0 is designed to be used with Tailwind CSS v4. If you are staying on Tailwind CSS v3, do not upgrade to tailwind-merge v3.0.0, as there are breaking changes in the expected class syntax. The upgrade involves changes to theme scale support, configuration keys, and mandatory fields.
  10. Resolve conflicts with modifiers and stacked modifiers

    main

    tailwind-merge supports standard Tailwind modifiers (like hover:, focus:) and stacked modifiers (like hover:focus:). It understands when the order of modifiers matters and resolves conflicts accordingly.

    twMerge('p-2 hover:p-4') // → 'p-2 hover:p-4'
    twMerge('hover:p-2 hover:p-4') // → 'hover:p-4'
    twMerge('hover:focus:p-2 focus:hover:p-4') // → 'focus:hover:p-4'