React gives you many ways to style React components, and the choice matters. You can write styles as objects, plain CSS files, scoped CSS Modules, utility classes, or CSS generated from JavaScript, and each approach trades locality, tooling, and reuse differently. This article compares them on the same small card.
The approaches at a glance
The five approaches differ in where the styles live and what you give up for convenience. A comparison table is the fastest way to see the split before the code.
| Approach | Where styles live | Best for |
|---|---|---|
| Inline styles | style object on the element | One-off dynamic values |
| Plain CSS | Global .css files | Small apps and quick styles |
| CSS Modules | Scoped .module.css files | Styles without a framework |
| Tailwind CSS | Utility classes in JSX | Fast, consistent UI systems |
| CSS-in-JS | Tagged template in JavaScript | Component-tied dynamic styles |
None of these is strictly better. They solve different pressures: how fast you can write a style, how easily you can reuse it, and how much tooling you are willing to run.
Inline styles
Inline styles put a style object directly on the element. There is no CSS file and no build step, which makes them the fastest way to attach a one-off value.
function Card() {
const styles = {
padding: "16px",
borderRadius: "8px",
boxShadow: "0 2px 8px rgba(0, 0, 0, 0.1)",
};
return <div style={styles}>A card styled with an object.</div>;
}The browser applies the padding, rounded corners, and shadow to the card. The object uses camelCase keys such as borderRadius because JavaScript properties cannot contain hyphens.
Inline styles cannot express hover states, media queries, or keyframe animations, and the values live inside the component. They work best for one-off values that no other element needs.
Plain CSS files
Plain CSS imports a global stylesheet and attaches a class name. The styles live outside the component, so the same rules can apply anywhere in the app.
.card {
padding: 16px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}The component imports the file once and uses the class name on the element. Vite injects the stylesheet globally, so the rule is available to every component in the app.
import "./card.css";
function Card() {
return <div className="card">A card styled by a global stylesheet.</div>;
}The browser renders the same card as before. Because the class is global, two files could both define .card and silently override each other, which is the main reason larger apps move to scoped styles.
CSS Modules
CSS Modules take a plain CSS file and scope every class to the component that imports it. Vite treats any file named .module.css as a CSS Module, so no extra package is required.
.card {
padding: 16px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}The import returns an object of generated class names, and the component reads one key from it. Each class in the file becomes a property on that object, so the markup stays clean.
import styles from "./Card.module.css";
function Card() {
return <div className={styles.card}>A card with scoped styles.</div>;
}At build time the .card class becomes something unique, so another component can define its own .card without a collision. The full tradeoff against a utility-first approach is in the CSS Modules vs Tailwind comparison.
Tailwind CSS
Tailwind replaces hand-written class names with small utility classes that each set one property. The styles stay in the JSX, but the classes are reusable and consistent.
function Card() {
return (
<div className="rounded-lg p-4 shadow-md">
A card styled with utility classes.
</div>
);
}The rounded-lg class sets the radius, p-4 sets the padding, and shadow-md sets the shadow. Tailwind scans these class names at build time and emits only the CSS you actually use, so the shipped file stays small. Setup and the current install command are covered in the Tailwind setup walkthrough.
CSS-in-JS with styled-components
styled-components writes real CSS inside tagged template literals and returns a React component. Each component carries its own styles, so deleting the component deletes its styles too.
import styled from "styled-components";
const Card = styled.div`
padding: 16px;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
`;The Card component renders a div with the styles attached, and props can drive dynamic values through template interpolations. The tradeoff is runtime cost: styles are generated and injected in the browser, which matters more in server rendered applications.
Which approach should you choose
- Choose inline styles for a single dynamic value that depends on state.
- Choose plain CSS for a small app or a quick prototype.
- Choose CSS Modules when you want scoped styles with no new mental model.
- Choose Tailwind when you want speed and consistency across many components.
- Choose styled-components when styles and component logic should live together.
The decision is about workflow, not performance. Pick one primary approach, keep it consistent, and revisit only when a real pain appears. Responsive behavior works differently in each approach, so check how breakpoints and media queries fit before you commit.
Rune AI
Key Insights
- Inline styles suit one-off values through the style prop.
- Plain CSS is simple and global but does not scope by component.
- CSS Modules scope styles with zero runtime cost.
- Tailwind moves design decisions into utility classes.
- styled-components ties styles to components with JavaScript.
Frequently Asked Questions
Which React styling approach is fastest to set up?
Can I mix styling approaches in one React app?
Do CSS-in-JS libraries work with Server Components?
Conclusion
React gives you several ways to style components, and each one solves a different problem. Inline styles, plain CSS, CSS Modules, Tailwind, and styled-components all fit different teams and codebases, so choose the approach that matches how your styles will change.
More in this topic
How to Build a Dropdown Menu in React
Build a React dropdown menu with the ARIA menu button pattern. Handle open and close, keyboard arrows, and clicks outside the menu.
How to Animate React Components with Motion
Animate React components with the Motion library. Set up motion, add enter, hover, and exit animations, and respect reduced motion.
Headless UI Components Explained: Logic Without Locked Styling
Understand headless UI components and how libraries like Radix give you unstyled, accessible behavior that you style yourself.