| name | coding-guidelines |
| description | Comprehensive React component coding guidelines, refactoring principles, and architectural patterns. **CRITICAL**: Focuses on patterns AI commonly fails to implement correctly, especially testability, props control, and component responsibility separation. Reference this skill when implementing or refactoring React components during Phase 2. |
Coding Guidelines - What AI Gets Wrong
This skill focuses on patterns AI commonly fails to implement correctly. Trust AI for syntax and structure, but scrutinize these critical areas where AI consistently makes mistakes.
⚠️ Critical: AI's Common Failures
1. Lack of Testability (Most Critical)
Pattern AI ALWAYS gets wrong: Creating components that control UI branches with internal state
function UserProfile({ userId }: { userId: string }) {
const [loading, setLoading] = useState(false)
const [error, setError] = useState<Error | null>(null)
const [user, setUser] = useState<User | null>(null)
if (loading) return <Spinner />
if (error) return <ErrorMessage error={error} />
if (!user) return <div>Not found</div>
return <div>{user.name}</div>
}
Why is this untestable?:
- Depends on internal state (
loading, error, user)
- To test each state, you must actually trigger those states
- Mocks and stubs become complex, tests become brittle
Correct pattern: Convert internal state to props, separate components by state
type UserProfileProps = {
user: User | null
}
function UserProfile({ user }: UserProfileProps) {
if (!user) return <div>Not found</div>
return <div>{user.name}</div>
}
test('displays Not found when user is null', () => {
render(<UserProfile user={null} />)
expect(screen.getByText('Not found')).toBeInTheDocument()
})
test('displays user name', () => {
const user = { name: 'Taro', id: '1' }
render(<UserProfile user={user} />)
expect(screen.getByText('Taro')).toBeInTheDocument()
})
Same applies to functions:
function validateUser() {
const user = getCurrentUser()
if (!user.email) return false
return true
}
function validateUser(user: User): boolean {
if (!user.email) return false
return true
}
2. Insufficient Props Control
Pattern AI ALWAYS gets wrong: Components hold internal state that cannot be controlled externally
function UserCard({ userId }: { userId: string }) {
const [user, setUser] = useState<User | null>(null)
useEffect(() => {
fetchUser(userId).then(setUser)
}, [userId])
return user ? <div>{user.name}</div> : <div>Loading...</div>
}
<UserCard userId="123" />
Correct pattern: Make everything controllable via props
type UserCardProps = {
user: User | null
isLoading?: boolean
error?: Error | null
}
function UserCard({ user, isLoading, error }: UserCardProps) {
if (isLoading) return <Spinner />
if (error) return <ErrorMessage error={error} />
if (!user) return <div>Not found</div>
return <div>{user.name}</div>
}
function UserPage() {
const { user, isLoading, error } = useUser('123')
return <UserCard user={user} isLoading={isLoading} error={error} />
}
test('loading state', () => {
render(<UserCard user={null} isLoading={true} />)
expect(screen.getByTestId('spinner')).toBeInTheDocument()
})
3. Insufficient Conditional Branch Extraction
Pattern AI ALWAYS gets wrong: Cramming multiple conditional branches into one component
function Dashboard() {
const { user, subscription, notifications } = useData()
return (
<div>
{/* Problem 1: user conditional branch */}
{user ? (
<div>
<h1>{user.name}</h1>
{/* Problem 2: subscription conditional branch */}
{subscription?.isPremium ? (
<PremiumBadge />
) : (
<FreeBadge />
)}
</div>
) : (
<LoginPrompt />
)}
{/* Problem 3: notifications conditional branch */}
{notifications.length > 0 ? (
<NotificationList items={notifications} />
) : (
<EmptyState />
)}
</div>
)
}
Correct pattern: Separate components for each conditional branch
type UserSectionProps = {
user: User | null
subscription: Subscription | null
}
function UserSection({ user, subscription }: UserSectionProps) {
if (!user) return <LoginPrompt />
return (
<div>
<h1>{user.name}</h1>
<SubscriptionBadge subscription={subscription} />
</div>
)
}
type SubscriptionBadgeProps = {
subscription: Subscription | null
}
function SubscriptionBadge({ subscription }: SubscriptionBadgeProps) {
if (subscription?.isPremium) return <PremiumBadge />
return <FreeBadge />
}
type NotificationSectionProps = {
notifications: Notification[]
}
function NotificationSection({ notifications }: NotificationSectionProps) {
if (notifications.length === 0) return <EmptyState />
return <NotificationList items={notifications} />
}
test('displays premium badge', () => {
const subscription = { isPremium: true }
render(<SubscriptionBadge subscription={subscription} />)
expect(screen.getByTestId('premium-badge')).toBeInTheDocument()
})
test('displays free badge', () => {
render(<SubscriptionBadge subscription={null} />)
expect(screen.getByTestId('free-badge')).toBeInTheDocument()
})
4. Mixing Data Fetching with UI Display
Pattern AI ALWAYS gets wrong: Data fetching with useEffect
'use client'
function UserProfile({ userId }: { userId: string }) {
const [user, setUser] = useState<User | null>(null)
const [loading, setLoading] = useState(true)
useEffect(() => {
fetch(`/api/users/${userId}`)
.then(res => res.json())
.then(data => {
setUser(data)
setLoading(false)
})
}, [userId])
if (loading) return <Spinner />
return <div>{user?.name}</div>
}
Correct pattern: Data fetching in Server Component, display in Presentational Component
async function UserProfileServer({ userId }: { userId: string }) {
const user = await fetchUser(userId)
return <UserProfile user={user} />
}
type UserProfileProps = {
user: User
}
function UserProfile({ user }: UserProfileProps) {
return <div>{user.name}</div>
}
<Suspense fallback={<Spinner />}>
<UserProfileServer userId="123" />
</Suspense>
test('displays user name', () => {
const user = { name: 'Taro', id: '1' }
render(<UserProfile user={user} />)
expect(screen.getByText('Taro')).toBeInTheDocument()
})
Refactoring Principles
Eight core principles guide component refactoring:
- Logic Extraction - Separate non-UI logic into utility files
- Presenter Pattern - Consolidate conditional text in presenter.ts
- Conditional UI Extraction - Extract conditional branches to components (CRITICAL)
- Naming and Structure - Use kebab-case directories, PascalCase files
- Props Control - All rendering controllable via props (CRITICAL)
- Avoid useEffect for Data - Use Server Components with async/await
- Avoid Over-Abstraction - Don't create unnecessary wrappers
- Promise Handling - Prefer .then().catch() over try-catch
CRITICAL marked principles are areas where AI ALWAYS makes mistakes.
Component Directory Structure
Key Rules
Directory Naming:
- Root:
kebab-case matching component name
- Entry point:
PascalCase file directly inside root
- Example:
read-only-editor/ReadOnlyEditor.tsx
Parent-Child Hierarchy:
- Child components in subdirectories under parent
- Clear ownership:
parent-component/child-component/grandchild-component/
- Import paths reflect relationships
Export Strategy:
- Root entry point is public API
- Re-export other files for external use
- Direct imports of subdirectory files limited to internal use
Quality Requirements
Non-Negotiable Standards
- Preserve external contracts - Don't change public APIs or behavior
- Run checks after all work -
bun run check:fix and bun run typecheck
- No new
any - Solve issues fundamentally, document when unavoidable
- No new ignores - No
@ts-ignore or // biome-ignore without reason
- Resolve warnings - Fix ESLint/Biome warnings, remove unnecessary ignores
- Improve type safety - Produce self-explanatory code (comments only for exceptional cases)
AI Weakness Checklist
Before considering implementation complete, verify AI didn't fall into these traps:
Testability ⚠️ (Most Critical)
Props Control ⚠️
Component Responsibility
Over-Abstraction
Type Safety
Code Examples
Quick Reference
Testable Component Pattern:
type ComponentProps = {
data: Data | null
isLoading?: boolean
error?: Error | null
}
function Component({ data, isLoading, error }: ComponentProps) {
if (isLoading) return <Spinner />
if (error) return <ErrorMessage error={error} />
if (!data) return <EmptyState />
return <Content data={data} />
}
Logic Extraction:
export const validateEmail = (email: string): boolean => {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)
}
Presenter Pattern:
export const getUserStatusText = (status: string): string => {
switch (status) {
case "active": return "Active"
case "inactive": return "Inactive"
default: return "Unknown"
}
}
Integration with Development Workflow
This skill is primarily referenced during Phase 2 (Implementation) when:
- Implementing new React components
- Refactoring existing components
- Extracting logic from components
- Organizing component directory structure
- Ensuring code quality standards
After implementation, code undergoes review in Phase 2 (Code Review) using Codex MCP.
Common Patterns
Server Component Pattern
export async function UserProfileServer({ userId }: { userId: string }) {
const user = await fetchUser(userId)
return <UserProfile user={user} />
}
<Suspense fallback={<Spinner />}>
<UserProfileServer userId={userId} />
</Suspense>
Presenter Pattern
export const getUserStatusText = (status: string): string => { }
export const getUserStatusColor = (status: string): string => { }
<Badge color={getUserStatusColor(status)}>
{getUserStatusText(status)}
</Badge>
Conditional UI Extraction
Summary: What to Watch For
AI will confidently write code that:
- Looks clean but is impossible to test (internal state dependencies)
- Works but can't be controlled from parent (no props control)
- Compiles but violates separation of concerns (data fetching + UI mixed)
- Is abstract but has no benefit (unnecessary wrappers)
Trust AI for:
- Syntax and TypeScript basics
- Import/export statements
- Basic component structure
Scrutinize AI for:
- Testability (internal state vs props)
- Component responsibility (one thing per component)
- Props control (can parent control all states?)
- Conditional branch extraction (separate components?)
When in doubt, ask: "Can I easily test this component's different states?"
If the answer is no, refactor until you can pass props to control each state independently.