ScreenshotNeo

BlogGuides

5 Ways to Style React with CSS (2026)

Compare five ways to style React, with runnable examples, trade-offs, and practical guidance for choosing CSS that fits your project.

By the ScreenshotNeo team29 September 20269 min read

5 Ways to Style React with CSS (2026)

React supports several ways to style components, and it does not require a particular framework. The five common approaches are plain CSS, CSS Modules, inline style objects, CSS-in-JS with styled-components, and Tailwind CSS. Choose plain CSS for familiar browser styles, CSS Modules for locally scoped class names, inline styles for values that come from JavaScript, styled-components for colocated styles and prop-driven variants, and Tailwind for composing utility classes in markup. You can mix these approaches where that keeps a project clearer.

Every example below is a React component. The stylesheet examples assume the relevant CSS file is imported by the app’s entry point or component, as supported by its build setup. For CSS Modules, the build tool must support module processing. React itself accepts className and a style object; it does not prescribe how CSS files are added. See the React documentation.

1. Plain or global CSS

Use a regular CSS file and refer to its classes with className. This is the most direct choice when your team knows CSS and wants browser-native selectors, pseudo-classes, media queries, and a familiar stylesheet.

Five styling methods connect React components to CSS in different ways.
Five styling methods connect React components to CSS in different ways.
/* Button.css */
.button {
  background: #2457d6;
  border: 0;
  border-radius: 0.5rem;
  color: white;
  cursor: pointer;
  font: inherit;
  padding: 0.65rem 1rem;
}

.button:hover {
  background: #1946b8;
}

.button:focus-visible {
  outline: 3px solid #ffbf47;
  outline-offset: 2px;
}

@media (prefers-reduced-motion: no-preference) {
  .button {
    transition: background-color 150ms ease;
  }
}
import './Button.css';

export function Button({ children, onClick }) {
  return (
    <button className="button" onClick={onClick}>
      {children}
    </button>
  );
}

Global CSS selectors apply wherever their names match, so use a naming convention and avoid broad rules such as button { ... } unless that global effect is intended. Plain CSS is a strong default for shared foundations such as resets, typography, tokens, and site-wide layout. It also suits components whose style rules are static and benefit from ordinary CSS selectors.

2. CSS Modules

CSS Modules keep standard CSS syntax but let a compatible build tool process each module separately. Import the file as an object and use its exported class names in JSX. This gives component styles a naming boundary without changing how you write CSS.

/* Card.module.css */
.card {
  border: 1px solid #d9deea;
  border-radius: 0.75rem;
  padding: 1.25rem;
}

.title {
  font-size: 1.25rem;
  margin: 0 0 0.5rem;
}

@media (max-width: 40rem) {
  .card {
    padding: 1rem;
  }
}
import styles from './Card.module.css';

export function Card({ title, children }) {
  return (
    <section className={styles.card}>
      <h2 className={styles.title}>{title}</h2>
      {children}
    </section>
  );
}

The exact scoping and import behavior depends on the project’s build tool and configuration. Check that your chosen setup processes .module.css files; do not assume React itself implements CSS Modules. Vite, Parcel, and Turbopack document CSS Module support, and CSS Modules can coexist with Tailwind in supported setups. Keep global tokens and resets in a deliberate global stylesheet; use modules for rules owned by a component.

3. Inline style objects

The style prop takes a JavaScript object. Property names use camelCase, and numeric values for many length properties are interpreted as pixels. Use this when a value is computed at runtime, such as a user-selected color or a chart position. React documents this as a route for styles that depend on JavaScript variables.

export function Status({ color, progress }) {
  const safeProgress = Math.max(0, Math.min(100, progress));

  return (
    <div
      role="status"
      style={{
        color,
        borderColor: color,
        '--progress': `${safeProgress}%`,
      }}
    >
      <div
        aria-hidden="true"
        style={{
          height: 8,
          width: `${safeProgress}%`,
          backgroundColor: color,
        }}
      />
      {safeProgress}% complete
    </div>
  );
}

