Table of Contents
- What are Styled Components?
- Setting Up Styled Components in a React Project
- Core Concepts
- Advanced Techniques
- Best Practices
- Real-World Example: Building a Styled Card Component
- Conclusion
- References
What are Styled Components?
Styled Components is a CSS-in-JS library that enables you to write CSS code directly within your React components. Instead of defining styles in separate .css or .scss files, you create reusable, styled React components by tagging template literals with CSS rules.
Key Benefits:
- Scoped Styles: Each component gets a unique class name, preventing style leaks and collisions.
- Dynamic Styling: Easily adjust styles based on component props or state.
- Maintainability: Styles live alongside the components they style, making codebases easier to navigate.
- Theming: Centralize design tokens (colors, fonts, spacing) for consistent UIs.
- No Class Name Hell: Say goodbye to manually managing class names like
card__header--primary.
Setting Up Styled Components in a React Project
Let’s get started by installing Styled Components in a React project.
Step 1: Install the Package
Use npm or yarn to install styled-components:
# Using npm
npm install styled-components
# Using yarn
yarn add styled-components
Step 2: Basic “Hello World” Example
Create a simple styled component to see it in action. Create a file Button.js:
// Button.js
import styled from 'styled-components';
// Define a styled button component
const StyledButton = styled.button`
padding: 12px 24px;
background-color: #4a6cf7;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
&:hover {
background-color: #3a5bdf;
}
`;
// Export as a React component
export default function Button({ children }) {
return <StyledButton>{children}</StyledButton>;
}
Step 3: Use the Component
Import and use Button in your app:
// App.js
import Button from './Button';
function App() {
return (
<div>
<h1>My Styled App</h1>
<Button>Click Me!</Button>
</div>
);
}
export default App;
When rendered, StyledButton will have a unique class name (e.g., sc-bdVaJa), ensuring styles don’t leak to other elements.
Core Concepts
Tagged Template Literals
Styled Components leverages tagged template literals—a JavaScript feature that lets you parse template strings with a function. The styled function is a tag that processes the CSS template literal and returns a React component.
Example:
const Heading = styled.h1`
font-size: 24px;
color: #333;
`;
Here, styled.h1 is a tag that takes the CSS string and generates a styled <h1> component.
Scoped Styles
By default, Styled Components generates unique class names for each component (e.g., sc-123xyz). This ensures styles are scoped to the component and don’t affect other elements in the DOM.
Example DOM output:
<h1 class="sc-123xyz">Hello World</h1>
No more worrying about accidental style overrides!
Dynamic Styling with Props
Easily adjust styles based on component props. Use ${props => ...} to inject dynamic values into the CSS.
Example: A button with variant props (primary/secondary):
const StyledButton = styled.button`
padding: 12px 24px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
/* Dynamic background color based on "variant" prop */
background-color: ${(props) =>
props.variant === 'primary' ? '#4a6cf7' : '#f5f5f5'};
/* Dynamic text color based on "variant" prop */
color: ${(props) =>
props.variant === 'primary' ? 'white' : '#333'};
`;
// Usage
<StyledButton variant="primary">Primary Button</StyledButton>
<StyledButton variant="secondary">Secondary Button</StyledButton>
Nesting Selectors
Like SCSS/Sass, Styled Components supports selector nesting for child elements, pseudo-classes, and pseudo-elements.
Example: Styling a card with nested elements:
const Card = styled.div`
padding: 20px;
border: 1px solid #e0e0e0;
border-radius: 8px;
/* Nest child selector */
h3 {
margin: 0 0 10px 0;
color: #2d2d2d;
}
/* Nest pseudo-class */
&:hover {
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
/* Nest pseudo-element */
&::after {
content: '';
display: block;
height: 4px;
width: 100%;
background: linear-gradient(to right, #4a6cf7, #7b61ff);
border-radius: 0 0 4px 4px;
}
`;
// Usage
<Card>
<h3>Card Title</h3>
<p>Card description text...</p>
</Card>
Theming with ThemeProvider
For consistent design systems, use ThemeProvider to pass a theme object (colors, fonts, spacing) to all styled components in your app.
Step 1: Define a Theme
Create a theme.js file with design tokens:
// theme.js
export const theme = {
colors: {
primary: '#4a6cf7',
secondary: '#f5f5f5',
text: '#333',
lightText: '#666',
},
fonts: {
sans: 'Inter, sans-serif',
serif: 'Georgia, serif',
},
spacing: {
sm: '8px',
md: '16px',
lg: '24px',
},
};
Step 2: Wrap App with ThemeProvider
In App.js, import ThemeProvider and your theme:
// App.js
import { ThemeProvider } from 'styled-components';
import { theme } from './theme';
import Button from './Button';
function App() {
return (
<ThemeProvider theme={theme}>
<div>
<Button>Styled with Theme!</Button>
</div>
</ThemeProvider>
);
}
Step 3: Use Theme in Styled Components
Access the theme via the theme prop in styled components:
// Updated Button.js
const StyledButton = styled.button`
padding: ${(props) => props.theme.spacing.md};
background-color: ${(props) => props.theme.colors.primary};
color: white;
font-family: ${(props) => props.theme.fonts.sans};
/* ... other styles */
`;
Advanced Techniques
Extending Components
Reuse styles from an existing component using styled(Component).
Example: Extend StyledButton to create a SmallButton:
const SmallButton = styled(StyledButton)`
padding: ${(props) => props.theme.spacing.sm};
font-size: 14px;
`;
// Usage
<SmallButton>Smaller Button</SmallButton>
Global Styles with createGlobalStyle
For global resets (e.g., box-sizing, margin: 0) or base styles, use createGlobalStyle.
Example:
// GlobalStyles.js
import { createGlobalStyle } from 'styled-components';
export const GlobalStyles = createGlobalStyle`
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: ${(props) => props.theme.fonts.sans};
color: ${(props) => props.theme.colors.text};
background-color: white;
}
`;
// In App.js, include GlobalStyles inside ThemeProvider
<ThemeProvider theme={theme}>
<GlobalStyles />
{/* App content */}
</ThemeProvider>
Animations with keyframes
Use the keyframes helper to define animations and reuse them across components.
Example: A fade-in animation:
import styled, { keyframes } from 'styled-components';
// Define keyframes
const fadeIn = keyframes`
from { opacity: 0; }
to { opacity: 1; }
`;
// Use in a component
const FadeInDiv = styled.div`
animation: ${fadeIn} 0.5s ease-in-out;
`;
Media Queries for Responsiveness
Write responsive styles with media queries directly in styled components.
Example: Adjust button size on mobile:
const StyledButton = styled.button`
padding: ${(props) => props.theme.spacing.md};
/* Mobile styles */
@media (max-width: 768px) {
padding: ${(props) => props.theme.spacing.sm};
font-size: 14px;
}
`;
Server-Side Rendering (SSR) Support
Styled Components works with SSR (e.g., Next.js) but requires extra setup to avoid “flash of unstyled content” (FOUC).
For Next.js, install styled-components and babel-plugin-styled-components, then add the plugin to .babelrc:
// .babelrc
{
"presets": ["next/babel"],
"plugins": [["styled-components", { "ssr": true }]]
}
Best Practices
- Use PascalCase for Styled Components: Match React’s component naming convention (e.g.,
StyledCard, notstyled-card). - Keep Components Focused: One styled component per file (e.g.,
Button.js,Card.js). - Avoid Over-Nesting: Deeply nested selectors can hurt readability and performance.
- Leverage Themes for Consistency: Centralize colors, fonts, and spacing to maintain design systems.
- Memoize Expensive Components: If a styled component re-renders frequently, wrap it in
React.memoto optimize performance.
Real-World Example: Building a Styled Card Component
Let’s build a reusable card component with variants, hover effects, and theming.
Step 1: Define the Card Component
Create Card.js:
import styled from 'styled-components';
const StyledCard = styled.div`
padding: ${(props) => props.theme.spacing.lg};
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
background-color: white;
transition: transform 0.2s ease-in-out;
/* Variant: "elevated" (more shadow) */
${(props) =>
props.variant === 'elevated' &&
`
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
`}
/* Hover effect */
&:hover {
transform: translateY(-2px);
}
/* Title styles */
.card-title {
font-family: ${(props) => props.theme.fonts.sans};
color: ${(props) => props.theme.colors.text};
margin-bottom: ${(props) => props.theme.spacing.md};
}
/* Description styles */
.card-description {
font-family: ${(props) => props.theme.fonts.sans};
color: ${(props) => props.theme.colors.lightText};
line-height: 1.5;
}
`;
export default function Card({ variant, title, description }) {
return (
<StyledCard variant={variant}>
<h3 className="card-title">{title}</h3>
<p className="card-description">{description}</p>
</StyledCard>
);
}
Step 2: Use the Card in App
In App.js:
import Card from './Card';
function App() {
return (
<ThemeProvider theme={theme}>
<div style={{ padding: '20px', maxWidth: '600px', margin: '0 auto' }}>
<Card
title="Basic Card"
description="A simple card with default styling."
/>
<Card
variant="elevated"
title="Elevated Card"
description="A card with extra shadow for emphasis."
/>
</div>
</ThemeProvider>
);
}
This card adapts to the theme, supports variants, and has interactive hover effects—all with clean, maintainable code.
Conclusion
Styled Components transforms React styling by merging CSS into components, offering scoped styles, dynamic theming, and seamless integration with React’s ecosystem. By following best practices like component focus, theming, and responsive design, you can build beautiful, maintainable UIs that scale with your application.
Whether you’re working on a small project or a large enterprise app, Styled Components simplifies styling and empowers you to create consistent, dynamic interfaces with ease.