--- name: atomic-design-templates description: Use when creating page layouts without real content. Templates define the skeletal structure of pages using organisms, molecules, and atoms. allowed-tools: - Bash - Read - Write - Edit - Glob - Grep --- # Atomic Design: Templates Master the creation of templates - page-level layouts that define content structure without actual content. Templates establish the skeletal structure that pages will use. ## What Are Templates? Templates are the page-level objects that place components into a layout and articulate the design's underlying content structure. They are: - **Composed of organisms**: Arrange organisms into page layouts - **Content-agnostic**: Use placeholder content, not real data - **Structural**: Define where content types will appear - **Reusable**: Same template can be used by multiple pages - **Responsive**: Handle all viewport sizes ## Common Template Types ### Marketing Templates - Landing page layouts - Homepage layouts - Product showcase layouts - About/Company layouts ### Application Templates - Dashboard layouts - Settings page layouts - Profile page layouts - List/Detail page layouts ### Content Templates - Blog post layouts - Article layouts - Documentation layouts - Help center layouts ### E-commerce Templates - Product listing layouts - Product detail layouts - Checkout layouts - Order confirmation layouts ## MainLayout Template Example ### Complete Implementation ```typescript // templates/MainLayout/MainLayout.tsx import React from 'react'; import { Header, type HeaderProps } from '@/components/organisms/Header'; import { Footer, type FooterProps } from '@/components/organisms/Footer'; import styles from './MainLayout.module.css'; export interface MainLayoutProps { /** Header configuration */ headerProps: HeaderProps; /** Footer configuration */ footerProps: FooterProps; /** Main content */ children: React.ReactNode; /** Show breadcrumbs */ showBreadcrumbs?: boolean; /** Breadcrumb component */ breadcrumbs?: React.ReactNode; /** Maximum content width */ maxWidth?: 'sm' | 'md' | 'lg' | 'xl' | 'full'; /** Page background color */ background?: 'white' | 'gray' | 'primary'; } export const MainLayout: React.FC = ({ headerProps, footerProps, children, showBreadcrumbs = false, breadcrumbs, maxWidth = 'lg', background = 'white', }) => { return (
{showBreadcrumbs && breadcrumbs && (
{breadcrumbs}
)}
{children}
); }; MainLayout.displayName = 'MainLayout'; ``` ```css /* templates/MainLayout/MainLayout.module.css */ .layout { display: flex; flex-direction: column; min-height: 100vh; } .main { flex: 1; display: flex; flex-direction: column; } .breadcrumbs { padding: 16px 24px; background-color: var(--color-neutral-50); border-bottom: 1px solid var(--color-neutral-200); } .content { flex: 1; margin: 0 auto; padding: 24px; width: 100%; } /* Max Width Variants */ .max-sm { max-width: 640px; } .max-md { max-width: 768px; } .max-lg { max-width: 1024px; } .max-xl { max-width: 1280px; } .max-full { max-width: 100%; } /* Background Variants */ .bg-white { background-color: var(--color-white); } .bg-gray { background-color: var(--color-neutral-50); } .bg-primary { background-color: var(--color-primary-50); } /* Responsive adjustments */ @media (max-width: 768px) { .content { padding: 16px; } } ``` ## DashboardLayout Template Example ```typescript // templates/DashboardLayout/DashboardLayout.tsx import React, { useState } from 'react'; import { Header } from '@/components/organisms/Header'; import { Sidebar, type SidebarProps } from '@/components/organisms/Sidebar'; import styles from './DashboardLayout.module.css'; export interface DashboardLayoutProps { /** Header props */ headerProps: { logo: React.ReactNode; user?: { name: string; email: string; avatar?: string }; onLogout?: () => void; }; /** Sidebar props */ sidebarProps: SidebarProps; /** Main content */ children: React.ReactNode; /** Page title */ pageTitle?: string; /** Page description */ pageDescription?: string; /** Page actions (buttons, etc.) */ pageActions?: React.ReactNode; /** Sidebar initially collapsed */ sidebarCollapsed?: boolean; } export const DashboardLayout: React.FC = ({ headerProps, sidebarProps, children, pageTitle, pageDescription, pageActions, sidebarCollapsed = false, }) => { const [isCollapsed, setIsCollapsed] = useState(sidebarCollapsed); return (
{/* Top Header */}
{/* Sidebar */} setIsCollapsed(!isCollapsed)} /> {/* Main Content Area */}
{/* Page Header */} {(pageTitle || pageActions) && (
{pageTitle &&

{pageTitle}

} {pageDescription && (

{pageDescription}

)}
{pageActions && (
{pageActions}
)}
)} {/* Page Content */}
{children}
); }; DashboardLayout.displayName = 'DashboardLayout'; ``` ```css /* templates/DashboardLayout/DashboardLayout.module.css */ .layout { display: flex; flex-direction: column; min-height: 100vh; } .body { display: flex; flex: 1; } .main { flex: 1; display: flex; flex-direction: column; overflow-x: hidden; background-color: var(--color-neutral-50); } .pageHeader { display: flex; justify-content: space-between; align-items: flex-start; gap: 24px; padding: 24px; background-color: var(--color-white); border-bottom: 1px solid var(--color-neutral-200); } .titleSection { flex: 1; } .pageTitle { margin: 0; font-size: 24px; font-weight: 600; color: var(--color-neutral-900); } .pageDescription { margin: 4px 0 0; font-size: 14px; color: var(--color-neutral-500); } .pageActions { display: flex; gap: 12px; flex-shrink: 0; } .content { flex: 1; padding: 24px; overflow-y: auto; } @media (max-width: 768px) { .pageHeader { flex-direction: column; align-items: stretch; } .pageActions { margin-top: 16px; } .content { padding: 16px; } } ``` ## AuthLayout Template Example ```typescript // templates/AuthLayout/AuthLayout.tsx import React from 'react'; import styles from './AuthLayout.module.css'; export interface AuthLayoutProps { /** Logo element */ logo: React.ReactNode; /** Page title */ title: string; /** Page subtitle */ subtitle?: string; /** Form content */ children: React.ReactNode; /** Footer content (links, etc.) */ footer?: React.ReactNode; /** Background image URL */ backgroundImage?: string; /** Show decorative side panel */ showSidePanel?: boolean; /** Side panel content */ sidePanelContent?: React.ReactNode; } export const AuthLayout: React.FC = ({ logo, title, subtitle, children, footer, backgroundImage, showSidePanel = false, sidePanelContent, }) => { return (
{/* Side Panel (optional) */} {showSidePanel && (
{sidePanelContent}
)} {/* Main Content */}
{/* Logo */}
{logo}
{/* Header */}

