FrontendIntermediate
12 min readNov 24, 2025
Reusable Component Architecture for Vue & Next.js
Build scalable component libraries using atomic design principles with practical examples for Vue and React.
R
Rithy Tep
Author
Atomic Design Principles
Breaking down UI into atoms, molecules, organisms, templates, and pages creates maintainable component systems.
Atoms: Base Components
// components/atoms/Button.vue <script setup lang="ts"> interface Props { variant?: 'primary' | 'secondary' | 'danger' size?: 'sm' | 'md' | 'lg' disabled?: boolean } const props = withDefaults(defineProps<Props>(), { variant: 'primary', size: 'md', disabled: false }) </script> <template> <button :class="[ 'btn', `btn-${variant}`, `btn-${size}`, { 'btn-disabled': disabled } ]" :disabled="disabled" > <slot /> </button> </template>
Molecules: Combined Components
// components/molecules/SearchInput.tsx import { Input } from '@/components/atoms/Input' import { Button } from '@/components/atoms/Button' interface SearchInputProps { onSearch: (query: string) => void placeholder?: string } export function SearchInput({ onSearch, placeholder }: SearchInputProps) { const [query, setQuery] = useState('') return ( <div className="flex gap-2"> <Input value={query} onChange={(e) => setQuery(e.target.value)} placeholder={placeholder} onKeyDown={(e) => e.key === 'Enter' && onSearch(query)} /> <Button onClick={() => onSearch(query)}> Search </Button> </div> ) }
Props Contracts with TypeScript
// types/components.ts export interface CardProps { title: string description?: string image?: string action?: { label: string onClick: () => void } variant?: 'default' | 'highlighted' } // components/Card.tsx export function Card({ title, description, image, action, variant = 'default' }: CardProps) { return ( <div className={`card card-${variant}`}> {image && <img src={image} alt={title} />} <h3>{title}</h3> {description && <p>{description}</p>} {action && ( <button onClick={action.onClick}> {action.label} </button> )} </div> ) }
Shared Component Library
// packages/ui/src/index.ts export { Button } from './Button' export { Input } from './Input' export { Card } from './Card' export { Modal } from './Modal' export type { ButtonProps, InputProps, CardProps, ModalProps } from './types'
Best Practices
- •Single Responsibility - Each component does one thing well
- •Prop Validation - Always define TypeScript interfaces
- •Composition over Inheritance - Use slots/children for flexibility
- •Consistent Naming - Follow team conventions
- •Documentation - Use Storybook or similar tools
#Vue.js#React#Next.js#Components#Atomic Design