DEV Community

Gabriel Linassi
Gabriel Linassi

Posted on


TailwindCSS + React best practices: The clean way

I've using Tailwind for a long time and I've had so much pain and that uncomfortable feeling of seeing your code a total mess. I've sharped my strategies to write Tailwind throughout the time and I think I've come up with a good pattern to build more complex components.

The pattern uses CSS Modules, Talwind @apply and tailwind-merge. Let's say I want to build a Button, this would be the folder structure:

Enter fullscreen mode Exit fullscreen mode

And the code like this:

Button /button/Button.tsx

import s from './Button.module.css'
import React from 'react'
import cn from 'classnames'
import Spinner from 'components/spinner'

type ButtonProps = {
  children: React.ReactNode
  fullWidth?: boolean
  loading?: boolean
  variant?: 'filled' | 'outlined'
  color?: 'primary' | 'secondary'
  size?: 'base' | 'lg'
} & Omit<React.ComponentProps<'button'>, 'className'>

const Button = ({
  variant = 'filled',
  color = 'primary',
  size = 'base',
}: ButtonProps) => {
  const classes = cn(s.root, s[variant], s[color], s[size], {
    [s.fullWidth]: fullWidth,

  return (
    <button className={classes} disabled={disabled || loading} {...props}>
      {loading && (
        <span className="ml-1.5">
          <Spinner className={s.spinner} />

export default Button
Enter fullscreen mode Exit fullscreen mode

Styles /button/Button.module.css

.root {
  @apply inline-flex items-center justify-center rounded-full font-semibold duration-150 disabled:pointer-events-none disabled:opacity-75;

.fullWidth {
  @apply w-full;

.base {
  @apply px-8 py-3;

.lg {
  @apply px-12 py-5;

.filled.primary {
  @apply bg-[#FAA806] text-[#FFFFFF] hover:bg-[#EE9F04];

.filled.secondary {
  @apply bg-[#373E4B] text-[#97A3B7] hover:bg-[#343A47];

.outlined.primary {
  @apply border-[#FAA806] text-[#FAA806];

.outlined.secondary {
  @apply border-[#373E4B] text-[#373E4B];

.primary .spinner {
  @apply fill-[#bc7e03] text-white;

.secondary .spinner {
  @apply fill-[#292e38] text-white;
Enter fullscreen mode Exit fullscreen mode

Usage (with NextJS Link)

<NextLink href="/signin" passHref>
  <Button fullWidth {...{ disabled, loading }}>
Enter fullscreen mode Exit fullscreen mode

Source code (Stackblitz)

Top comments (0)