{title}

{subtitle &&

{subtitle}

}
{/* Form Content */}
{children}
{/* Footer */} {footer &&
{footer}
}
); }; AuthLayout.displayName = 'AuthLayout'; ``` ```css /* templates/AuthLayout/AuthLayout.module.css */ .layout { display: flex; min-height: 100vh; } .sidePanel { display: none; width: 50%; background-color: var(--color-primary-600); background-size: cover; background-position: center; position: relative; } @media (min-width: 1024px) { .sidePanel { display: flex; align-items: center; justify-content: center; } } .sidePanelContent { padding: 48px; color: var(--color-white); text-align: center; } .main { flex: 1; display: flex; align-items: center; justify-content: center; padding: 24px; background-color: var(--color-neutral-50); } .container { width: 100%; max-width: 400px; } .logo { text-align: center; margin-bottom: 32px; } .header { text-align: center; margin-bottom: 32px; } .title { margin: 0; font-size: 28px; font-weight: 700; color: var(--color-neutral-900); } .subtitle { margin: 8px 0 0; font-size: 16px; color: var(--color-neutral-500); } .content { background-color: var(--color-white); padding: 32px; border-radius: 12px; box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); } .footer { margin-top: 24px; text-align: center; font-size: 14px; color: var(--color-neutral-500); } .footer a { color: var(--color-primary-500); text-decoration: none; } .footer a:hover { text-decoration: underline; } ``` ## ProductListingLayout Template Example ```typescript // templates/ProductListingLayout/ProductListingLayout.tsx import React from 'react'; import { MainLayout, type MainLayoutProps } from '../MainLayout'; import styles from './ProductListingLayout.module.css'; export interface ProductListingLayoutProps { /** Main layout props */ layoutProps: Omit; /** Category title */ categoryTitle: string; /** Category description */ categoryDescription?: string; /** Product count */ productCount: number; /** Filter sidebar content */ filters: React.ReactNode; /** Sort/view controls */ controls: React.ReactNode; /** Product grid content */ products: React.ReactNode; /** Pagination content */ pagination?: React.ReactNode; /** Show filters on mobile */ mobileFiltersOpen?: boolean; /** Toggle mobile filters */ onToggleMobileFilters?: () => void; } export const ProductListingLayout: React.FC = ({ layoutProps, categoryTitle, categoryDescription, productCount, filters, controls, products, pagination, mobileFiltersOpen = false, onToggleMobileFilters, }) => { return ( {/* Category Header */}

