Overview
React offers multiple ways to style your components, each with its own advantages and use cases. This section covers the main styling approaches used in modern React applications.
Dynamic Styling Apply styles conditionally based on component state
CSS Modules Scoped CSS that prevents style conflicts
Styled Components CSS-in-JS with dynamic prop-based styling
Tailwind CSS Utility-first CSS framework for rapid development
Inline Styles
Inline styles are JavaScript objects applied directly to elements. They’re useful for highly dynamic styles.
import { useState } from 'react' ;
export default function AuthInputs () {
const [ enteredEmail , setEnteredEmail ] = useState ( '' );
const [ submitted , setSubmitted ] = useState ( false );
const emailNotValid = submitted && ! enteredEmail . includes ( '@' );
return (
< input
type = "email"
style = { {
backgroundColor: emailNotValid ? '#fed2d2' : '#d1d5db'
} }
onChange = { ( event ) => setEnteredEmail ( event . target . value ) }
/>
);
}
Inline styles use camelCase property names (e.g., backgroundColor instead of background-color) and values must be strings or numbers.
Pros and Cons
Direct access to component state and props
No CSS file needed
Styles are scoped to the component
Great for highly dynamic values
No pseudo-selectors (:hover, :focus, etc.)
No media queries
Can become verbose for complex styles
Inline styles have high specificity
Dynamic CSS Classes
Conditionally apply CSS classes for more maintainable styling.
Conditional Classes
Corresponding CSS
import { useState } from 'react' ;
export default function AuthInputs () {
const [ enteredEmail , setEnteredEmail ] = useState ( '' );
const [ submitted , setSubmitted ] = useState ( false );
const emailNotValid = submitted && ! enteredEmail . includes ( '@' );
return (
< div >
< label className = { `label ${ emailNotValid ? 'invalid' : '' } ` } >
Email
</ label >
< input
type = "email"
className = { emailNotValid ? 'invalid' : undefined }
onChange = { ( event ) => setEnteredEmail ( event . target . value ) }
/>
</ div >
);
}
Use template literals to combine multiple class names dynamically. For more complex scenarios, consider the classnames or clsx libraries.
CSS Modules
CSS Modules automatically scope styles to components, preventing conflicts.
Create a CSS Module
Name your file with .module.css extension (e.g., Header.module.css)
Import the module
Import styles as an object in your component
Apply scoped classes
Use the imported object to apply classes
Header.jsx
Header.module.css
import logo from '../assets/logo.png' ;
import classes from './Header.module.css' ;
export default function Header () {
return (
< header >
< img src = { logo } alt = "A canvas" />
< h1 > ReactArt </ h1 >
< p className = { classes . paragraph } >
A community of artists and art-lovers.
</ p >
</ header >
);
}
CSS Modules work out-of-the-box with Vite and Create React App. Element selectors (like header, h1) remain global, while class selectors get scoped.
Styled Components
Styled-components is a popular CSS-in-JS library that allows you to write CSS directly in your JavaScript files.
Installation
npm install styled-components
Basic Usage
import { styled } from 'styled-components' ;
const Button = styled . button `
padding: 1rem 2rem;
font-weight: 600;
text-transform: uppercase;
border-radius: 0.25rem;
color: #1f2937;
background-color: #f0b322;
border: none;
&:hover {
background-color: #f0920e;
}
` ;
export default Button ;
Dynamic Styling with Props
import { styled } from 'styled-components' ;
const Label = styled . label `
display: block;
margin-bottom: 0.5rem;
font-size: 0.75rem;
font-weight: 700;
letter-spacing: 0.1em;
text-transform: uppercase;
color: ${ ({ $invalid }) => ( $invalid ? '#f87171' : '#6b7280' ) } ;
` ;
const Input = styled . input `
width: 100%;
padding: 0.75rem 1rem;
line-height: 1.5;
background-color: ${ ({ $invalid }) => ( $invalid ? '#fed2d2' : '#d1d5db' ) } ;
color: ${ ({ $invalid }) => ( $invalid ? '#ef4444' : '#374151' ) } ;
border: 1px solid ${ ({ $invalid }) => ( $invalid ? '#f73f3f' : 'transparent' ) } ;
border-radius: 0.25rem;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
` ;
export default function CustomInput ({ label , invalid , ... props }) {
return (
< p >
< Label $invalid = { invalid } > { label } </ Label >
< Input $invalid = { invalid } { ... props } />
</ p >
);
}
Use the $ prefix for props that shouldn’t be passed to the DOM (like $invalid). This prevents React warnings about unknown DOM attributes.
import { styled } from 'styled-components' ;
import logo from '../assets/logo.png' ;
const StyledHeader = styled . header `
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-top: 2rem;
margin-bottom: 2rem;
& img {
object-fit: contain;
margin-bottom: 2rem;
width: 11rem;
height: 11rem;
}
& h1 {
font-size: 1.5rem;
font-weight: 600;
letter-spacing: 0.4em;
text-align: center;
text-transform: uppercase;
color: #9a3412;
font-family: 'Pacifico', cursive;
margin: 0;
}
& p {
text-align: center;
color: #a39191;
margin: 0;
}
@media (min-width: 768px) {
margin-bottom: 4rem;
& h1 {
font-size: 2.25rem;
}
}
` ;
export default function Header () {
return (
< StyledHeader >
< img src = { logo } alt = "A canvas" />
< h1 > ReactArt </ h1 >
< p > A community of artists and art-lovers. </ p >
</ StyledHeader >
);
}
Styled-components supports all CSS features including pseudo-selectors, media queries, animations, and nested selectors using the & symbol.
Tailwind CSS
Tailwind CSS is a utility-first CSS framework that provides pre-built classes for rapid UI development.
Setup with Vite
Install Tailwind
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
Configure template paths
export default {
content: [
"./index.html" ,
"./src/**/*.{js,ts,jsx,tsx}" ,
] ,
theme: {
extend: {},
} ,
plugins: [] ,
}
Add Tailwind directives
@tailwind base;
@tailwind components;
@tailwind utilities;
Usage Example
import { useState } from 'react' ;
import Button from './Button.jsx' ;
import Input from './Input.jsx' ;
export default function AuthInputs () {
const [ enteredEmail , setEnteredEmail ] = useState ( '' );
const [ enteredPassword , setEnteredPassword ] = useState ( '' );
const [ submitted , setSubmitted ] = useState ( false );
const emailNotValid = submitted && ! enteredEmail . includes ( '@' );
const passwordNotValid = submitted && enteredPassword . trim (). length < 6 ;
function handleLogin () {
setSubmitted ( true );
}
return (
< div
id = "auth-inputs"
className = "w-full max-w-sm p-8 mx-auto rounded shadow-md bg-gradient-to-b from-stone-700 to-stone-800"
>
< div className = "flex flex-col gap-2 mb-6" >
< Input
label = "Email"
invalid = { emailNotValid }
type = "email"
onChange = { ( event ) => setEnteredEmail ( event . target . value ) }
/>
< Input
invalid = { passwordNotValid }
label = "Password"
type = "password"
onChange = { ( event ) => setEnteredPassword ( event . target . value ) }
/>
</ div >
< div className = "flex justify-end gap-4" >
< button type = "button" className = "text-amber-400 hover:text-amber-500" >
Create a new account
</ button >
< Button onClick = { handleLogin } > Sign In </ Button >
</ div >
</ div >
);
}
Tailwind’s utility classes make it easy to build responsive, dynamic UIs without writing custom CSS. Classes like hover:, focus:, and responsive prefixes (sm:, md:, lg:) enable complex styling with minimal code.
Choosing a Styling Approach
Inline Styles Best for: Highly dynamic values, one-off stylesAvoid when: You need pseudo-selectors or media queries
CSS Modules Best for: Traditional CSS workflow, component-scoped stylesAvoid when: You need highly dynamic prop-based styling
Styled Components Best for: Component libraries, dynamic theming, full CSS features in JSAvoid when: You prefer separation of concerns or have performance constraints
Tailwind CSS Best for: Rapid prototyping, consistent design systems, utility-first approachAvoid when: You prefer semantic class names or have a custom design system
Best Practices
Choose one primary styling approach per project
Use inline styles sparingly, only for truly dynamic values
Create reusable styled components for common patterns
Performance considerations
Document your team’s chosen approach
Use linting tools to enforce styling conventions
Consider developer experience and team familiarity
Refs & Portals Learn about DOM manipulation and portals
Animations Add smooth transitions and animations