Skip to content
Back to skills

Styled Components

ASecurity

Implements CSS-in-JS styling with styled-components using tagged template literals, theming, and dynamic props. Use when wanting component-scoped CSS-in-JS, dynamic styling based on props, or theming support.

  • 4 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added September 10, 2026
designbashreactnode

Works with

  • cursor
  • cli

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro scans all 3 files and shows the line behind each finding

Scanned September 10, 2026

npx -y skills add mgd34msu/goodvibes-gemini --skill styled-components --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Styled Components?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Styled Components
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/mgd34msu-styled-components/badge)](https://www.skillsdirectory.com/skills/mgd34msu-styled-components)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: styled-components
description: Implements CSS-in-JS styling with styled-components using tagged template literals, theming, and dynamic props. Use when wanting component-scoped CSS-in-JS, dynamic styling based on props, or theming support.
---

# styled-components

CSS-in-JS library using tagged template literals to style React components.

## Quick Start

**Install:**
```bash
npm install styled-components
```

**Create styled components:**
```tsx
import styled from 'styled-components';

const Button = styled.button`
  padding: 12px 24px;
  border: none;
  border-radius: 8px;
  font-size: 16px;
  cursor: pointer;
  background: #3b82f6;
  color: white;

  &:hover {
    background: #2563eb;
  }
`;

const Container = styled.div`
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 16px;
`;

function App() {
  return (
    <Container>
      <Button>Click me</Button>
    </Container>
  );
}
```

## Props-Based Styling

### Transient Props ($ prefix)

Props prefixed with `$` are filtered from DOM:

```tsx
interface ButtonProps {
  $variant?: 'primary' | 'secondary';
  $size?: 'sm' | 'md' | 'lg';
}

const Button = styled.button<ButtonProps>`
  padding: ${props => {
    switch (props.$size) {
      case 'sm': return '8px 16px';
      case 'lg': return '16px 32px';
      default: return '12px 24px';
    }
  }};

  background: ${props => props.$variant === 'secondary' ? '#e5e7eb' : '#3b82f6'};
  color: ${props => props.$variant === 'secondary' ? '#1f2937' : 'white'};
`;

// Usage
<Button $variant="secondary" $size="lg">
  Secondary Button
</Button>
```

### Conditional Styles

```tsx
const Card = styled.div<{ $elevated?: boolean; $selected?: boolean }>`
  padding: 16px;
  border-radius: 8px;
  background: white;

  ${props => props.$elevated && `
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  `}

  ${props => props.$selected && `
    border: 2px solid #3b82f6;
  `}
`;
```

## Extending Styles

```tsx
const Button = styled.button`
  padding: 12px 24px;
  border: 2px solid #3b82f6;
  border-radius: 8px;
  background: transparent;
  color: #3b82f6;
  cursor: pointer;
`;

// Extend with additional styles
const PrimaryButton = styled(Button)`
  background: #3b82f6;
  color: white;
`;

const DangerButton = styled(Button)`
  border-color: #ef4444;
  color: #ef4444;

  &:hover {
    background: #ef4444;
    color: white;
  }
`;
```

## Polymorphic Components

Change the rendered element with `as`:

```tsx
const Button = styled.button`
  padding: 12px 24px;
  text-decoration: none;
  display: inline-block;
`;

// Render as anchor
<Button as="a" href="/about">About</Button>

// Render as Link (React Router)
<Button as={Link} to="/about">About</Button>
```

## Styling Existing Components

```tsx
import { Link } from 'react-router-dom';

// Component must accept and pass className
const StyledLink = styled(Link)`
  color: #3b82f6;
  text-decoration: none;

  &:hover {
    text-decoration: underline;
  }
`;

// Custom component
function CustomCard({ className, children }: { className?: string; children: React.ReactNode }) {
  return <div className={className}>{children}</div>;
}

const StyledCard = styled(CustomCard)`
  padding: 16px;
  border-radius: 8px;
`;
```

## Attrs

Attach default or computed attributes:

```tsx
const Input = styled.input.attrs<{ $size?: string }>(props => ({
  type: 'text',
  placeholder: props.placeholder || 'Enter text...',
}))`
  padding: ${props => props.$size === 'large' ? '16px' : '12px'};
  border: 1px solid #e5e7eb;
  border-radius: 6px;
`;

// Password input with overridden type
const PasswordInput = styled(Input).attrs({
  type: 'password',
  placeholder: 'Enter password',
})``;
```

## Animations

```tsx
import styled, { keyframes } from 'styled-components';

const fadeIn = keyframes`
  from {
    opacity: 0;
    transform: translateY(-10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
`;

const spin = keyframes`
  from {
    transform: rotate(0deg);
  }
  to {
    transform: rotate(360deg);
  }
`;

const FadeInDiv = styled.div`
  animation: ${fadeIn} 0.3s ease-out;
`;

const Spinner = styled.div`
  width: 24px;
  height: 24px;
  border: 2px solid #e5e7eb;
  border-top-color: #3b82f6;
  border-radius: 50%;
  animation: ${spin} 0.8s linear infinite;
`;
```

## Theming

### ThemeProvider

```tsx
import styled, { ThemeProvider } from 'styled-components';

const theme = {
  colors: {
    primary: '#3b82f6',
    secondary: '#6b7280',
    success: '#10b981',
    error: '#ef4444',
    background: '#ffffff',
    text: '#1f2937',
  },
  spacing: {
    xs: '4px',
    sm: '8px',
    md: '16px',
    lg: '24px',
    xl: '32px',
  },
  borderRadius: {
    sm: '4px',
    md: '8px',
    lg: '16px',
    full: '9999px',
  },
};

type Theme = typeof theme;

declare module 'styled-components' {
  export interface DefaultTheme extends Theme {}
}

// Access theme in components
const Button = styled.button`
  background: ${props => props.theme.colors.primary};
  padding: ${props => props.theme.spacing.md};
  border-radius: ${props => props.theme.borderRadius.md};
`;

// Wrap app
function App() {
  return (
    <ThemeProvider theme={theme}>
      <Button>Themed Button</Button>
    </ThemeProvider>
  );
}
```

### useTheme Hook

```tsx
import { useTheme } from 'styled-components';

function Component() {
  const theme = useTheme();

  return (
    <div style={{ color: theme.colors.primary }}>
      Using theme in regular component
    </div>
  );
}
```

### Dark Mode

```tsx
const lightTheme = {
  colors: {
    background: '#ffffff',
    text: '#1f2937',
    primary: '#3b82f6',
  },
};

const darkTheme = {
  colors: {
    background: '#1f2937',
    text: '#f9fafb',
    primary: '#60a5fa',
  },
};

function App() {
  const [isDark, setIsDark] = useState(false);

  return (
    <ThemeProvider theme={isDark ? darkTheme : lightTheme}>
      <AppContainer>
        <button onClick={() => setIsDark(!isDark)}>Toggle Theme</button>
      </AppContainer>
    </ThemeProvider>
  );
}
```

## Global Styles

```tsx
import { createGlobalStyle } from 'styled-components';

const GlobalStyle = createGlobalStyle`
  *,
  *::before,
  *::after {
    box-sizing: border-box;
  }

  body {
    margin: 0;
    font-family: system-ui, -apple-system, sans-serif;
    background: ${props => props.theme.colors.background};
    color: ${props => props.theme.colors.text};
  }

  a {
    color: ${props => props.theme.colors.primary};
  }
`;

function App() {
  return (
    <ThemeProvider theme={theme}>
      <GlobalStyle />
      <MainContent />
    </ThemeProvider>
  );
}
```

## CSS Helper

Share style fragments:

```tsx
import styled, { css } from 'styled-components';

const flexCenter = css`
  display: flex;
  align-items: center;
  justify-content: center;
`;

const truncate = css`
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
`;

const Card = styled.div`
  ${flexCenter}
  padding: 16px;
`;

const Title = styled.h2`
  ${truncate}
  max-width: 200px;
`;
```

## Component Selectors

Reference other styled components:

```tsx
const Icon = styled.span`
  color: #6b7280;
  transition: color 0.2s;
`;

const Button = styled.button`
  display: flex;
  align-items: center;
  gap: 8px;

  &:hover ${Icon} {
    color: #3b82f6;
  }
`;

// Usage
<Button>
  <Icon>+</Icon>
  Add Item
</Button>
```

## Best Practices

1. **Define outside render** - Prevent recreation on each render
2. **Use transient props** - Prefix with `$` to avoid DOM warnings
3. **Extend over duplicate** - Use `styled(Component)` for variants
4. **Type your theme** - Extend `DefaultTheme` for autocomplete
5. **Colocate with components** - Keep styles near their component

## Common Patterns

### Responsive Styles

```tsx
const breakpoints = {
  sm: '640px',
  md: '768px',
  lg: '1024px',
  xl: '1280px',
};

const Container = styled.div`
  padding: 16px;

  @media (min-width: ${breakpoints.md}) {
    padding: 24px;
  }

  @media (min-width: ${breakpoints.lg}) {
    padding: 32px;
    max-width: 1200px;
    margin: 0 auto;
  }
`;
```

### Variants with Object Syntax

```tsx
const variants = {
  primary: css`
    background: #3b82f6;
    color: white;
  `,
  secondary: css`
    background: #e5e7eb;
    color: #1f2937;
  `,
  outline: css`
    background: transparent;
    border: 2px solid #3b82f6;
    color: #3b82f6;
  `,
};

const Button = styled.button<{ $variant?: keyof typeof variants }>`
  padding: 12px 24px;
  border: none;
  border-radius: 8px;
  cursor: pointer;

  ${props => variants[props.$variant || 'primary']}
`;
```

## Reference Files

- [references/ssr.md](references/ssr.md) - Server-side rendering
- [references/patterns.md](references/patterns.md) - Advanced patterns

Files in this skill

  • SKILL.md8.5 KB
  • references/patterns.md11.4 KB
  • references/ssr.md7.8 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.