Skip to main content

Your First Styled Component

Let’s build a complete example from scratch. This guide assumes you’ve already installed styled-static.
1

Import styled

Start by importing the styled function from styled-static:
Button.tsx
import { styled } from '@alex.radulescu/styled-static';
2

Create a styled component

Use the styled API to create a Button component:
Button.tsx
import { styled } from '@alex.radulescu/styled-static';

const Button = styled.button`
  padding: 0.5rem 1rem;
  background: #3b82f6;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 1rem;
  font-weight: 500;
  transition: background 0.2s;

  &:hover {
    background: #2563eb;
  }

  &:active {
    transform: scale(0.98);
  }

  &:disabled {
    opacity: 0.5;
    cursor: not-allowed;
  }
`;
All CSS is extracted at build time. The :hover, :active, and :disabled styles use native CSS nesting.
3

Use the component

Use your styled component like any other React component:
Button.tsx
export function Demo() {
  return (
    <div>
      <Button onClick={() => alert('Clicked!')}>Click me</Button>
      <Button disabled>Disabled</Button>
    </div>
  );
}
That’s it! Your component is fully styled with static CSS. No runtime overhead.

Extend Components

One of the most powerful features is component extension. Create variants by extending base components:
import { styled } from '@alex.radulescu/styled-static';

// Base button
const Button = styled.button`
  padding: 0.5rem 1rem;
  border: none;
  border-radius: 4px;
  font-size: 1rem;
  cursor: pointer;
`;

// Primary variant (extends Button)
const PrimaryButton = styled(Button)`
  background: #3b82f6;
  color: white;

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

// Danger variant (extends Button)
const DangerButton = styled(Button)`
  background: #ef4444;
  color: white;

  &:hover {
    background: #dc2626;
  }
`;

// Usage
export function App() {
  return (
    <>
      <Button>Base</Button>
      <PrimaryButton>Primary</PrimaryButton>
      <DangerButton>Danger</DangerButton>
    </>
  );
}
CSS Cascade Order — When components are extended, classes are ordered correctly: base styles first, extension styles second, user className last.

Add Conditional Styles

Use the css helper for conditional styling:
import { styled, css } from '@alex.radulescu/styled-static';

// Base button
const Button = styled.button`
  padding: 0.5rem 1rem;
  background: #e5e7eb;
  border: none;
  border-radius: 4px;
  cursor: pointer;
`;

// Active state class
const activeClass = css`
  background: #3b82f6;
  color: white;
  outline: 2px solid #93c5fd;
`;

// Component with conditional styling
export function ToggleButton() {
  const [active, setActive] = useState(false);

  return (
    <Button
      className={active ? activeClass : ''}
      onClick={() => setActive(!active)}
    >
      {active ? 'Active' : 'Inactive'}
    </Button>
  );
}
The css helper generates a scoped class name that you can apply conditionally. All CSS is still extracted at build time.

Add Global Styles

Every app needs some global styles. Use createGlobalStyle to define them:
App.tsx
import { createGlobalStyle } from '@alex.radulescu/styled-static';
import { Button } from './Button';

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

  body {
    margin: 0;
    font-family: system-ui, -apple-system, sans-serif;
    background: #f9fafb;
    color: #111827;
  }

  :root {
    --color-primary: #3b82f6;
    --color-danger: #ef4444;
  }
`;

export function App() {
  return (
    <>
      <GlobalStyle />
      <div className="app">
        <h1>My App</h1>
        <Button>Click me</Button>
      </div>
    </>
  );
}
GlobalStyle renders nothing — it only injects CSS at build time. Render it once at your app root.

Create Type-Safe Variants

For components with multiple variants, use styledVariants for type safety:
import { styledVariants, css } from '@alex.radulescu/styled-static';