{categoryTitle}

{categoryDescription && (

{categoryDescription}

)} {productCount} products
{/* Desktop Filters */} {/* Mobile Filters Overlay */} {mobileFiltersOpen && (

Filters

{filters}
)} {/* Main Content */}
{/* Controls Bar */}
{controls}
{/* Product Grid */}
{products}
{/* Pagination */} {pagination && (
{pagination}
)}
); }; ProductListingLayout.displayName = 'ProductListingLayout'; ``` ## BlogPostLayout Template Example ```typescript // templates/BlogPostLayout/BlogPostLayout.tsx import React from 'react'; import { MainLayout, type MainLayoutProps } from '../MainLayout'; import { Avatar } from '@/components/atoms/Avatar'; import { Text } from '@/components/atoms/Typography'; import styles from './BlogPostLayout.module.css'; export interface Author { name: string; avatar?: string; bio?: string; } export interface BlogPostLayoutProps { /** Main layout props */ layoutProps: Omit; /** Post title */ title: string; /** Post subtitle */ subtitle?: string; /** Featured image */ featuredImage?: string; /** Author information */ author: Author; /** Publication date */ publishedAt: string; /** Reading time */ readingTime?: string; /** Post categories/tags */ tags?: React.ReactNode; /** Main article content */ children: React.ReactNode; /** Table of contents */ tableOfContents?: React.ReactNode; /** Author bio card */ showAuthorBio?: boolean; /** Related posts */ relatedPosts?: React.ReactNode; /** Comments section */ comments?: React.ReactNode; /** Social share buttons */ shareButtons?: React.ReactNode; } export const BlogPostLayout: React.FC = ({ layoutProps, title, subtitle, featuredImage, author, publishedAt, readingTime, tags, children, tableOfContents, showAuthorBio = true, relatedPosts, comments, shareButtons, }) => { const formattedDate = new Date(publishedAt).toLocaleDateString('en-US', { month: 'long', day: 'numeric', year: 'numeric', }); return (
{/* Article Header */}
{tags &&
{tags}
}

{title}

{subtitle &&

{subtitle}

} {/* Author & Meta */}
{author.name} {formattedDate} {readingTime && ` ยท ${readingTime}`}
{shareButtons && (
{shareButtons}
)}
{/* Featured Image */} {featuredImage && (
{title}
)} {/* Content with Optional TOC */}
{/* Table of Contents (Desktop) */} {tableOfContents && ( )} {/* Main Content */}
{children}
{/* Author Bio */} {showAuthorBio && (
{author.name} {author.bio && {author.bio}}
)} {/* Share (Bottom) */} {shareButtons && (
{shareButtons}
)}
{/* Related Posts */} {relatedPosts && (

Related Posts

{relatedPosts}
)} {/* Comments */} {comments && (
{comments}
)}
); }; BlogPostLayout.displayName = 'BlogPostLayout'; ``` ## TwoColumnLayout Template Example ```typescript // templates/TwoColumnLayout/TwoColumnLayout.tsx import React from 'react'; import styles from './TwoColumnLayout.module.css'; export interface TwoColumnLayoutProps { /** Left column (usually main content) */ main: React.ReactNode; /** Right column (usually sidebar) */ sidebar: React.ReactNode; /** Sidebar position */ sidebarPosition?: 'left' | 'right'; /** Sidebar width */ sidebarWidth?: 'narrow' | 'medium' | 'wide'; /** Sticky sidebar */ stickySidebar?: boolean; /** Reverse on mobile (show sidebar first) */ reverseMobile?: boolean; /** Gap between columns */ gap?: 'sm' | 'md' | 'lg'; } export const TwoColumnLayout: React.FC = ({ main, sidebar, sidebarPosition = 'right', sidebarWidth = 'medium', stickySidebar = false, reverseMobile = false, gap = 'md', }) => { const layoutClass = [ styles.layout, styles[`sidebar-${sidebarPosition}`], styles[`width-${sidebarWidth}`], styles[`gap-${gap}`], reverseMobile && styles.reverseMobile, ] .filter(Boolean) .join(' '); const sidebarClass = [ styles.sidebar, stickySidebar && styles.sticky, ] .filter(Boolean) .join(' '); return (
{main}
); }; TwoColumnLayout.displayName = 'TwoColumnLayout'; ``` ## Best Practices ### 1. Use Placeholder Content ```typescript // GOOD: Template with placeholder content slots const ProductDetailLayout = ({ productGallery, // Placeholder for gallery component productInfo, // Placeholder for product details productTabs, // Placeholder for tabs relatedProducts, // Placeholder for recommendations }) => (
{productGallery}
{productInfo}
{productTabs}
{relatedProducts}
); // BAD: Template with hardcoded content const ProductDetailLayout = ({ product }) => (
{/* Too specific */}

