Guide for implementing React compound component patterns with dot notation in this codebase. Use when creating new UI components that have multiple related sub-components, building forms, dashboards, or pages with distinct sections, or when refactoring components that have complex prop drilling. Activates for component composition, context providers, reusable UI patterns.
70
88%
Does it follow best practices?
Impact
—
No eval scenarios have been run
Passed
No known issues
Use compound components when a component has multiple related sub-parts that need shared state — skip for simple 1-2 prop components or one-offs that won't be reused.
This pattern requires 'use client' — createContext, useContext, and useState are client-only.
children can still be a Server Component (the "donut" pattern).Full mechanics (donut pattern, why sub-components are themselves client, fix options): references/nextjs-server-client-boundary.md.
Create context, root, and sub-components in a single file:
'use client';
import React, { createContext, useContext, type PropsWithChildren } from 'react';
// --- Context ---
interface MyComponentContextType {
sharedState: string;
onAction?: (value: string) => void;
}
const MyComponentContext = createContext<MyComponentContextType | null>(null);
function useMyComponentContext() {
const context = useContext(MyComponentContext);
if (!context) throw new Error('MyComponent compound components must be used within a MyComponent');
return context;
}
// --- Sub-components ---
const MyComponentHeader: React.FC<{ title: string; subtitle?: string }> = ({ title, subtitle }) => (
<header>
<h1>{title}</h1>
{subtitle && <p>{subtitle}</p>}
</header>
);
MyComponentHeader.displayName = 'MyComponentHeader';
const MyComponentContent: React.FC<PropsWithChildren> = ({ children }) => {
const { sharedState } = useMyComponentContext();
return <div><span>State: {sharedState}</span>{children}</div>;
};
MyComponentContent.displayName = 'MyComponentContent';
const MyComponentFooter: React.FC<PropsWithChildren> = ({ children }) => (
<footer>{children}</footer>
);
MyComponentFooter.displayName = 'MyComponentFooter';
// --- Root ---
interface MyComponentOwnProps {
sharedState: string;
onAction?: (value: string) => void;
className?: string;
}
const MyComponent: React.FC<PropsWithChildren<MyComponentOwnProps>> & {
Header: typeof MyComponentHeader;
Content: typeof MyComponentContent;
Footer: typeof MyComponentFooter;
} = ({ children, sharedState, onAction, className }) => (
<MyComponentContext.Provider value={{ sharedState, onAction }}>
<div className={className}>{children}</div>
</MyComponentContext.Provider>
);
MyComponent.displayName = 'MyComponent';
MyComponent.Header = MyComponentHeader;
MyComponent.Content = MyComponentContent;
MyComponent.Footer = MyComponentFooter;
export default MyComponent;components/
MyComponent/
MyComponent.tsx # Main component with all sub-components
MyComponent.module.scss # Styles
index.tsx # Re-exportsindex.tsx:
import MyComponent from './MyComponent';
export default MyComponent;This codebase already ships several. Example:
import Dashboard from '@/components/Dashboard';
<Dashboard onPlayerClick={handlePlayerClick}>
<Dashboard.Header title="Team Name" subtitle="Manager Name" ctaText="Optimize" ctaHref="/optimize" />
<Dashboard.Stats stats={[{ title: 'GW20', value: 65, description: 'Rank: 1,234' }]} />
<Dashboard.PerformanceChart data={historyData} currentGameweek={20} />
<Dashboard.TeamGrid title="Starting XI" players={startingXI} />
<Dashboard.HotPlayers players={hotPlayers} />
</Dashboard>Before composing Team, Dashboard, PlayersPage, AuthForm, or UIKit/Modal, get the exact sub-component names and props from references/existing-components.md — don't guess them (e.g. it's Dashboard.Deadline/.GameweekInfo/.AuthCTA and Team.Summary/Team.Position type="goa|def|mid|fwd"). Open the component's own file to confirm anything not listed there.
children ?? defaultContent fallback in root when refactoring.Root.SubComponent = ...)index.tsx for clean exports