tailwind-merge的基本使用
使用tailwind-merge自定义配置来告诉自己定义的类是属于哪一类的,然后进行合并。import {import {配置说明:什么需要配置,什么不需要配置✅ 需要配置的情况:1. @theme 中定义的非标准名称(如自定义的 font-size、shadow 等)例如:--font-size-menu-choice → 生成 text-menu-choice虽然会自动生成,但建议配置以确保
一.为什么要使用tailwind-merge?
主要是为了解决tailwindcss中样式冲突时不能很好的按照我们的css层叠想法去合并冲突类。
官方文档:https://tailwindcss.com/docs/styling-with-utility-classes#managing-style-conflicts
有一个例子是:
<div class="grid flex"> <!-- ... --></div>
请问上面的样式最后是应用哪个display的属性? 按照我们的经验,肯定觉得是flex,但是实际上是grid,原因是因为taiwindcss时根据样式表的顺序来应用的,而不是我们自己写的顺序
当你使用同样的一个样式的时候,跟我们以往的经验不同,在后面的样式不会重叠前面的。
另外一个例子就是
<div className="px-2 py-1 p-3 w-10 h-10 bg-amber-200"></div>
这样子,你觉得最终是多少呢?
✅ 最终结果:
由于 px-* 和 py-* 类在样式表中定义得比 p-* 类更晚,所以:
最终的 padding 效果是:
padding-top: 0.25rem; (4px) - 来自 py-1
padding-bottom: 0.25rem; (4px) - 来自 py-1
padding-left: 0.5rem; (8px) - 来自 px-2
padding-right: 0.5rem; (8px) - 来自 px-2
📝 为什么不是 p-3 的 12px 全方向?
因为在 Tailwind 的样式表中,方向性的 padding 类(px-*, py-*)定义在通用 padding 类(p-*)之后,所以它们会覆盖 p-3 的效果。
简单说:最终是上下 4px,左右 8px 的 padding!
相信大家肯定觉得很难受了,所以就引出了下一个工具:tailwind-merge
这个工具就能按照我们的想法来合并css,后面我也会写一个关于tailwind-merge的使用。
二.基本使用
1.安装
pnpm i tailwind-merge
2.使用:
import { twMerge } from 'tailwind-merge'
export default function TailwindMerge() {
// 在组件内部使用
const className = twMerge('px-2 py-1 bg-red hover:bg-dark-red', 'p-3 bg-[#B91C1C]')
console.log(className) // 每次组件渲染时执行
合并后的类名:hover:bg-dark-red p-3 bg-[#B91C1C]
return (
<div className={className}>
<h1>Tailwind Merge</h1>
<p>合并后的类名:{className}</p>
</div>
)
}
可以看到上面的使用还是很方便的
3.坑点
文档:https://github.com/dcastil/tailwind-merge/blob/v3.4.0/docs/configuration.md#usage-with-custom-tailwind-config
虽然这样子使用很方便,但是它有个坑点,就是之前我们也提到过,我们自己在tailwind里面会去自定义自己的样式,由于tailwind-merge不认识我们自定义的样式,然后会导致它在合并的时候会被保留。比如:
/*
1. @utility - 定义原子级工具类(Tailwind v4 新特性)
- 用途:创建可复用的单一用途工具类
- 特点:会被 Tailwind 的 JIT 引擎处理,支持响应式和伪类变体
- 可以使用:md:flex-center、hover:flex-center 等
*/
@utility flex-center {
display: flex;
align-items: center;
justify-content: center;
}
@utility flex-center-x {
display: flex;
justify-content: center;
}
@utility flex-center-y {
display: flex;
align-items: center;
}
<div className={twMerge("grid flex-center")}>aaa</div>
f12中css选择器该div上还有两个类名"grid flex-center" ,最后是'grid'布局,跟预期不符
4.如何解决自定义类的问题?
使用tailwind-merge自定义配置来告诉自己定义的类是属于哪一类的,然后进行合并。代码我放在下面了:
import { type ClassValue, clsx } from 'clsx';
import {
createTailwindMerge,
getDefaultConfig,
mergeConfigs,
} from 'tailwind-merge';
const defaultConfig = getDefaultConfig();
/* ============================================================================
配置说明:什么需要配置,什么不需要配置
============================================================================
✅ 需要配置的情况:
1. @theme 中定义的非标准名称(如自定义的 font-size、shadow 等)
例如:--font-size-menu-choice → 生成 text-menu-choice
虽然会自动生成,但建议配置以确保 tailwind-merge 能识别
2. @utility 定义的组合类(包含多个 CSS 属性)
例如:@utility text-s-title-s { font-size + font-weight + line-height }
必须配置!需要创建独立 classGroup + conflictingClassGroups
3. @utility 定义的自定义工具类(如 flex-center)
例如:@utility flex-center { display + align-items + justify-content }
需要配置到对应的 classGroup 中
❌ 不需要配置的情况:
1. @theme 中的标准颜色、间距(会自动生成标准 Tailwind 类)
例如:--color-brand → 生成 text-brand, bg-brand, border-brand
例如:--spacing-100 → 生成 p-100, m-100, w-100, h-100
tailwind-merge 默认就能识别这些标准格式!
2. @apply 定义的组件类(如 .avatar、.btn-primary)
这些不是工具类,tailwind-merge 会把它们当作普通字符串处理
不需要配置,也不建议在 tailwind-merge 中使用
============================================================================ */
// ===== 1. @theme 定义的自定义字体大小 =====
// 对应 CSS: --font-size-menu-choice, --font-size-title-l 等
// 生成的类: text-menu-choice, text-title-l 等
const customFontSizes = [
'menu-choice',
'menu-not-choice',
'title-l',
'title-m',
'title-s',
'number-l',
'number-m',
'number-s',
'body-l',
'body-m',
'body-s',
'tips',
];
// ===== 2. @utility 定义的文字样式组合类 =====
// 对应 CSS: @utility text-s-title-s { font-size + font-weight + line-height }
// 特点: 包含多个 CSS 属性,需要与多个 classGroup 冲突
// 必须配置: 创建独立组 + conflictingClassGroups
const textStyleUtilityClasses = [
's-title-s',
's-title-m',
's-title-l',
's-body-s',
's-body-m',
's-body-l',
];
export const twMergeConfig = mergeConfigs(defaultConfig, {
extend: {
classGroups: {
/* -------------------------------------------------------------------------
字体大小相关配置
------------------------------------------------------------------------- */
// ✅ 需要配置:@theme 中定义的自定义字体大小
// 对应 CSS: --font-size-menu-choice 等
// 生成的类: text-menu-choice, text-title-l 等
'font-size': [
{
text: customFontSizes,
},
],
/* -------------------------------------------------------------------------
组合类配置(包含多个 CSS 属性的 @utility 类)
------------------------------------------------------------------------- */
// ✅ 必须配置:@utility 定义的文字样式组合类
// 对应 CSS: @utility text-s-title-s { font-size + font-weight + line-height }
// 原因: 包含多个属性,需要与 font-size、font-weight、line-height 都冲突
'text-style-combo': [
{
text: textStyleUtilityClasses,
},
],
/* -------------------------------------------------------------------------
其他自定义工具类
------------------------------------------------------------------------- */
// ✅ 需要配置:@theme 中定义的自定义阴影
// 对应 CSS: --shadow-d-base, --shadow-d-dropdown 等
// 生成的类: shadow-d-base, shadow-d-dropdown 等
shadow: [
{
shadow: ['d-base', 'd-dropdown', 'd-button'],
},
],
// ✅ 需要配置:@utility 定义的布局工具类
// 对应 CSS: @utility flex-center { display + align-items + justify-content }
// 原因: 自定义类,需要加入 display 组才能与 flex、grid 等冲突
display: [
'flex-center',
'flex-center-x',
'flex-center-y',
],
/* -------------------------------------------------------------------------
❌ 不需要配置的内容(tailwind-merge 默认能处理)
------------------------------------------------------------------------- */
// ❌ 不需要配置:@theme 中的标准颜色
// 例如: --color-brand-color → 自动生成 text-brand-color, bg-brand-color 等
// tailwind-merge 默认能识别所有 text-*, bg-*, border-* 等标准格式
// ❌ 不需要配置:@theme 中的标准间距
// 例如: --spacing-100 → 自动生成 p-100, m-100, w-100, h-100 等
// tailwind-merge 默认能识别所有 p-*, m-*, w-*, h-* 等标准格式
// ❌ 不需要配置:@apply 定义的组件类
// 例如: .avatar, .btn-primary, .card
// 这些不是工具类,tailwind-merge 会当作普通字符串处理
// 而且不建议在 tailwind-merge 中使用(见 TAILWIND_MERGE_BEST_PRACTICES.md)
},
/* ---------------------------------------------------------------------------
冲突关系配置
---------------------------------------------------------------------------
说明:只有包含多个 CSS 属性的组合类需要配置冲突关系
例如:text-s-title-s 包含 font-size + font-weight + line-height
必须声明它与这三个 classGroup 都冲突
--------------------------------------------------------------------------- */
conflictingClassGroups: {
// text-style-combo 与三个样式类都冲突
'text-style-combo': ['font-size', 'font-weight', 'line-height'],
// 反向声明:让 tailwind-merge 知道双向冲突关系
'font-size': ['text-style-combo'],
'font-weight': ['text-style-combo'],
'line-height': ['text-style-combo'],
},
},
});
export const twMerge = createTailwindMerge(() => twMergeConfig);
/**
* 合并 Tailwind CSS 类名的工具函数
*
* 功能:
* - 自动合并冲突的 Tailwind 类(后面的覆盖前面的)
* - 支持条件类名(使用 clsx)
* - 支持自定义的 @utility 组合类
*
* @example
* // 基础合并:后面的覆盖前面的
* cn('px-2 py-1', 'p-3') // => 'p-3'
*
* @example
* // 条件类名
* cn('text-red-500', condition && 'text-blue-500')
* // => 'text-blue-500' (if condition is true)
*
* @example
* // 组合类之间的合并
* cn('text-s-title-s', 'text-s-title-l') // => 'text-s-title-l'
*
* @example
* // 组合类与单独属性类的合并
* cn('text-2xl font-bold', 'text-s-title-s')
* // => 'text-s-title-s' (删除了 text-2xl 和 font-bold)
*
* @example
* // 单独属性类覆盖组合类
* cn('text-s-title-s', 'font-bold')
* // => 'font-bold' (删除了 text-s-title-s)
*/
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
导入使用:
import { twMerge } from "tailwind-merge";
import { cn } from "@/utils/cn";
<div className={twMerge("grid flex-center")}>aaa</div>
<div className={cn("grid flex-center")}>bbb</div>
效果:
可以看到,因为配置了flex-center是属于display类别的,所以会和前面的grid进行合并。
5.注意点:
flex-center定义的类display: flex;align-items: center;justify-content: center;都是同一类别的,所以可以直接配置到tailwind-merge的display中。但是下面的text-s-title-s就是不同的类别。
/*
定义文字样式组合类 - 使用 @utility
这样定义的类支持响应式变体,如:md:text-s-title-s
*/
@utility text-s-title-s {
font-size: 16px;
font-weight: 500;
line-height: 16px;
}
@utility text-s-title-m {
font-size: 20px;
font-weight: 500;
line-height: 20px;
}
@utility text-s-title-l {
font-size: 24px;
font-weight: 600;
line-height: 24px;
}
所以我们在tailwind配置中是这样子配置的:
conflictingClassGroups: {
// text-style-combo 与三个样式类都冲突
'text-style-combo': ['font-size', 'font-weight', 'line-height'],
// 反向声明:让 tailwind-merge 知道双向冲突关系
'font-size': ['text-style-combo'],
'font-weight': ['text-style-combo'],
'line-height': ['text-style-combo'],
},
为什么要这样子配置:
原因就是tailwind是原子级别的,所以tailwind-merge默认你定义的类名也是,只会去替换你在配置中定义的类别。如果我们只定义这个
'font-size': [
{
text: [...customFontSizes ,...textStyleUtilityClasses],
},
],
那么text-s-title-s只会去覆盖前面的font-size,但是不会去覆盖font-weight和line-height
最好不要使用apply来自定义组合类(因为你不知道这个组合类应该放在配置项的哪一块。比如上面的flex-center是属于定位的,就是放在display这里),不然tailwind-merge识别不了,解释如文档。然后他会把识别不了的就不进行合并,应用到div上面去。
三.与clsx一起使用
1.clsx是什么?
动态类名拼接库。这个一般都是用classNames和clsx
a. 基本介绍
classNames: 最早的条件类名合并库(2015年),功能全面但体积稍大
clsx: 更轻量、更快的替代方案(2018年),与 classNames API 兼容,体积更小(~200B)
clsx 和 classNames 功能完全一致,clsx 更轻量更快
两者都只做条件拼接,不处理 Tailwind 类名冲突
tailwind-merge 专门解决 Tailwind 类名冲突问题
最佳组合: cn = twMerge + clsx(您的项目可以考虑这样做)
您当前的项目已经有 tailwind-merge,如果需要添加条件类名功能,建议安装 clsx 并创建 cn 工具函数!
2.为什么要使用动态类名拼接库?
// ❌ 传统方式 - 难以维护
function Button({ variant, size, disabled, loading, className }) {
let btnClass = 'btn';
if (variant === 'primary') btnClass += ' btn-primary';
if (variant === 'secondary') btnClass += ' btn-secondary';
if (size === 'sm') btnClass += ' btn-sm';
if (size === 'lg') btnClass += ' btn-lg';
if (disabled) btnClass += ' btn-disabled';
if (loading) btnClass += ' btn-loading';
if (className) btnClass += ' ' + className;
return <button className={btnClass}>Click</button>;
}
// ✅ 使用 clsx - 清晰直观
function Button({ variant, size, disabled, loading, className }) {
return (
<button className={clsx(
'btn',
{
'btn-primary': variant === 'primary',
'btn-secondary': variant === 'secondary',
'btn-sm': size === 'sm',
'btn-lg': size === 'lg',
'btn-disabled': disabled,
'btn-loading': loading
},
className // 外部传入的类名
)}>
Click
</button>
);
}
3.为什么要与clsx一起使用?
如见该文章
4.总结
clsx和classNames一样,主要是为了我们程序员好书写类名,但是不会去处理那些有冲突的样式, 但是tailwind-merge拿到clsx返回的类名,会把这些类名进行合并,然后给到div合并后的类名。
四.最终封装
import { type ClassValue, clsx } from 'clsx';
import {
createTailwindMerge,
getDefaultConfig,
mergeConfigs,
} from 'tailwind-merge';
const defaultConfig = getDefaultConfig();
/* ============================================================================
配置说明:什么需要配置,什么不需要配置
============================================================================
✅ 需要配置的情况:
1. @theme 中定义的非标准名称(如自定义的 font-size、shadow 等)
例如:--font-size-menu-choice → 生成 text-menu-choice
虽然会自动生成,但建议配置以确保 tailwind-merge 能识别
2. @utility 定义的组合类(包含多个 CSS 属性)
例如:@utility text-s-title-s { font-size + font-weight + line-height }
必须配置!需要创建独立 classGroup + conflictingClassGroups
3. @utility 定义的自定义工具类(如 flex-center)
例如:@utility flex-center { display + align-items + justify-content }
需要配置到对应的 classGroup 中
❌ 不需要配置的情况:
1. @theme 中的标准颜色、间距(会自动生成标准 Tailwind 类)
例如:--color-brand → 生成 text-brand, bg-brand, border-brand
例如:--spacing-100 → 生成 p-100, m-100, w-100, h-100
tailwind-merge 默认就能识别这些标准格式!
2. @apply 定义的组件类(如 .avatar、.btn-primary)
这些不是工具类,tailwind-merge 会把它们当作普通字符串处理
不需要配置,也不建议在 tailwind-merge 中使用
============================================================================ */
// ===== 1. @theme 定义的自定义字体大小 =====
// 对应 CSS: --font-size-menu-choice, --font-size-title-l 等
// 生成的类: text-menu-choice, text-title-l 等
const customFontSizes = [
'menu-choice',
'menu-not-choice',
'title-l',
'title-m',
'title-s',
'number-l',
'number-m',
'number-s',
'body-l',
'body-m',
'body-s',
'tips',
];
// ===== 2. @utility 定义的文字样式组合类 =====
// 对应 CSS: @utility text-s-title-s { font-size + font-weight + line-height }
// 特点: 包含多个 CSS 属性,需要与多个 classGroup 冲突
// 必须配置: 创建独立组 + conflictingClassGroups
const textStyleUtilityClasses = [
's-title-s',
's-title-m',
's-title-l',
's-body-s',
's-body-m',
's-body-l',
];
export const twMergeConfig = mergeConfigs(defaultConfig, {
extend: {
classGroups: {
/* -------------------------------------------------------------------------
字体大小相关配置
------------------------------------------------------------------------- */
// ✅ 需要配置:@theme 中定义的自定义字体大小
// 对应 CSS: --font-size-menu-choice 等
// 生成的类: text-menu-choice, text-title-l 等
'font-size': [
{
text: customFontSizes,
},
],
/* -------------------------------------------------------------------------
组合类配置(包含多个 CSS 属性的 @utility 类)
------------------------------------------------------------------------- */
// ✅ 必须配置:@utility 定义的文字样式组合类
// 对应 CSS: @utility text-s-title-s { font-size + font-weight + line-height }
// 原因: 包含多个属性,需要与 font-size、font-weight、line-height 都冲突
'text-style-combo': [
{
text: textStyleUtilityClasses,
},
],
/* -------------------------------------------------------------------------
其他自定义工具类
------------------------------------------------------------------------- */
// ✅ 需要配置:@theme 中定义的自定义阴影
// 对应 CSS: --shadow-d-base, --shadow-d-dropdown 等
// 生成的类: shadow-d-base, shadow-d-dropdown 等
shadow: [
{
shadow: ['d-base', 'd-dropdown', 'd-button'],
},
],
// ✅ 需要配置:@utility 定义的布局工具类
// 对应 CSS: @utility flex-center { display + align-items + justify-content }
// 原因: 自定义类,需要加入 display 组才能与 flex、grid 等冲突
display: [
'flex-center',
'flex-center-x',
'flex-center-y',
],
/* -------------------------------------------------------------------------
❌ 不需要配置的内容(tailwind-merge 默认能处理)
------------------------------------------------------------------------- */
// ❌ 不需要配置:@theme 中的标准颜色
// 例如: --color-brand-color → 自动生成 text-brand-color, bg-brand-color 等
// tailwind-merge 默认能识别所有 text-*, bg-*, border-* 等标准格式
// ❌ 不需要配置:@theme 中的标准间距
// 例如: --spacing-100 → 自动生成 p-100, m-100, w-100, h-100 等
// tailwind-merge 默认能识别所有 p-*, m-*, w-*, h-* 等标准格式
// ❌ 不需要配置:@apply 定义的组件类
// 例如: .avatar, .btn-primary, .card
// 这些不是工具类,tailwind-merge 会当作普通字符串处理
// 而且不建议在 tailwind-merge 中使用(见 TAILWIND_MERGE_BEST_PRACTICES.md)
},
/* ---------------------------------------------------------------------------
冲突关系配置
---------------------------------------------------------------------------
说明:只有包含多个 CSS 属性的组合类需要配置冲突关系
例如:text-s-title-s 包含 font-size + font-weight + line-height
必须声明它与这三个 classGroup 都冲突
--------------------------------------------------------------------------- */
conflictingClassGroups: {
// text-style-combo 与三个样式类都冲突
'text-style-combo': ['font-size', 'font-weight', 'line-height'],
// 反向声明:让 tailwind-merge 知道双向冲突关系
'font-size': ['text-style-combo'],
'font-weight': ['text-style-combo'],
'line-height': ['text-style-combo'],
},
},
});
export const twMerge = createTailwindMerge(() => twMergeConfig);
/**
* 合并 Tailwind CSS 类名的工具函数
*
* 功能:
* - 自动合并冲突的 Tailwind 类(后面的覆盖前面的)
* - 支持条件类名(使用 clsx)
* - 支持自定义的 @utility 组合类
*
* @example
* // 基础合并:后面的覆盖前面的
* cn('px-2 py-1', 'p-3') // => 'p-3'
*
* @example
* // 条件类名
* cn('text-red-500', condition && 'text-blue-500')
* // => 'text-blue-500' (if condition is true)
*
* @example
* // 组合类之间的合并
* cn('text-s-title-s', 'text-s-title-l') // => 'text-s-title-l'
*
* @example
* // 组合类与单独属性类的合并
* cn('text-2xl font-bold', 'text-s-title-s')
* // => 'text-s-title-s' (删除了 text-2xl 和 font-bold)
*
* @example
* // 单独属性类覆盖组合类
* cn('text-s-title-s', 'font-bold')
* // => 'font-bold' (删除了 text-s-title-s)
*/
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs));
}
这里的自定义配置这一块要根据自己的tailwind来。
导入使用:
import { cn } from "@/utils/cn";
<div className={cn("grid text-2xl font-bold",'text-s-title-s')}>bbb</div>
五.demo地址
更多推荐
所有评论(0)