{product.name}

{/* Real content */}

{product.description}

); ``` ### 2. Define Clear Content Areas ```typescript // GOOD: Clear, named content slots interface PageTemplateProps { header: React.ReactNode; hero?: React.ReactNode; main: React.ReactNode; sidebar?: React.ReactNode; footer: React.ReactNode; } // BAD: Generic children only interface PageTemplateProps { children: React.ReactNode; } ``` ### 3. Handle Responsive Layouts ```typescript // GOOD: Responsive considerations built-in const DashboardLayout = ({ sidebar, main }) => (
{main}
); // CSS handles responsive behavior // .sidebar { @media (max-width: 768px) { display: none; } } ``` ### 4. Keep Templates Thin ```typescript // GOOD: Template just arranges organisms const MainLayout = ({ header, main, footer }) => (
{header}
{main}
{footer}
); // BAD: Template with business logic const MainLayout = ({ userId }) => { const user = useUser(userId); // Fetching data const isAdmin = user?.role === 'admin'; // Business logic return (
{/* ... */}
); }; ``` ## Anti-Patterns to Avoid ### 1. Templates with Real Content ```typescript // BAD: Hardcoded real content const HomepageLayout = () => (

Welcome to Our Store

{/* Real content! */}

Shop our latest collection...

{/* Real content! */}
); // GOOD: Content passed as props/children const HomepageLayout = ({ heroTitle, heroDescription }) => (

{heroTitle}

{heroDescription}

); ``` ### 2. Over-Nested Templates ```typescript // BAD: Templates containing templates const AppLayout = () => ( {/* Too much nesting */} ); // GOOD: Choose appropriate template directly const AppPage = () => ( {/* Content */} ); ``` ### 3. Templates with Too Many Props ```typescript // BAD: Too many configuration options interface LayoutProps { showHeader: boolean; showFooter: boolean; showSidebar: boolean; sidebarPosition: 'left' | 'right'; headerVariant: 'default' | 'minimal' | 'transparent'; footerVariant: 'default' | 'minimal'; maxWidth: 'sm' | 'md' | 'lg' | 'xl'; // ... 20 more props } // GOOD: Create separate templates const FullPageLayout = ({ ... }) => { ... }; const MinimalLayout = ({ ... }) => { ... }; const SidebarLayout = ({ ... }) => { ... }; ``` ## Template Composition Patterns ### Nested Layouts ```typescript // Base layout for all pages const BaseLayout = ({ children }) => (
{children}
); // Marketing layout extending base const MarketingLayout = ({ children }) => (
{children}
); // App layout extending base const AppLayout = ({ children }) => (
{children}
); ``` ### Slot-Based Layouts ```typescript interface SlotLayoutProps { slots: { header?: React.ReactNode; sidebar?: React.ReactNode; main: React.ReactNode; footer?: React.ReactNode; }; } const SlotLayout: React.FC = ({ slots }) => (
{slots.header &&
{slots.header}
}
{slots.sidebar && }
{slots.main}
{slots.footer && }
); ``` ## When to Use This Skill - Creating page structure patterns - Building reusable layout components - Establishing consistent page architectures - Setting up responsive frameworks - Defining content slot patterns ## Related Skills - `atomic-design-fundamentals` - Core methodology overview - `atomic-design-organisms` - Building complex organisms - `atomic-design-integration` - Framework-specific patterns