Inline styles are not a replacement for stylesheets. They cannot express selectors such as :hover and :focus, or media queries. Tailwind’s documentation makes the same distinction: inline styles cannot target states like hover or focus, while utility variants can. Put structural rules and interaction states in a stylesheet, and pass only the changing values through JavaScript. Validate values that come from user or remote data, and avoid building CSS values from untrusted strings.

4. CSS-in-JS with styled-components

styled-components uses tagged template literals to define React components with attached styles. It supports ordinary CSS features, including pseudo-selectors and media queries, and can make prop-driven variants or theming part of a component API.

import styled from 'styled-components';

const Button = styled.button`
  background: ${({ $tone }) => $tone === 'danger' ? '#b42318' : '#2457d6'};
  border: 0;
  border-radius: 0.5rem;
  color: white;
  cursor: pointer;
  font: inherit;
  padding: 0.65rem 1rem;

  &:hover {
    filter: brightness(0.92);
  }

  &:focus-visible {
    outline: 3px solid #ffbf47;
    outline-offset: 2px;
  }
`;

export function ActionButton({ tone = 'primary', children, ...props }) {
  return (
    <Button $tone={tone} {...props}>
      {children}
    </Button>
  );
}

The transient $tone prop is used by the style and is not forwarded as a normal HTML attribute. Before adopting this approach across an app, evaluate the library version, runtime behavior, server rendering setup, and compatibility with the React architecture and rendering framework you use. The extra component API can help keep variants together; for a small number of static styles, a stylesheet may be simpler.

5. Utility classes with Tailwind CSS

Tailwind composes single-purpose classes in markup. Its variants cover responsive breakpoints and states such as hover and focus; arbitrary values are available when a design value is outside the standard scale. You can still write plain CSS for custom rules and base or component layers.

export function Button({ children, disabled = false }) {
  return (
    <button
      disabled={disabled}
      className="rounded-lg bg-blue-700 px-4 py-2.5 font-medium text-white
                 hover:bg-blue-800 focus-visible:outline-2
                 focus-visible:outline-offset-2 focus-visible:outline-amber-400
                 disabled:cursor-not-allowed disabled:opacity-50"
    >
      {children}
    </button>
  );
}

This assumes Tailwind is installed and configured so the build can generate the utilities used by the app. Follow the official installation guide for the project’s setup. A class string can become hard to scan; extract repeated patterns into React components and agree on formatting and variant conventions. For values from a database or API, Tailwind documents inline styles as one option; avoid constructing class names dynamically in ways the build cannot detect and generate.

How to choose

Method Good fit Watch for
Plain CSS Static rules, browser-native selectors, shared styles Global name collisions; use naming discipline
CSS Modules Component-owned styles with familiar CSS syntax Build-tool support and project-specific scoping behavior
Inline styles Values calculated from JavaScript at render time No selectors, pseudo-classes, or media queries
styled-components Colocated styles, prop variants, theming Runtime, server rendering, and framework integration
Tailwind Fast composition with a constrained utility vocabulary Dense markup and setup that generates used utilities

A practical selection sequence:

  1. Start with the CSS your team already understands and your build already supports.
  2. If class collisions or ownership boundaries are a problem, consider CSS Modules.
  3. If only a few values vary at runtime, keep the stylesheet and pass those values via inline styles or custom properties.
  4. If component variants and theming are central to your design system, evaluate styled-components against your rendering setup.
  5. If your team prefers utility composition, establish Tailwind conventions and extract repeated UI into components.

There is no need to force every rule into one method. For example, a project can use global CSS for typography and tokens, CSS Modules for component layout, and inline values for data-driven chart colors. Keep overlapping ownership explicit so that one element does not acquire competing sources of truth.

Common mistakes and troubleshooting