const Button = styledVariants({
  component: 'button',
  css: css`
    padding: 0.5rem 1rem;
    border: none;
    border-radius: 4px;
    font-weight: 500;
    cursor: pointer;
  `,
  variants: {
    color: {
      primary: css`
        background: #3b82f6;
        color: white;
      `,
      danger: css`
        background: #ef4444;
        color: white;
      `,
      ghost: css`
        background: transparent;
        border: 1px solid #e5e7eb;
      `,
    },
    size: {
      sm: css`
        font-size: 0.875rem;
        padding: 0.25rem 0.75rem;
      `,
      md: css`
        font-size: 1rem;
        padding: 0.5rem 1rem;
      `,
      lg: css`
        font-size: 1.125rem;
        padding: 0.75rem 1.5rem;
      `,
    },
  },
  defaultVariants: {
    color: 'primary',
    size: 'md',
  },
});

// Usage with full type safety
export function Demo() {
  return (
    <>
      <Button>Default (primary + md)</Button>
      <Button color="danger" size="lg">Large Danger</Button>
      <Button color="ghost" size="sm">Small Ghost</Button>
    </>
  );
}
Wrap CSS in css template literals to get syntax highlighting from the styled-components VSCode extension.

Add Animations

Create scoped animations with keyframes:
import { styled, keyframes } from '@alex.radulescu/styled-static';

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

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

export function LoadingSpinner() {
  return <Spinner />;
}
Animation names are automatically scoped (hashed) to avoid conflicts between components.

Complete Example

Here’s a complete example putting everything together:
App.tsx
import { useState } from 'react';
import {
  styled,
  css,
  createGlobalStyle,
  styledVariants,
  keyframes,
} from '@alex.radulescu/styled-static';

// Global styles
const GlobalStyle = createGlobalStyle`
  * { box-sizing: border-box; }
  body {
    margin: 0;
    font-family: system-ui, sans-serif;
    background: #f9fafb;
  }
`;

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

// Container
const Container = styled.div`
  max-width: 600px;
  margin: 2rem auto;
  padding: 2rem;
  animation: ${fadeIn} 0.3s ease-out;
`;

// Type-safe button with variants
const Button = styledVariants({
  component: 'button',
  css: css`
    padding: 0.5rem 1rem;
    border: none;
    border-radius: 4px;
    font-weight: 500;
    cursor: pointer;
    transition: all 0.2s;
  `,
  variants: {
    variant: {
      primary: css`background: #3b82f6; color: white;`,
      danger: css`background: #ef4444; color: white;`,
    },
  },
});

// Active state
const activeClass = css`
  outline: 2px solid #3b82f6;
  outline-offset: 2px;
`;

export function App() {
  const [active, setActive] = useState(false);

  return (
    <>
      <GlobalStyle />
      <Container>
        <h1>styled-static Demo</h1>
        <div style={{ display: 'flex', gap: '1rem' }}>
          <Button variant="primary" onClick={() => setActive(!active)}>
            Toggle Active
          </Button>
          <Button
            variant="danger"
            className={active ? activeClass : ''}
          >
            {active ? 'Active' : 'Inactive'}
          </Button>
        </div>
      </Container>
    </>
  );
}
You’re ready! You now know the core concepts of styled-static. Explore the API documentation to learn more.

What’s Happening Under the Hood?

When you build your app, the Vite plugin:
  1. Extracts CSS — All template literals are parsed and CSS is extracted
  2. Generates class names — Each styled component gets a unique, scoped class
  3. Creates inline components — Components are generated as inline functions (no factory)
  4. Bundles CSS — CSS is bundled into static files via Vite’s pipeline
The result? A tiny runtime (~45 bytes) and all your CSS in static files.

Learn More

Read about the build-time transformation in detail

Next Steps

Styling Components

Learn advanced styling techniques and patterns

Variants

Master type-safe variants with styledVariants and cssVariants

Theming

Add dark mode and custom themes to your app

API Reference

Explore the complete API documentation

Build docs developers (and LLMs) love