This repository demonstrates different approaches to styling React components, including CSS Modules, Styled Components, and Tailwind CSS. Each implementation shows how to create and style a simple button component using these different methodologies.
βββ app/
β βββ layout.tsx # Root layout component
β βββ page.tsx # Main page component
β βββ globals.css # Global styles
β βββ registry.tsx # Styled-components registry
βββ components/
β βββ css-modules/ # CSS Modules implementation
β β βββ button.tsx
β β βββ button.module.css
β βββ styled-components/ # Styled-components implementation
β β βββ button.tsx
β βββ tailwind/ # Tailwind CSS implementation
β βββ button.tsx
βββ styles/
βββ styled.d.ts # Styled-components TypeScript definitions
- Clone the repository:
git clone https://github.com/breehall/three-ways-to-style
- Install dependencies:
npm install
- Run the development server:
npm run dev
- Open http://localhost:3000 in your browser to see the fancy buttons in action.
CSS Modules allow you to write traditional CSS with locally scoped class names. This prevents style conflicts and provides better CSS organization.
Example usage:
import styles from './button.module.css';
const Button = ({
variant,
size,
disabled,
loading,
icon,
children,
onClick,
}: ButtonProps) => {
return <button className={styles.button}>{children}</button>;
};
Styled Components provide a way to write CSS-in-JS with a component-based approach. This allows for more dynamic and reusable styles.
Example usage:
import styled from 'styled-components';
const StyledButton = styled.button`
background-color: ${({ theme }) => theme.colors.primary};
color: ${({ theme }) => theme.colors.text};
Tailwind CSS is a utility-first CSS framework that allows you to build complex designs with a minimal amount of code.
Example usage:
import { Button } from '@/components/tailwind/button';
export function Button() {
return (
<button className="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
Click me
</button>
);
}
Hereβs a quick breakdown of the pros and cons for each styling solution used in this repo:
β Pros
- Simple, scoped styles with zero runtime
- Familiar for developers coming from traditional CSS
- SSR and React Server Componentβfriendly by default
- No extra dependencies or config needed
- No built-in support for variants or themes
- Can get messy as components grow
- Requires extra tooling or patterns to scale
- Styling dynamic states can feel verbose or repetitive
β Pros
- Utility-first styling = fast prototyping and predictable output
- Scales well for large teams and design systems
tailwind-variants
makes managing variants clean and composable- Supports theming via
@theme
and native CSS variables in v4 - Minimal runtime, great performance
- Readability can suffer with long class lists
- Requires strong conventions to stay maintainable
- Custom theming in CSS can get messy without structure
- Learning curve for new team members unfamiliar with utility-first CSS
β Pros
- Dynamic styles based on props
- Built-in theming via
ThemeProvider
- Highly expressive and colocated with component logic
- Great for deeply interactive or animated components
- Runtime style injection = performance cost
- Not compatible with React Server Components (relies on context)
- Hydration risks in SSR setups (like Next.js)
- Officially in maintenance mode β alternatives like Emotion or vanilla-extract may be better for new projects
This repo was created for my talk at React Miami 2025 β βStyled & SASSy: Choosing the Right Styling Solution in React.β
In this talk, I walk through how the styling landscape is shifting with React 19, Tailwind v4, and modern CSS and how three popular approaches hold up: CSS Modules, Tailwind CSS, and Styled Components.
Iβm Bree Hall, a software engineer, developer advocate, and your tech bestie β¨
I love helping devs make smarter styling decisions and build beautiful things with confidence.
π If you found this helpful, follow me for more styling tips, dev resources, and cozy frontend content π!