Symptom Likely cause Fix
Styles do not appear Stylesheet was not imported, path is wrong, or build configuration does not process the file Check the import path and development server output; verify the relevant CSS or module support in the build tool.
Module class is undefined File is not treated as a CSS Module, or the exported class name does not match Use the project’s module filename convention and confirm module processing is enabled.
Inline value is ignored Property name uses CSS kebab-case, a value has invalid units, or a custom property name is wrong Use camelCase for standard properties, include units in strings when needed, and spell custom properties with their -- prefix.
Hover or responsive rule fails Interaction or media query was put in an inline object Move selector-based behavior into a stylesheet, styled component, or Tailwind variant.
Tailwind class has no effect The utility was not generated or the project configuration does not scan the file containing it Check the installation and content/source configuration for the Tailwind version in use; use complete class names the build can discover.
styled-components style is missing or differs after rendering Library setup or server-rendering integration is incomplete, or values differ between render environments Check the current library and framework integration instructions, then keep server and client inputs consistent.

When debugging, inspect the rendered element’s class or style attribute, then inspect computed styles in browser developer tools. Look for a missing stylesheet, a selector that does not match, a rule overridden by greater specificity or later order, and a media query that is not active at the current viewport. For style values derived from props, verify the actual value at the point it reaches the component.

Performance, reliability, and maintenance

These approaches do not have a universal performance winner across all React apps. The dossier’s official sources provide qualitative trade-offs, not directly comparable performance or adoption benchmarks. Avoid choosing from an invented speed ranking. Plain CSS and CSS Modules rely on stylesheet rules and build-time processing; Tailwind also generates utility CSS at build time; CSS-in-JS can introduce runtime and server-rendering considerations. Measure the actual application if performance is a deciding factor.

For reliability, standardize file naming, token ownership, and how variants are represented. Keep focus states and responsive behavior in a method that supports selectors and media queries. Check server-rendering compatibility before using a runtime styling library in a server-rendered app. For long-term maintenance, avoid mixing multiple systems inside the same component without a clear reason, and remove obsolete rules when a component changes.

Or skip the browser setup

If you are styling a React page and need a screenshot for a review, bug report, or visual comparison, you can capture a URL with ScreenshotNeo, a screenshot API and MCP server from Yorker Media. Its API accepts a URL in one GET request and returns an image or PDF. See the ScreenshotNeo API documentation.

A capture flow can remove common overlays before producing a screenshot.
A capture flow can remove common overlays before producing a screenshot.
curl -G "https://api.screenshotneo.com/v1/shot" \
  -d access_key=YOUR_API_KEY \
  --data-urlencode url=https://stripe.com \
  -o shot.webp
import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({
  access_key: 'YOUR_API_KEY',
  url: 'https://stripe.com',
});
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
const bytes = new Uint8Array(await res.arrayBuffer());
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', bytes));

Cookie banners, newsletter popups, and chat widgets are removed before the shot, and each cleanup step can be turned off. Bot checks, blank pages, timeouts, failed loads, and cache hits cost nothing; response headers identify the page verdict and billing status. AI agents can use its MCP server tools to take screenshots, get page information, and capture PDFs. The free plan includes 1,000 screenshots a month with no card; paid plans start at $5 for 3,000. Sign up for free ScreenshotNeo screenshots.

FAQ

Can I use more than one styling method in a React app?

Yes. React does not mandate one method. Define where global rules, component-owned styles, and runtime values belong so the combination remains understandable.

Does React use class or className?

Use className in JSX to set a CSS class.

Should every dynamic style be inline?

No. Inline objects are useful for values supplied by JavaScript, while stylesheets and utility variants remain better for selectors, states, and responsive rules.

Which approach should a new project choose?

Choose based on the team’s CSS habits, build setup, need for scoping, runtime values, and component architecture. The comparison above is a starting point; it is not a universal ranking.

References