Tutorials Logic, IN info@tutorialslogic.com

Styling in React CSS Modules, Tailwind, Styled

How Styling Works in React

React does not force one styling solution. A React component is simply a JavaScript function that returns UI, so you can style that UI with regular CSS files, inline styles, CSS Modules, utility-first frameworks like Tailwind CSS, or CSS-in-JS libraries such as styled-components. The best choice depends on team preference, project size, and how much style isolation or dynamic behavior you need.

The important idea is to keep styles maintainable. As components grow, styling decisions affect readability, reuse, naming collisions, and even performance. That is why it helps to understand the trade-offs between the main approaches instead of treating them as interchangeable.

Popular Styling Approaches

Approach Strength Weakness Best fit
Inline styles Simple for dynamic one-off values No pseudo-classes or media queries Small dynamic style objects
Global CSS files Familiar and easy to start with Class name conflicts can grow over time Small to medium projects
CSS Modules Scoped class names without conflicts Slightly more verbose imports Component-based apps needing isolation
Styled Components Co-locates styles with components and supports dynamic props Runtime overhead and different authoring model Teams that prefer CSS-in-JS
Tailwind CSS Fast utility-based styling with consistency Long class strings if unmanaged Design systems and utility-first workflows

Example 1: Basic CSS File and Inline Styles

This combination is simple and practical. Use CSS classes for most styling, and use inline styles only for small dynamic values such as widths, colors, or transforms that depend directly on props or state.

CSS File and Inline Style Example

CSS File and Inline Style Example
.card {
    padding: 16px;
    border-radius: 10px;
    border: 1px solid #d0d7de;
    background: #ffffff;
    box-shadow: 0 6px 18px rgba(0, 0, 0, 0.06);
}

.card-title {
    margin-bottom: 8px;
    color: #1f2937;
}

Example 1: Basic CSS File and Inline Styles - JSX Example

Example 1: Basic CSS File and Inline Styles - JSX Example
import './Card.css'

function Card({ title, children, highlight = false }) {
    return (
        <div
            className="card"
            style={{ borderColor: highlight ? '#2563eb' : '#d0d7de' }}
        >
            <h3 className="card-title">{title}</h3>
            <div>{children}</div>
        </div>
    )
}

Example 2: CSS Modules

CSS Modules solve one of the biggest pain points of global CSS: naming collisions. Each class name is scoped to the component file, so you can use common names like button, title, or container without worrying about another file overriding them.

CSS Modules with clsx

CSS Modules with clsx
.button {
    border: none;
    border-radius: 8px;
    padding: 10px 16px;
    font-weight: 600;
    cursor: pointer;
}

.primary {
    background: #2563eb;
    color: white;
}

.danger {
    background: #dc2626;
    color: white;
}

.disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

Example 2: CSS Modules - JSX Example

Example 2: CSS Modules - JSX Example
import clsx from 'clsx'
import styles from './Button.module.css'

function Button({ label, variant = 'primary', disabled = false }) {
    return (
        <button
            className={clsx(
                styles.button,
                styles[variant],
                { [styles.disabled]: disabled }
            )}
            disabled={disabled}
        >
            {label}
        </button>
    )
}

Example 3: Styled Components

Styled Components is a popular CSS-in-JS library. It allows you to define styles directly in JavaScript and vary them based on props. This can feel natural in component-driven design, especially when themes and prop-based variants are common.

Styled Components Example

Styled Components Example
import styled from 'styled-components'

const Button = styled.button`
    border: none;
    border-radius: 8px;
    padding: 10px 16px;
    font-weight: 600;
    color: white;
    background: ${props => props.variant === 'danger' ? '#dc2626' : '#2563eb'};

    &:hover {
        opacity: 0.92;
    }

    &:disabled {
        opacity: 0.6;
        cursor: not-allowed;
    }
`

function App() {
    return (
        <div>
            <Button>Save</Button>
            <Button variant="danger">Delete</Button>
        </div>
    )
}

Example 4: Tailwind CSS

Tailwind CSS uses small utility classes instead of writing most component CSS manually. This can speed up UI development and encourage consistent spacing, typography, and color usage across a React project.

Tailwind with clsx and tailwind-merge

Tailwind with clsx and tailwind-merge
import clsx from 'clsx'
import { twMerge } from 'tailwind-merge'

function cn(...inputs) {
    return twMerge(clsx(inputs))
}

function Button({ label, variant = 'primary', disabled = false }) {
    const classes = cn(
        'rounded-md px-4 py-2 font-semibold transition-colors',
        variant === 'primary' && 'bg-blue-600 text-white hover:bg-blue-700',
        variant === 'danger' && 'bg-red-600 text-white hover:bg-red-700',
        disabled && 'cursor-not-allowed opacity-60'
    )

    return (
        <button className={classes} disabled={disabled}>
            {label}
        </button>
    )
}

How to Choose a Styling Approach

  • Use global CSS when the project is small and the team wants a familiar workflow
  • Use CSS Modules when you want scoped styles without moving away from normal CSS
  • Use Styled Components when prop-driven styles and themes are central to the design system
  • Use Tailwind CSS when you prefer utility classes and consistent tokens directly in markup
  • Use inline styles only for small dynamic values, not for full complex styling systems

React Styling Failure Cases

Mistake Why it causes trouble Better approach
Putting all styles in one global file Creates collisions and hard-to-track overrides Split styles by component or feature
Using inline styles for everything Loses pseudo-classes, media queries, and maintainability Reserve inline styles for small dynamic cases
Mixing many styling systems without a rule Makes the codebase inconsistent Choose a primary approach and use it consistently
Hardcoding design values everywhere Makes design updates difficult Use tokens, variables, or reusable classes

Best Practices

  • Pick a primary styling strategy and use it consistently
  • Keep style decisions close to the component or feature they belong to
  • Prefer reusable tokens for spacing, colors, and typography
  • Use conditional class helpers like clsx when styles depend on props
  • Think about responsiveness, hover states, focus states, and accessibility from the start
  • Avoid class name collisions by using modules or a clear naming system

Summary

React supports many valid styling approaches, and each one has its own trade-offs. Global CSS is simple, CSS Modules provide scoped styles, Styled Components combine styles with component logic, and Tailwind CSS offers a utility-first workflow. The right choice depends on the size of the project, the team's preferences, and the design system needs.

The most important thing is not choosing the trendiest option. It is choosing an approach that keeps your components readable, your styles maintainable, and your UI consistent as the application grows.

Before you move on

Styling in React CSS Modules, Tailwind, Styled Mastery Check

5 checks
  • React does not force one styling solution.
  • The best choice depends on team preference, project size, and how much style isolation or dynamic behavior you need.
  • The important idea is to keep styles maintainable.
  • As components grow, styling decisions affect readability, reuse, naming collisions, and even performance.
  • That is why it helps to understand the trade-offs between the main approaches instead of treating them as interchangeable.

React JS Questions Learners Ask

CSS Modules transform local class names into generated names during the build. If the file exports styles.card, the rendered class may be something like Card_card__a1b2c.

Conditional JSX can easily produce both the base class and the override class, such as p-2 and p-4 or text-gray-500 and text-red-600. The final result depends on class order and Tailwind generation rules, which can become hard to read when strings are built manually.

Inline styles are fine for dynamic values such as a calculated width or a color coming from data, but they become awkward for hover states, media queries, pseudo-elements, design tokens, and shared component variants. They also create a new style object when written inline during render unless you keep the value stable.

Browse Free Tutorials

Explore 500+ free tutorials across 20+ languages and frameworks.