3 min read

This is how I code my React components with Tailwind and Typescript and it has proven over time

This is how I code my React components with Tailwind and Typescript and it has proven over time

React is a popular JavaScript library used for building user interfaces. It provides a component-based architecture that allows developers to build reusable UI elements. Tailwind is a utility-first CSS framework that makes styling components a breeze. When used together, React and Tailwind can help developers create beautiful, responsive, and easy-to-maintain UIs.

TLDR; here is the code

export interface SomeComponentProps {
  foo?: boolean;
}

const SomeComponent: React.FC<React.PropsWithChildren<HTMLAttributes<HTMLDivElement> & SomeComponentProps>> = ({
  foo = true,
  children,
  className,
  ...props
}) => {
  return (
    <div
      className={twMerge(
        'bg-red-500',
        className,
      )}
      {...props}
    >
      {children}
    </div>
  );
};

In this article, I will share some tips on how I code my React components with Tailwind, and why it has proven to be a great combination over time.

Always Merge Classes for Flexibility

When working with Tailwind and React, I always merge classes to provide flexibility and make it easier to modify styles. I use a utility function called twMerge, which combines multiple class names into a single string. This function is similar to the popular clsx library but optimized for Tailwind classes.

import { twMerge } from 'tailwind-merge';

const SomeComponent: React.FC<Props> = ({ className, ...props }) => (
  <div className={twMerge('bg-red-500', className)} {...props} />
);

By using twMerge, I can add, remove or modify Tailwind classes without having to worry about managing multiple class names. This approach also allows me to use dynamic class names based on component props or state.

The reasons why I recommend you ALWAYS merge classes are:

  • Flexibility: in your daily work you will always have some special cases. Maybe add some margin here because some components visually needs more space only in that case? Simply add a m-4 class to your component as a property and done. No need to customize your component and you will easily recognize what you did and why.
  • Manage your margin via CSS classes passed as properties
  • Manage responsive toggling, e.g. show on mobile or hide on desktop, via CSS classes as props

Manage Responsive Toggling via Classes

Responsive toggling via classes means that we use CSS classes to apply different styles to a component based on the screen size or device. This is a common technique in responsive web design, and it allows us to create components that look great on a wide range of devices, from small mobile screens to large desktop monitors.

One example of responsive toggling via classes in Tailwind is the hidden class, which can be used to hide a component on certain screen sizes. For example, we might use the hidden md:block class to hide a component on screens smaller than the medium breakpoint, but show it on larger screens.

This also prevents a FOUC (flash of unstyled content) on initial render if you'd otherwise use Javascript conditional logic to render components based on the viewport. So I like to stick to traditional methods and use CSS classes for responsive hiding/showing.

Manage Margin via Classes, Never Include

Now, regarding the management of margin classes, it's better to pass them as props instead of embedding them into the component. When we embed margin classes directly into a component, we tie the component's layout to its styles, making it harder to modify and reuse. By passing margin classes as props, we keep the component's layout and styling separate, which makes it more flexible and easier to modify.

Overall, managing responsive toggling via classes and passing margin classes as props are both important techniques for creating flexible and responsive components in Tailwind. By using these techniques, we can create components that look great on a wide range of devices, are easy to modify and reuse, and provide a smooth user experience.

Spread Props to Maintain HTML API

Finally, I always spread props to maintain the HTML API. This means that any props passed to the component will be spread to the rendered element, just like they would be in regular HTML. By spreading props, I can pass any valid HTML attribute to the component, such as onClick, id, title, or data-* attributes, and they will be applied to the rendered <div> element.

To maintain this make sure to merge props that are also native HTML api, for example like className using twMerge / clsx or style which can be solved by a simple object spread like style={{ ...style, borderColor: 'black' }}.

Conclusion

In this article, I have shared some tips on how I code my React components with Tailwind. By always merging classes, managing responsive toggling via classes, managing margin via classes, and spreading props to maintain the HTML API, I have found that my components are more flexible, easier to modify, and easier to reason about.

If you're interested in learning more about Tailwind, I highly recommend checking out the official documentation. And if you're looking for a utility function to merge Tailwind classes, check out the tailwind-merge library on npm.

Happy coding! πŸš€