How to properly join tailwind css classes using clsx?

Viewed 2717

I am trying to join tailwind classes and apply them to a button using clsx. One class is applied by default 'rounded-none' and the other one is passed in as a prop

const Button = ({
  children, ...props
}): JSX.Element => {

  return (
    <ADButton
      className={clsx('rounded-none', props.className)}
      {...props}
    >
      {children}
    </ADButton>
  );
};

Let's say I have added padding-top: 0px; to the button like shown below

<Button
  color="primary"
  className="pt-0"
>
  {t('btn.add')}
</Button>

The joined className should look like 'rounded-none pt-0'. If no className prop is passed, then just apply ‘rounded-none’

The problem am having right now is ‘rounded-none’ only gets applied to buttons without className prop. On buttons with className prop, only className prop gets applied but not ‘rounded-none’. How can I fix this so that both classes are joined and applied to the button?

1 Answers

You're having this issue because the default prop of ADButton is set to className and the extra prop you're passing to the Button component is set to className as well. In effect, you are overriding the default className with your newly passed className prop. If there are are 2 similarly named props, React will choose the one that is declared later in the event of a conflict.

So this:

<ADButton
      className={clsx('rounded-none', 'pt-0')}
// I am declared later so I win
      className='pt-0'
    >
      {children}
    </ADButton>

becomes:

<ADButton
      className="pt-0"
    >
      {children}
    </ADButton>

Here's one solution:

const Button = ({
  children, ...props
}) => {
  const { classNameDestructured = "", ...rest } = props;
  return (
    <ADButton
      className={clsx('rounded-none', classNameDestructured)}
      {...rest}
    >
      {children}
    </ADButton>
  );
};

You destructure props and set a default for classNameDestructured. This allows you to declare Button without additional props:

<Button>
  {t('btn.add')}
</Button>

Then you pass classNameDestructured as an argument to your clsx() function. Your additional classes are then joined and applied to the button.

This works because className isn't declared twice as a prop in ADButton anymore so we've eliminated the overriding conflict.

Related