React Styling

React does not have a built-in styling system. You pick an approach based on your project’s needs. This is one of the areas where the React ecosystem offers real choice, but that choice can feel overwhelming at first.

This page covers the five most common approaches, when to use each, and how they compare.

1. Inline styles

React supports inline CSS through the style prop. Unlike HTML, the value is a JavaScript object, not a string.

function Alert({ message }) {
  return (
    <div style={{ backgroundColor: 'red', color: 'white', padding: '10px' }}>
      {message}
    </div>
  );
}

Note the double curly braces: the outer {} is JSX for “this is a JavaScript expression,” and the inner {} is the JavaScript object literal.

Style names use camelCase

CSS property names become camelCase in React:

const styles = {
  marginTop: '20px',
  backgroundColor: '#f0f0f0',
  fontSize: '16px',
};

Numbers default to pixels

If you pass a plain number, React adds px automatically:

// These are equivalent:
style={{ fontSize: 16 }}
style={{ fontSize: '16px' }}

// Use a string for other units:
style={{ fontSize: '1.5rem' }}
style={{ width: '50%' }}

Store styles in a variable

For multi-property styles, put the object in a variable:

const cardStyle = {
  border: '1px solid #ccc',
  borderRadius: 8,
  padding: 16,
};

function Card({ children }) {
  return <div style={cardStyle}>{children}</div>;
}

When to use inline styles

Inline styles work well for dynamic values that depend on props or state. For example, setting a progress bar width based on a percentage:

function ProgressBar({ value }) {
  return (
    <div style={{ width: '100%', backgroundColor: '#eee' }}>
      <div style={{ width: `${value}%`, backgroundColor: '#4caf50', height: 8 }} />
    </div>
  );
}

Limitations: Inline styles cannot use media queries, hover states, focus states, animations, or pseudo-elements (:before, :after). For general layout and component styling, use one of the approaches below.

2. External CSS files

You can import a regular .css file directly into a component:

/* Button.css */
.button {
  background-color: #0070f3;
  color: white;
  border: none;
  padding: 8px 16px;
  border-radius: 4px;
  cursor: pointer;
}

.button:hover {
  background-color: #005bb5;
}
// Button.jsx
import './Button.css';

function Button({ label }) {
  return <button className="button">{label}</button>;
}

This is straightforward and requires no extra setup. It works exactly like CSS on a regular webpage.

Limitation: Class names are global. If two different CSS files both define .button, they will conflict. In a small project this is fine; in a larger one it becomes a problem.

CSS Modules solve the global naming problem by scoping class names to the component that imports them. Two components can both have a .button class and they will never clash.

How it works

Name your file with .module.css:

/* Button.module.css */
.button {
  background-color: #0070f3;
  color: white;
  border: none;
  padding: 8px 16px;
  border-radius: 4px;
  cursor: pointer;
}

.primary {
  background-color: #0070f3;
}

.secondary {
  background-color: #6c757d;
}

Import it as an object:

// Button.jsx
import styles from './Button.module.css';

function Button({ label, variant = 'primary' }) {
  return (
    <button className={`${styles.button} ${styles[variant]}`}>
      {label}
    </button>
  );
}

At build time, styles.button becomes a unique class name like Button_button__3xKa9. No other component’s .button class can interfere.

Setup

CSS Modules are built into Vite and Next.js with no configuration needed. Just name your file *.module.css and import it.

When to use CSS Modules

CSS Modules are a solid default for most React projects. You get the familiarity of writing regular CSS, hover states and media queries work normally, and you avoid class name collisions automatically.

4. Tailwind CSS

Tailwind is a utility-first CSS framework. Instead of writing CSS files, you apply small utility classes directly in your JSX.

function Button({ label }) {
  return (
    <button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
      {label}
    </button>
  );
}

Each class does one thing: bg-blue-500 sets the background colour, text-white sets the text colour, py-2 adds vertical padding, and so on.

When to use Tailwind

Tailwind is popular because you rarely leave your JSX file to style something. It works well for teams that want consistent spacing, colours, and typography without writing custom CSS. It also removes the need to come up with class names.

The downside is that JSX elements can become visually noisy when many utilities are applied. Some developers use the clsx or cn utility function to keep long class lists manageable.

See tailwindcss.com for setup instructions. Vite and Next.js both have official Tailwind guides.

5. CSS-in-JS (Styled Components, Emotion)

CSS-in-JS libraries let you write CSS directly inside your JavaScript using tagged template literals. The most popular options are Styled Components and Emotion .

// With Styled Components
import styled from 'styled-components';

const Button = styled.button`
  background-color: ${(props) => (props.primary ? '#0070f3' : '#6c757d')};
  color: white;
  border: none;
  padding: 8px 16px;
  border-radius: 4px;
  cursor: pointer;

  &:hover {
    opacity: 0.9;
  }
`;

// Used like any other component:
function App() {
  return (
    <>
      <Button primary>Primary</Button>
      <Button>Secondary</Button>
    </>
  );
}

Notice how the primary prop changes the background colour directly inside the CSS. This tight coupling between styles and props is the main appeal.

When to use CSS-in-JS

CSS-in-JS is a good choice when your styles depend heavily on props or dynamic values, and when you want styles and component logic to live in the same file. It adds a small runtime cost and a dependency, so weigh that against the benefits for your project.

Comparison

ApproachScoped?Media queries / hover?Dynamic styles?Setup needed?
Inline stylesYes (by nature)NoYesNone
External CSSNo (global)YesLimitedNone
CSS ModulesYesYesLimitedNone (Vite/Next.js)
Tailwind CSSYesYesLimitedYes (install)
CSS-in-JSYesYesYesYes (install)

A simple rule: If you are not sure what to pick, use CSS Modules. They work without any extra setup in Vite and Next.js, they scale well, and they keep your CSS familiar and separate from your logic.

What to learn next