React Component Patterns
Purpose
Define React + TypeScript + Tailwind conventions for Cooper's renderer process. Ensure consistent, accessible, and maintainable UI components.
When to Use
- Creating new React components in
src/renderer/ - Modifying existing UI components
- Adding hooks, context, or utilities
When NOT to Use
- Main process or preload changes
- Non-UI logic changes
Activation Rules
Step 1: Component Structure
// src/renderer/components/MyComponent.tsx
import React, { useState, useEffect } from 'react'
interface MyComponentProps {
// All props explicitly typed — no `any`
title: string
onAction: (id: string) => void
isDisabled?: boolean
}
export const MyComponent: React.FC<MyComponentProps> = ({ title, onAction, isDisabled = false }) => {
// Hooks at top
const [state, setState] = useState<string>('')
// Effects after state
useEffect(() => {
// side effects
}, [])
// Event handlers
const handleClick = () => {
onAction(state)
}
// Render
return (
<div className="flex items-center gap-2 p-2">
<span className="text-sm font-medium">{title}</span>
<button
disabled={isDisabled}
className="px-3 py-1 rounded bg-blue-600 text-white hover:bg-blue-700 disabled:opacity-50"
>
Action
</button>
</div>
)
}
Step 2: Styling Rules
- Tailwind utility classes — Primary styling method
- CSS modules — Only when Tailwind can't express it
- No inline styles — Use Tailwind or CSS modules
- Theme-aware — Use Cooper's ThemeContext for colors when applicable
- Dark mode — All components must work in dark theme (Cooper's default)
Step 3: State Management
- React Context + hooks — No Redux or external state libraries
- Custom hooks — Extract reusable logic into
src/renderer/hooks/ - Interfaces — All types in
src/renderer/types/
Hook pattern:
// src/renderer/hooks/useSessionManager.ts
export function useSessionManager() {
const [sessions, setSessions] = useState<Session[]>([]);
const createSession = async (model: string) => {
const session = await window.electronAPI.copilot.createSession(model);
setSessions((prev) => [...prev, session]);
return session;
};
return { sessions, createSession };
}
Step 4: IPC Integration
When components need backend data:
// ✅ Good: use the typed preload bridge
const data = await window.electronAPI.copilot.getSession(sessionId)
// ❌ Bad: direct Node.js access
const data = require('electron').ipcRenderer.invoke(...) // NEVER
Step 5: Accessibility
- Semantic HTML elements (
button,nav,main, notdivfor everything) - Keyboard navigation support
- ARIA labels for interactive elements
- Focus management for modals and dialogs
Cooper-Specific Conventions
- Components directory:
src/renderer/components/ - Hooks directory:
src/renderer/hooks/ - Types directory:
src/renderer/types/ - Context directory:
src/renderer/context/ - Themes directory:
src/renderer/themes/ - Test files:
tests/components/<Component>.test.tsx
Success Criteria
- Props interface defined (no
any) - Tailwind for styling (no inline styles)
- Dark mode compatible
- Keyboard accessible
- Custom hooks extracted for reusable logic
Related Skills
- electron-ipc-patterns — For IPC calls from components
- test-fixing — For component tests
Source: CooperAgent/cooper — distributed by TomeVault.