21 de abril de 2025 • 4 min de leitura
From Atom to Page - Aplicando Atomic Design com Next.js
Um guia técnico com exemplos reais de como aplicar todos os níveis do Atomic Design em um projeto com React + Next.js.
🧩 Por que "From Atom to Page"?
Atomic Design é mais do que um jeito de nomear pastas.
É uma forma de pensar a arquitetura de componentes com clareza semântica, reaproveitamento consciente e manutenção fácil.
Neste artigo, você vai ver:
- Como estruturar seu projeto por nível (átomos, moléculas, organismos, templates e páginas)
- Como aplicar isso com Next.js (App Router) e Tailwind CSS
- Como escrever componentes realmente reutilizáveis — sem cair no buraco das props infinitas
🚫 O problema: componentes genéricos demais
<Card
title="Produto A"
description="Descrição"
icon={<Info />}
withActions
variant="info"
size="compact"
/>
O código parece reutilizável, mas:
- Fica acoplado demais à lógica interna
- Quebra facilmente em escala
- Fica difícil de manter e testar
✅ A solução: composição clara + Atomic Design
Átomos (Text + Button)
export const Text = ({ as: Tag = 'p', children }: {
as?: React.ElementType;
children: React.ReactNode;
}) => (
<Tag className="text-base text-zinc-700 dark:text-zinc-300">{children}</Tag>
);
export const Button = ({ children, ...props }: {
children: React.ReactNode;
} & React.ButtonHTMLAttributes<HTMLButtonElement>) => (
<button
className="px-4 py-2 rounded-md bg-blue-600 text-white hover:bg-blue-700 text-sm"
{...props}
>
{children}
</button>
);
Moléculas
export const CardHeader = ({ icon, title }: {
icon?: React.ReactNode;
title: React.ReactNode;
}) => (
<div className="flex items-center gap-3">
{icon && (
<span className="inline-flex items-center justify-center h-5 w-5 text-blue-500">
{icon}
</span>
)}
{title}
</div>
);
export const CardActions = ({ children }: { children: React.ReactNode }) => (
<div className="flex justify-end items-center">
{children}
</div>
);
Organismo
export const Card = ({
header,
children,
actions,
}: {
header: React.ReactNode;
children: React.ReactNode;
actions?: React.ReactNode;
}) => (
<div className="bg-zinc-800 dark:bg-zinc-900 rounded-xl shadow-lg/5 overflow-hidden">
<div className="divide-y divide-zinc-700">
<div className="p-6 flex justify-between items-start">
{header}
{actions && <div>{actions}</div>}
</div>
<div className="p-6">
{children}
</div>
</div>
</div>
);
Template (CardListTemplate)
export const CardListTemplate = ({ cards }: {
cards: { icon: React.ReactNode; title: string; description: string }[];
}) => (
<div className="grid gap-6 md:grid-cols-2">
{cards.map((card, idx) => (
<Card
key={idx}
header={<CardHeader icon={card.icon} title={<Text as="h3" variant="title">{card.title}</Text>} />}
actions={<CardActions><Button>See more</Button></CardActions>}
>
<Text variant="body">{card.description}</Text>
</Card>
))}
</div>
);
Página (app/produtos/page.tsx
)
import { Info, AlertTriangle } from 'lucide-react';
import { CardListTemplate } from '@/components/templates/CardListTemplate';
export default function ProdutosPage() {
return (
<main className="container mx-auto py-10">
<h1 className="text-3xl font-bold text-zinc-100 dark:text-zinc-100 mb-6">
Products
</h1>
<CardListTemplate
cards={[
{ title: 'Product A', description: 'Description A', icon: <Info size={20} /> },
{ title: 'Product B', description: 'Description B', icon: <AlertTriangle size={20} /> },
]}
/>
</main>
);
}
🎯 Conclusão
Atomic Design só funciona quando:
- Cada componente tem responsabilidade clara
- A composição vem antes da configuração
- O time fala a mesma linguagem semântica
Este exemplo mostra como escalar design system com estrutura de verdade, sem cair em abstrações frágeis.
Github
O repositório com todos os exemplos do artigo (e dos carrosséis) está disponível em:
👉 github.com/jlameira/from-atom-to-page