Date: Sun, 21 May 2023 16:55:14 +0200
Subject: [PATCH 09/19] add empty "when and how to use it" page
---
docs/README.md | 1 +
docs/what-is-it-for.md | 2 +-
docs/when-and-how-to-use-it.md | 19 +++++++++++++++++++
3 files changed, 21 insertions(+), 1 deletion(-)
create mode 100644 docs/when-and-how-to-use-it.md
diff --git a/docs/README.md b/docs/README.md
index ccb23789..fd9ca9ee 100644
--- a/docs/README.md
+++ b/docs/README.md
@@ -24,6 +24,7 @@ twMerge('px-2 py-1 bg-red hover:bg-dark-red', 'p-3 bg-[#B91C1C]')
## Get started
- [What is it for](./what-is-it-for.md)
+- [When and how to use it](./when-and-how-to-use-it.md)
- [Features](./features.md)
- [Configuration](./configuration.md)
- [Recipes](./recipes.md)
diff --git a/docs/what-is-it-for.md b/docs/what-is-it-for.md
index 904a6be8..bb018dd7 100644
--- a/docs/what-is-it-for.md
+++ b/docs/what-is-it-for.md
@@ -36,6 +36,6 @@ tailwind-merge overrides conflicting classes and keeps everything else untouched
---
-Next: [Features](./features.md)
+Next: [When and how to use it](./when-and-how-to-use-it.md)
[Back to overview](./README.md)
diff --git a/docs/when-and-how-to-use-it.md b/docs/when-and-how-to-use-it.md
new file mode 100644
index 00000000..e7d81858
--- /dev/null
+++ b/docs/when-and-how-to-use-it.md
@@ -0,0 +1,19 @@
+# When and how to use it
+
+
+
+## When to use it
+
+
+
+## How to use it
+
+
+
+---
+
+Next: [Features](./features.md)
+
+Previous: [What is it for](./what-is-it-for.md)
+
+[Back to overview](./README.md)
From 8d5f9a0da7d8f4b4e2cf7d8da5e9a7a1d7d7405a Mon Sep 17 00:00:00 2001
From: Dany Castillo <31006608+dcastil@users.noreply.github.com>
Date: Sun, 21 May 2023 20:36:13 +0200
Subject: [PATCH 10/19] add content for "when to use it" and "when not to use
it"
---
docs/when-and-how-to-use-it.md | 37 ++++++++++++++++++++++++++++++++--
1 file changed, 35 insertions(+), 2 deletions(-)
diff --git a/docs/when-and-how-to-use-it.md b/docs/when-and-how-to-use-it.md
index e7d81858..ccf45da3 100644
--- a/docs/when-and-how-to-use-it.md
+++ b/docs/when-and-how-to-use-it.md
@@ -1,15 +1,48 @@
# When and how to use it
-
+Like any other package, tailwind-merge comes with opportunities and trade-offs. This document tries to help you decide if tailwind-merge is the right tool for your use case based on my own experience and the feedback I got from the community.
+
+> **Note**
+> If you're thinking of a major argument that is not covered here, please [let me know](https://github.com/dcastil/tailwind-merge/discussions/new?category=ideas)!
## When to use it
-
+### Using Tailwind CSS and component composition
+
+I hope this is self-explanatory, but tailwind-merge is probably only useful if you use Tailwind CSS and compose components together in some form. If you have a use case for tailwind-merge outside of thosse boundaries, please [let me know](https://github.com/dcastil/tailwind-merge/discussions/new?category=show-and-tell), I'm curious about it!
+
+### Using highly reusable components
+
+tailwind-merge is a great fit for highly reusable components like in design systems or UI component libraries. If you expect that styles of a component will be modified on multiple levels, e.g. ContextMenuOption -> MenuOption -> BaseOption, with each component passing some modifications to the component it renders, tailwind-merge can help you to keep the API surface between components small.
+
+### Fast development velocity and iteration speed wanted
+
+tailwind-merge allows you to support a wide range of styling use cases without having to explicitly define each of them separately within a component. E.g. you can pass a custom width to a button component, change its text color or position it absolutely with a single `className` prop without the need to define support for custom widths, text colors or positioning within the button component explicitly.
+
+### Preventing premature abstractions
+
+Let's say you have a Button component that you already use in many places. You have a place in your app in which you want to make its background red to signal that the action of the button is destructive. You could modify the Button component to deal with the concept of destructiveness, but then you'd need to make sure that those styles work with all the other permutations of the component which you don't need in the place where the destructive button is used. And maybe you're not even sure whether you'll keep the Button red in this one place, so the time investment of making the Button understand destructiveness doesn't seem worth it.
+
+tailwind-merge allows you to defer the creation of abstractions like destructiveness to the point where you're sure that you need them. You can just pass a `className` prop to the Button component in which you define the red background and be done with it for now. If you later decide that you want to make the Button red in more places, you can still define the logic inside the Button component later.
+
+## When not to use it
+
+### Bundle size constraints
+
+tailwind-merge relies on a large config (~5 kB out of the ~7 kB minified and gzipped bundle size) to understand which classes are conflicting. This might be limiting if you have tight bundle size constraints.
+
+### API surface constraints
+
+With large teams or components that are made available publicly, API surface of components becomes an issue as it will be used and misused in any way the API allows. With this in mind tailwind-merge might give too much freedom to users of a component which could make it harder to maintain and evolve the component over time. If you need full control over styling in your components, tailwind-merge is probably not be the right tool for you.
## How to use it
+## Alternatives
+
+
+
---
Next: [Features](./features.md)
From 4a839ddabbe2a5f3c2bfeedc517bbc098f08f35d Mon Sep 17 00:00:00 2001
From: Dany Castillo <31006608+dcastil@users.noreply.github.com>
Date: Sat, 27 May 2023 14:22:03 +0200
Subject: [PATCH 11/19] add "start discussion" template to issue template
config
---
.github/ISSUE_TEMPLATE/config.yml | 3 +++
1 file changed, 3 insertions(+)
diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml
index 9b9aa179..74a16528 100644
--- a/.github/ISSUE_TEMPLATE/config.yml
+++ b/.github/ISSUE_TEMPLATE/config.yml
@@ -9,3 +9,6 @@ contact_links:
- name: Bug Report
url: https://github.com/dcastil/tailwind-merge/issues/new
about: If something is broken with tailwind-merge itself, create a bug report.
+ - name: Start discussion
+ url: https://github.com/dcastil/tailwind-merge/discussions
+ about: Anything else on your mind? Check out the discussions forum.
From 5ad44516ecb09348b07e22eb5493671a28f7a29d Mon Sep 17 00:00:00 2001
From: Dany Castillo <31006608+dcastil@users.noreply.github.com>
Date: Wed, 31 May 2023 22:44:25 +0200
Subject: [PATCH 12/19] add note about object support to `twJoin` API reference
docs
---
docs/api-reference.md | 2 ++
1 file changed, 2 insertions(+)
diff --git a/docs/api-reference.md b/docs/api-reference.md
index 4c77b6aa..c0d9eb5a 100644
--- a/docs/api-reference.md
+++ b/docs/api-reference.md
@@ -35,6 +35,8 @@ twJoin(
It is used internally within `twMerge` and a direct subset of [`clsx`](https://www.npmjs.com/package/clsx). If you use `clsx` or [`classnames`](https://www.npmjs.com/package/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](https://github.com/dcastil/tailwind-merge/discussions/137#discussioncomment-3481605).
+
## `getDefaultConfig`
```ts
From 5901e85306371b486f914e1dcc8728439e63407b Mon Sep 17 00:00:00 2001
From: Dany Castillo <31006608+dcastil@users.noreply.github.com>
Date: Wed, 31 May 2023 23:04:22 +0200
Subject: [PATCH 13/19] write docs about when and how to use tailwind-merge
---
docs/when-and-how-to-use-it.md | 97 ++++++++++++++++++++++++++++------
1 file changed, 82 insertions(+), 15 deletions(-)
diff --git a/docs/when-and-how-to-use-it.md b/docs/when-and-how-to-use-it.md
index ccf45da3..9ba56988 100644
--- a/docs/when-and-how-to-use-it.md
+++ b/docs/when-and-how-to-use-it.md
@@ -1,21 +1,39 @@
# When and how to use it
-Like any other package, tailwind-merge comes with opportunities and trade-offs. This document tries to help you decide if tailwind-merge is the right tool for your use case based on my own experience and the feedback I got from the community.
+Like any other package, tailwind-merge comes with opportunities and trade-offs. This document tries to help you decide whether tailwind-merge is the right tool for your use case based on my own experience and the feedback I got from the community.
> **Note**
> If you're thinking of a major argument that is not covered here, please [let me know](https://github.com/dcastil/tailwind-merge/discussions/new?category=ideas)!
-## When to use it
+## Why not to use it
-### Using Tailwind CSS and component composition
+Generally speaking, there are situations where you _could_ use tailwind-merge but probably shouldn't. Think of tailwind-merge as an escape hatch rather than the primary tool to handle style variants.[^simonswiss-quote]
-I hope this is self-explanatory, but tailwind-merge is probably only useful if you use Tailwind CSS and compose components together in some form. If you have a use case for tailwind-merge outside of thosse boundaries, please [let me know](https://github.com/dcastil/tailwind-merge/discussions/new?category=show-and-tell), I'm curious about it!
+[^simonswiss-quote]: Don't just take my word for it, [Simon Vrachliotis thinks so too](https://twitter.com/simonswiss/status/1663721037949984768).
-### Using highly reusable components
+### Increases bundle size
-tailwind-merge is a great fit for highly reusable components like in design systems or UI component libraries. If you expect that styles of a component will be modified on multiple levels, e.g. ContextMenuOption -> MenuOption -> BaseOption, with each component passing some modifications to the component it renders, tailwind-merge can help you to keep the API surface between components small.
+tailwind-merge relies on a large config (~5 kB out of the ~7 kB minified and gzipped bundle size) to understand which classes are conflicting. This might be limiting if you have tight bundle size constraints.
+
+### Might give too much freedom to users of a component
+
+With large teams or components that are made available publicly you can expect users of components to use and misuse the component's API in any way the component allows. With this in mind tailwind-merge might give too much freedom to users of a component which could make it harder to maintain and evolve the component over time. With tailwind-merge you give up full control over styling in your components.
+
+### More difficult to refactor highly reusable components
+
+When you allow arbitrary classes to be passed into a component, you can break the styles of the component's users when you refactor the component's internal styles. If you need to be able to refactor a component's styles often, those styles shouldn't be merged with styles from props unless you're willing to refactor the component's uses as well.
+
+### Not using Tailwind CSS or component composition
-### Fast development velocity and iteration speed wanted
+tailwind-merge is probably only useful if you use Tailwind CSS and compose components together in some form. If you have a use case for tailwind-merge outside of thosse boundaries, please [let me know](https://github.com/dcastil/tailwind-merge/discussions/new?category=show-and-tell), I'm curious about it!
+
+## Why to use it
+
+### Easy to compose components through multiple levels
+
+tailwind-merge is a great fit for highly composed components like in design systems or UI component libraries. If you expect that styles of a component will be modified on multiple levels, e.g. ContextMenuOption -> MenuOption -> BaseOption, with each component passing some modifications to the component it renders, tailwind-merge can help you to keep the API surface between components small.
+
+### Enables fast development velocity and iteration speed
tailwind-merge allows you to support a wide range of styling use cases without having to explicitly define each of them separately within a component. E.g. you can pass a custom width to a button component, change its text color or position it absolutely with a single `className` prop without the need to define support for custom widths, text colors or positioning within the button component explicitly.
@@ -25,23 +43,72 @@ Let's say you have a Button component that you already use in many places. You h
tailwind-merge allows you to defer the creation of abstractions like destructiveness to the point where you're sure that you need them. You can just pass a `className` prop to the Button component in which you define the red background and be done with it for now. If you later decide that you want to make the Button red in more places, you can still define the logic inside the Button component later.
-## When not to use it
+## How to use it
-### Bundle size constraints
+### Joining internal classes
-tailwind-merge relies on a large config (~5 kB out of the ~7 kB minified and gzipped bundle size) to understand which classes are conflicting. This might be limiting if you have tight bundle size constraints.
+If you want to merge classes that are all defined within a component, prefer using the [`twJoin`](./api-reference.md#twjoin) function over `twMerge`. As the name suggests, `twJoin` only joins the class strings together and doesn't deal with conflicting classes.
-### API surface constraints
+```jsx
+// React components with JSX syntax used in this example
-With large teams or components that are made available publicly, API surface of components becomes an issue as it will be used and misused in any way the API allows. With this in mind tailwind-merge might give too much freedom to users of a component which could make it harder to maintain and evolve the component over time. If you need full control over styling in your components, tailwind-merge is probably not be the right tool for you.
+function MyComponent({ forceHover, disbaled, isMuted }) {
+ return (
+
+ {/* More code… */}
+
+ )
+}
+```
-## How to use it
+Joining classes instead of merging forces you to write your code in a way so that no merge conflicts appear which seems like more work at first. But it has two big advantages:
+
+1. It's much more performant because no conflict resolution is computed. `twJoin` has the same performance characteristics as other class joining libraries like [`clsx`](https://www.npmjs.com/package/clsx).
+
+2. It's usually easier to reason about. When you can't override classes, you naturally start to put classes that are in conflict with each other closer together through conditionals like ternaries. Also when a condition within the `twJoin` call is truthy, you can be sure that this class will be applied without the need to check whether conflicting classes appear in a later argument. Not relying on overrides makes it easier to understand which classes are in conflict with each other and which classes are applied in which cases.
+
+But there are also exceptions to (2) in which using `twMerge` for purely internally defined classes is preferable, especially in some complicated cases. So just take this as a rule of thumb.
-
+### Merging internal classes with `className` prop
+
+The primary purpose of tailwind-merge is to merge a `className` prop with the default classes of a component.
+
+```jsx
+// React components with JSX syntax used in this example
+
+function MyComponent({ forceHover, disbaled, isMuted, className }) {
+ return (
+
+ {/* More code… */}
+
+ )
+}
+```
+
+You don't need to worry about potentially expensive re-renders here because tailwind-merge [caches results](./features.md#results-are-cached) so that a re-render with the same props and state becomes computationally lightweight as far as the call to `twMerge` goes.
## Alternatives
-
+TODO:
+
+- Write about important modifier
+- Adding prop dealing with one-off style to component
+- Adding more granular className props like `paddingClassNames` to component
---
From 52ce30e69becaf011d907e9dd911af3b8398dea6 Mon Sep 17 00:00:00 2001
From: Dany Castillo <31006608+dcastil@users.noreply.github.com>
Date: Wed, 31 May 2023 23:50:58 +0200
Subject: [PATCH 14/19] add alternatives to using tailwind-merge to docs
---
docs/when-and-how-to-use-it.md | 56 +++++++++++++++++++++++++++++++---
1 file changed, 52 insertions(+), 4 deletions(-)
diff --git a/docs/when-and-how-to-use-it.md b/docs/when-and-how-to-use-it.md
index 9ba56988..94a35648 100644
--- a/docs/when-and-how-to-use-it.md
+++ b/docs/when-and-how-to-use-it.md
@@ -104,11 +104,55 @@ You don't need to worry about potentially expensive re-renders here because tail
## Alternatives
-TODO:
+In case the disadvantages of tailwind-merge weigh in too much for your use case, here are some alternatives that might be a better fit.
-- Write about important modifier
-- Adding prop dealing with one-off style to component
-- Adding more granular className props like `paddingClassNames` to component
+### Adding props that toggle internal styles
+
+This is the goold-old way of styling components and is also probably your default. E.g. think of a variant prop that toggles between a primary and a secondary styles of a button. The `variant` prop is already toggling between internal styles of the component. If you have a one-off use case to give the button a full width, you can add a `isFullWidth` prop to the button component which toggles the `w-full` class internally.
+
+```jsx
+// React components with JSX syntax used in this example
+
+function Button({ variant = 'primary', isFullWidth, ...props }) {
+ return
+}
+
+const BUTTON_VARIANTS = {
+ primary: 'bg-blue-500 text-white',
+ secondary: 'bg-gray-200 text-black',
+}
+
+function join(...args) {
+ return args.filter(Boolean).join(' ')
+}
+```
+
+### Using Tailwind's important modifier
+
+If you have too many different one-off use cases to add a prop for each of them to a component, you can use Tailwind's [important modifier](https://tailwindcss.com/docs/configuration#important-modifier) to override internal styles.
+
+```jsx
+// React components with JSX syntax used in this example
+
+function MyComponent() {
+ return (
+ <>
+
+
+ >
+ )
+}
+
+function Button({ className ...props }) {
+ return
+}
+
+function join(...args) {
+ return args.filter(Boolean).join(' ')
+}
+```
+
+The main downsides of this approach are that it only works one level deep (you can't override the `!bg-red-500` class in the example above) and that it's not obvious from the component's API which classes can be overridden.
---
@@ -117,3 +161,7 @@ Next: [Features](./features.md)
Previous: [What is it for](./what-is-it-for.md)
[Back to overview](./README.md)
+
+```
+
+```
From fa49d6bf92b32d46457d3deb3d8e31a026501980 Mon Sep 17 00:00:00 2001
From: Dany Castillo <31006608+dcastil@users.noreply.github.com>
Date: Thu, 1 Jun 2023 11:12:50 +0200
Subject: [PATCH 15/19] improve arrows in docs
---
docs/when-and-how-to-use-it.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/when-and-how-to-use-it.md b/docs/when-and-how-to-use-it.md
index 94a35648..d3a04f26 100644
--- a/docs/when-and-how-to-use-it.md
+++ b/docs/when-and-how-to-use-it.md
@@ -31,7 +31,7 @@ tailwind-merge is probably only useful if you use Tailwind CSS and compose compo
### Easy to compose components through multiple levels
-tailwind-merge is a great fit for highly composed components like in design systems or UI component libraries. If you expect that styles of a component will be modified on multiple levels, e.g. ContextMenuOption -> MenuOption -> BaseOption, with each component passing some modifications to the component it renders, tailwind-merge can help you to keep the API surface between components small.
+tailwind-merge is a great fit for highly composed components like in design systems or UI component libraries. If you expect that styles of a component will be modified on multiple levels, e.g. ContextMenuOption → MenuOption → BaseOption, with each component passing some modifications to the component it renders, tailwind-merge can help you to keep the API surface between components small.
### Enables fast development velocity and iteration speed
From 2cb2cef4058fd81bce688f8aa7d8e15d0c8a63c7 Mon Sep 17 00:00:00 2001
From: Dany Castillo <31006608+dcastil@users.noreply.github.com>
Date: Thu, 1 Jun 2023 11:26:37 +0200
Subject: [PATCH 16/19] small improvements
---
docs/when-and-how-to-use-it.md | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/docs/when-and-how-to-use-it.md b/docs/when-and-how-to-use-it.md
index d3a04f26..ef6edd21 100644
--- a/docs/when-and-how-to-use-it.md
+++ b/docs/when-and-how-to-use-it.md
@@ -39,7 +39,7 @@ tailwind-merge allows you to support a wide range of styling use cases without h
### Preventing premature abstractions
-Let's say you have a Button component that you already use in many places. You have a place in your app in which you want to make its background red to signal that the action of the button is destructive. You could modify the Button component to deal with the concept of destructiveness, but then you'd need to make sure that those styles work with all the other permutations of the component which you don't need in the place where the destructive button is used. And maybe you're not even sure whether you'll keep the Button red in this one place, so the time investment of making the Button understand destructiveness doesn't seem worth it.
+Let's say you have a Button component that you already use in many places. You have a place in your app in which you want to make its background red to signal that the action of the button is destructive. You could modify the Button component to deal with the concept of destructiveness (e.g. by passing a `variant` prop with the value `destructive`), but then you'd need to make sure that those styles work with all the other permutations of the component which you don't need in the place where the destructive button is used. And maybe you're not even sure whether you'll keep the Button red in this one place, so the time investment of making the Button understand destructiveness doesn't seem worth it.
tailwind-merge allows you to defer the creation of abstractions like destructiveness to the point where you're sure that you need them. You can just pass a `className` prop to the Button component in which you define the red background and be done with it for now. If you later decide that you want to make the Button red in more places, you can still define the logic inside the Button component later.
@@ -47,12 +47,12 @@ tailwind-merge allows you to defer the creation of abstractions like destructive
### Joining internal classes
-If you want to merge classes that are all defined within a component, prefer using the [`twJoin`](./api-reference.md#twjoin) function over `twMerge`. As the name suggests, `twJoin` only joins the class strings together and doesn't deal with conflicting classes.
+If you want to merge classes that are all defined within a component, prefer using the [`twJoin`](./api-reference.md#twjoin) function over [`twMerge`](./api-reference.md#twmerge). As the name suggests, `twJoin` only joins the class strings together and doesn't deal with conflicting classes.
```jsx
// React components with JSX syntax used in this example
-function MyComponent({ forceHover, disbaled, isMuted }) {
+function MyComponent({ forceHover, disabled, isMuted }) {
return (
Date: Thu, 1 Jun 2023 11:31:24 +0200
Subject: [PATCH 17/19] improve docs a bit
---
docs/when-and-how-to-use-it.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/when-and-how-to-use-it.md b/docs/when-and-how-to-use-it.md
index ef6edd21..06a20289 100644
--- a/docs/when-and-how-to-use-it.md
+++ b/docs/when-and-how-to-use-it.md
@@ -152,7 +152,7 @@ function join(...args) {
}
```
-The main downside of this approach are that it only works one level deep (you can't override the `!bg-red-500` class in the example above).
+The main downside of this approach is that it only works one level deep (you can't override the `!bg-red-500` class in the example above). But if you don't need to be able to override styles through multiple levels of composition, this might be the most lightweight approach possible.
---
From c87d4dc6781ba64c9597f91c598a4930628a20e7 Mon Sep 17 00:00:00 2001
From: Dany Castillo <31006608+dcastil@users.noreply.github.com>
Date: Thu, 1 Jun 2023 14:41:59 +0200
Subject: [PATCH 18/19] remove obsolete content
---
docs/when-and-how-to-use-it.md | 4 ----
1 file changed, 4 deletions(-)
diff --git a/docs/when-and-how-to-use-it.md b/docs/when-and-how-to-use-it.md
index 06a20289..9719043b 100644
--- a/docs/when-and-how-to-use-it.md
+++ b/docs/when-and-how-to-use-it.md
@@ -161,7 +161,3 @@ Next: [Features](./features.md)
Previous: [What is it for](./what-is-it-for.md)
[Back to overview](./README.md)
-
-```
-
-```
From 0a9a0cea1706a554dd3066dbafab45e414a7beab Mon Sep 17 00:00:00 2001
From: Dany Castillo <31006608+dcastil@users.noreply.github.com>
Date: Thu, 1 Jun 2023 18:50:45 +0200
Subject: [PATCH 19/19] revert accidental change in package export test
---
scripts/test-built-package-exports.cjs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/scripts/test-built-package-exports.cjs b/scripts/test-built-package-exports.cjs
index c106b0d0..d01a07ba 100644
--- a/scripts/test-built-package-exports.cjs
+++ b/scripts/test-built-package-exports.cjs
@@ -1,6 +1,6 @@
const assert = require('assert')
-const { twMerge } = require('../dist')
+const { twMerge } = require('..')
assert(twMerge() === '')
assert(