React Component Style Guide
Component Structure
Section titled “Component Structure”Base Components
Section titled “Base Components”- Use functional components with TypeScript (
React.FC<Props>) - Export both the component and its props interface
- Group related components in a single file when they share common functionality
interface MyComponentProps { prop1: string; prop2?: number;}
const MyComponent: FC<MyComponentProps> = ({ prop1, prop2 }) => { return <div>{/* ... */}</div>;};
export { MyComponent };export type { MyComponentProps };Styling
Section titled “Styling”- Use Material-UI’s
styledAPI for base styling - Leverage the theme for consistent styling
- Use
sxprop for component-specific overrides - Define hover states and transitions in the
sxprop
const StyledComponent = styled(Paper)(({ theme }) => ({ ...theme.typography.body2, padding: theme.spacing(2), borderRadius: '8px',}))Props Interface
Section titled “Props Interface”- Define clear, descriptive interfaces for props
- Use JSDoc comments for complex props
- Make props optional when they have sensible defaults
- Use discriminated unions for components with multiple states
interface ComponentProps { /** Image URL for the avatar */ avatar?: string name: string onAction?: () => void}Testing
Section titled “Testing”Story-Driven Development
Section titled “Story-Driven Development”- Create stories first using Storybook
- Use stories in tests via
composeStory - Define a default export with component metadata
- Include multiple stories for different states
const meta: Meta<typeof MyComponent> = { title: "Category/Component", component: MyComponent, parameters: { layout: "centered" }, tags: ["autodocs"], render: (args) => ( <Box sx={{ width: "256px" }}> <MyComponent {...args} /> </Box> )};Test Structure
Section titled “Test Structure”- Test component rendering
- Test user interactions
- Use
renderWithProvidersfor components requiring store/router context - Test both success and error paths
- Use meaningful test descriptions
test("Component handles user interactions", async () => { const user = userEvent.setup(); renderWithProviders(<Component />);
// Test initial render const element = screen.getByTestId("component"); expect(element).toBeInTheDocument();
// Test interactions await user.click(element); // Assert expected outcomes});Event Handling
Section titled “Event Handling”Click Events
Section titled “Click Events”- Use descriptive handler names (
handleClick,handleSubmit) - Prevent event bubbling when needed with
stopPropagation() - Separate complex logic into helper functions
const handleClick = (event: React.MouseEvent) => { event.stopPropagation() // Handle click logic}State Management
Section titled “State Management”- Use Redux for global state
- Use local state for UI-specific concerns
- Leverage selectors for derived state
- Handle loading and error states explicitly
Accessibility
Section titled “Accessibility”Required Attributes
Section titled “Required Attributes”- Use semantic HTML elements
- Include
data-testidfor testing - Add ARIA labels where necessary
- Ensure keyboard navigation support
Visual Feedback
Section titled “Visual Feedback”- Implement hover states
- Show selected states clearly
- Use consistent focus indicators
- Provide loading states
Performance
Section titled “Performance”Optimization
Section titled “Optimization”- Avoid unnecessary re-renders
- Use proper dependency arrays in hooks
- Implement virtualization for long lists
- Lazy load components when appropriate
Best Practices
Section titled “Best Practices”- Keep components focused and single-purpose
- Extract reusable logic into custom hooks
- Use TypeScript for type safety
- Follow consistent naming conventions
Error Handling
Section titled “Error Handling”User Feedback
Section titled “User Feedback”- Use snackbars for notifications
- Handle loading states gracefully
- Provide meaningful error messages
- Include recovery actions where possible
Error Boundaries
Section titled “Error Boundaries”- Implement error boundaries for critical components
- Log errors appropriately
- Provide fallback UI for error states
Component Organization
Section titled “Component Organization”Composite Components
Section titled “Composite Components”- Group related components in a single file when they share common base styling or functionality
- Export individual components and their types separately
- Use a base component (like
Card) that other variants extend
// Example structureconst BaseCard = styled(Paper)(/* ... */);const StaffCard: FC<StaffCardProps> = /* ... */;const GridCard: FC<GridCardProps> = /* ... */;
export { BaseCard, StaffCard, GridCard };export type { StaffCardProps, GridCardProps };Component Documentation
Section titled “Component Documentation”- Add JSDoc comments for component usage examples
- Document complex state management requirements
- Include prop interface documentation inline
/** * A campaign card component that displays status and metrics * * @usage * <CampaignCard * name="test campaign" * budget={100} * live={live} * onChangeStatus={changeStatus} * /> */Event Propagation
Section titled “Event Propagation”- Handle event bubbling explicitly
- Document event propagation behavior
- Use separate handlers for nested interactive elements
const handleMenuButtonClick = (event: React.MouseEvent<HTMLButtonElement>) => { event.stopPropagation() // Stop card click event handleClickMenu(event) // Handle menu open}Component State Organization
Section titled “Component State Organization”- Group related state declarations together
- Initialize all state at the top of the component
- Use descriptive state variable names
const [menuAnchor, setMenuAnchor] = useState<null | HTMLElement>(null)const [deleteConfirmationOpen, setDeleteConfirmationOpen] = useState(false)const openMenu = Boolean(menuAnchor) // Derived stateRedux Integration
Section titled “Redux Integration”- Use typed hooks for Redux operations
- Handle side effects in event handlers
- Keep Redux operations isolated from UI logic
const selectedGridId = useAppSelector(selectors.selectedGridId)const [dispatch, actions] = useAppDispatch()Styling
Section titled “Styling”Theme Integration
Section titled “Theme Integration”- Use theme values for colors, spacing, and typography
- Handle dark/light mode variations through theme
- Define interactive states using theme values
sx={{ color: (theme) => theme.palette.text.primary, background: (theme) => theme.palette.action.hover, border: isSelected ? (theme) => `2px solid ${theme.palette.primary.main}` : "2px solid transparent",}}Responsive Design
Section titled “Responsive Design”- Use Grid components for layout
- Handle text overflow appropriately
- Define mobile-first responsive styles
<Grid item xs zeroMinWidth> <Typography noWrap sx={{ overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap", }} > {content} </Typography></Grid>User Feedback
Section titled “User Feedback”Interactive States
Section titled “Interactive States”- Show loading states during async operations
- Provide visual feedback for selections
- Include hover and active states
sx={{ transition: "border-color 0.3s", "&:hover": { background: (theme) => theme.palette.action.hover, cursor: "pointer", }}}Confirmation Flows
Section titled “Confirmation Flows”- Use dialogs for destructive actions
- Provide clear feedback messages
- Include undo/cancel options
<DeleteConfirmationDialog isOpen={deleteConfirmationOpen} title={`${name}`} message={`Are you sure you want to remove ${name}?`} onRemove={handleRemove} onCancel={() => setDeleteConfirmationOpen(false)}/>