Atomic Design Meets Feature-Based Architecture in Next.js: A Practical Guide
How to build scalable, maintainable frontends by combining two powerful patterns
The Problem with Pure Atomic Design
Atomic Design, introduced by Brad Frost, gives us a brilliant vocabulary for UI components: atoms, molecules, organisms, templates, and pages. It works beautifully for design systems and component libraries.
But here’s the catch: it doesn’t tell us where to put business logic.
When you’re building a real application with data fetching, state management, and complex user flows, you’ll inevitably ask: Where does my API call go? Which component owns this state?
Pure atomic design leaves these questions unanswered.
The Problem with Pure Feature-Based Architecture
Feature-based architecture organizes code by business domain: /features/auth, /features/checkout, /features/products. Each feature owns its components, hooks, and logic.
This works great for business logic, but creates a different problem: component duplication.
Get Buwaneka Sumanasekara’s stories in your inbox
Join Medium for free to get updates from this writer.
That Button component? You'll find three slightly different versions across three features. Your Card component exists in five places with minor variations.
Solution: Hybrid Approach
Defined feature based approach inside atomic design’s molecules and organisms. Thats it !!
How should I made the decision ?
Quick Decision Rules (If you not get it)
- “Is this component used in 2+ features?”
YES →shared/folder
NO →[feature]/folder - “Could this component be useful elsewhere?”
If unsure, start in feature folder
Move toshared/when actually needed (YAGNI principle) - “Is this component tightly coupled to feature logic?”
YES → Feature folder (even if potentially reusable)
NO → Consider shared folder
Sample Codes
ItemCard (feature molecule)
/*
* @file item-card.tsx
* @description
* @created Sat Jan 10 2026
* @author Buwaneka
*/
// molecules/product/item-card.tsx
// Feature-specific molecule - only used in product listings
import { OptimizedImage } from '@/components/atoms/optimized-image';
import { Typography } from '@/components/atoms/typography';
import { Badge } from '@/components/atoms/badge';
import { PriceTag } from '@/components/molecules/shared/price-tag';
export function ItemCard({ product }: ItemCardProps) {
return (
<div className="...">
<OptimizedImage src={product.image} alt={product.name} />
<Typography variant="h4">{product.name}</Typography>
<PriceTag price={product.price} discount={product.discount} />
{product.isNew && <Badge variant="new">New</Badge>}
</div>
);
}Header (shared organism)
/*
* @file header.tsx
* @description
* @created Sat Jan 10 2026
* @author Buwaneka
*/
// organisms/shared/header.tsx
// Shared organism - used across all pages
import { Logo } from '@/components/molecules/shared/logo-section';
import { SearchBar } from '@/components/molecules/shared/search-bar';
import { NavItem } from '@/components/molecules/shared/nav-item';
import { LanguageSwitcher } from '@/components/molecules/shared/language-switcher';
export function Header() {
return (
<header className="...">
<Logo />
<nav>
<NavItem href="/products">Products</NavItem>
<NavItem href="/categories">Categories</NavItem>
</nav>
<SearchBar />
<LanguageSwitcher />
</header>
);
}CartDrawer (Feature organism)
/*
* @file card-drawer.tsx
* @description
* @created Sat Jan 10 2026
* @author Buwaneka
*/
// organisms/cart/cart-drawer.tsx
// Feature-specific organism - only used in cart feature
import { Button } from '@/components/atoms/button';
import { Typography } from '@/components/atoms/typography';
import { CartItem } from '@/components/molecules/cart/cart-item';
import { CartSummaryRow } from '@/components/molecules/cart/cart-summary-row';
export function CartDrawer({ isOpen, onClose }: CartDrawerProps) {
return (
<aside className="...">
<Typography variant="h2">Your Cart</Typography>
<div className="cart-items">
{items.map(item => <CartItem key={item.id} {...item} />)}
</div>
<CartSummaryRow label="Subtotal" value={subtotal} />
<Button onClick={handleCheckout}>Checkout</Button>
</aside>
);
}






