Your First Styled Component
Let’s build a complete example from scratch. This guide assumes you’ve already installed styled-static.
Import styled
Start by importing the styled function from styled-static:import { styled } from '@alex.radulescu/styled-static';
Create a styled component
Use the styled API to create a Button component: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.
Use the component
Use your styled component like any other React component: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:
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>
</>
);
}
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:
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:
- Extracts CSS — All template literals are parsed and CSS is extracted
- Generates class names — Each styled component gets a unique, scoped class
- Creates inline components — Components are generated as inline functions (no factory)
- 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