Take full ownership of your UI development with a custom-built solution
Full Design Control
Own every design decision and API surface, tailoring components exactly to your project's specific needs without compromise.
No Dependency Risk
Eliminate risks from third-party library roadmaps, deprecations, or breaking changes in external dependencies.
Consistent Styling
Ensure uniform styling across all applications with a single source of truth for every component's appearance.
Easy Maintenance
Keep styles organized and maintainable, avoiding the chaos of scattered styling throughout your codebase.
<h3>Installing styled-components</h3><pre><code>npm install styled-components # or yarn add styled-components</code></pre><p>For TypeScript projects, install the type definitions:</p><pre><code>npm install -D @types/styled-components</code></pre>
<h3>Dynamic Styles with Props</h3><p>Pass props to styled components to create dynamic, adaptable styles. This is one of the most powerful features when building <a href="/resources/guides/web-development/building-reusable-ui-components-with-react-hooks/">reusable UI components</a>:</p><pre><code>const Button = styled.button<ButtonProps>` background-color: ${props => props.variant === 'primary' ? '#3498db' : '#95a5a6'}; padding: ${props => props.size === 'large' ? '12px 24px' : '8px 16px'}; opacity: ${props => props.disabled ? 0.6 : 1}; `; interface ButtonProps { variant?: 'primary' | 'secondary'; size?: 'small' | 'medium' | 'large'; disabled?: boolean; }</code></pre><p><strong>Transient Props:</strong> Use the <code>$</code> prefix to prevent props from being passed to the underlying DOM element:</p><pre><code>const Button = styled.button` transform: ${props => props.$isActive ? 'scale(1.05)' : 'scale(1)'}; `; // $isActive won't appear in the DOM</code></pre>
<h3>Creating Your Theme System</h3><p>Design a centralized theme with design tokens that cascade through all components. Following patterns from <a href="https://dev.to/joshuawasike/design-systems-in-action-building-a-reusable-component-library-with-react-3peb" target="_blank" rel="noopener">DEV Community's component library guide</a>:</p><pre><code>// src/theme/index.ts export const theme = { colors: { primary: '#3498db', secondary: '#2ecc71', accent: '#e74c3c', background: '#ffffff', surface: '#f8f9fa', text: '#2c3e50', textMuted: '#95a5a6', border: '#e0e0e0', error: '#e74c3c', success: '#2ecc71', warning: '#f39c12', }, spacing: { xs: '4px', small: '8px', medium: '16px', large: '24px', xl: '32px', xxl: '48px', }, typography: { fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif", sizes: { small: '12px', medium: '14px', base: '16px', large: '18px', xl: '24px', xxl: '32px', }, weights: { normal: 400, medium: 500, bold: 700, }, }, breakpoints: { mobile: '480px', tablet: '768px', desktop: '1024px', wide: '1200px', }, shadows: { small: '0 2px 4px rgba(0,0,0,0.1)', medium: '0 4px 8px rgba(0,0,0,0.12)', large: '0 8px 16px rgba(0,0,0,0.15)', }, borderRadius: { small: '4px', medium: '8px', large: '12px', round: '50%', }, } as const; export type Theme = typeof theme;</code></pre>
Start with atomic components that form the foundation of your library
Button
Multiple variants (primary, secondary, outline, ghost), size variations, disabled state, loading spinner, full-width option.
Input
Consistent focus states, error state styling, label integration, placeholder styling, helper text support.
Card
Shadow and border options, padding variations, header and footer slots, hover interactions, clickable variants.
Typography
Consistent heading hierarchy, body text, caption styles, all tied to theme typography tokens.
interface ButtonProps { variant?: 'primary' | 'secondary' | 'outline' | 'ghost'; size?: 'small' | 'medium' | 'large'; fullWidth?: boolean; disabled?: boolean; isLoading?: boolean; }
const variantStyles = {
primary: css background-color: ${props => props.theme.colors.primary}; color: white; border: none; ,
secondary: css background-color: ${props => props.theme.colors.secondary}; color: white; border: none; ,
outline: css background-color: transparent; color: ${props => props.theme.colors.primary}; border: 2px solid ${props => props.theme.colors.primary}; ,
ghost: css background-color: transparent; color: ${props => props.theme.colors.text}; border: none; ,
};
const sizeStyles = {
small: css padding: 6px 12px; font-size: 12px; ,
medium: css padding: 10px 20px; font-size: 14px; ,
large: css padding: 14px 28px; font-size: 16px; ,
};
export const Button = styled.button<ButtonProps>` display: inline-flex; align-items: center; justify-content: center; gap: 8px; border-radius: ${props => props.theme.borderRadius.medium}; font-weight: 500; cursor: pointer; transition: all 0.2s ease;
${props => variantStyles[props.variant || 'primary']} ${props => sizeStyles[props.size || 'medium']}
${props => props.fullWidth && csswidth: 100%;}
${props => props.disabled && css opacity: 0.6; cursor: not-allowed; }
&:hover:not(:disabled) { transform: translateY(-1px); box-shadow: ${props => props.theme.shadows.medium}; } `;
// Usage // <Button variant="primary" size="large" isLoading>Click Me</Button></code></pre>
<h3>Installing Storybook</h3><p>Add Storybook to your component library project for visual documentation and testing. Following the approach from <a href="https://dev.to/joshuawasike/design-systems-in-action-building-a-reusable-component-library-with-react-3peb" target="_blank" rel="noopener">DEV Community's design systems guide</a>:</p><pre><code>npx storybook@latest init # This detects your project type and sets up Storybook automatically</code></pre><p>After installation, you'll have:</p><ul><li>Storybook server running on port 6006</li><li>Example stories in <code>src/**/*.stories.tsx</code></li><li>Configuration in <code>.storybook/</code></li><li>Build command for static documentation site</li></ul>