Component Patterns
Three types of components, each with a clear role.
1. Page Components
Location: modules/*/pages/
Compose features into a complete view. No direct data fetching - receive data via props from SSR or use hooks.
// modules/stories/pages/StoryPage.tsx
export function StoryPage({ story, error, ogImage, excerpt }: StoryPageProps) {
if (error) return <ErrorState error={error} />;
return (
<>
<StoryMeta story={story} ogImage={ogImage} excerpt={excerpt} />
<StoryLayout>
<StoryContent story={story} />
<StoryEngagement />
</StoryLayout>
</>
);
}Pattern: Composition only. Handles error/loading states. Delegates everything else.
2. Feature Components
Location: modules/*/components/
Implement specific functionality. May use hooks for logic. Scoped to their module.
// modules/engagement/components/ReactionBar.tsx
export function ReactionBar({ reactions, onToggle }: ReactionBarProps) {
return (
<div className="flex gap-2">
{reactions.map(r => (
<ReactionButton key={r.type} reaction={r} onToggle={onToggle} />
))}
</div>
);
}Pattern: Receives data via props. Calls callbacks for actions. Testable in isolation.
3. Shared Components
Location: shared/components/
Generic, reusable UI. No business logic. Used by 2+ modules.
// shared/components/Button.tsx
export function Button({ variant, size, children, ...props }: ButtonProps) {
return (
<button className={getButtonStyles(variant, size)} {...props}>
{children}
</button>
);
}Pattern: Pure presentation. Configurable via props. No knowledge of domain.