Skip to content
Back to skills

React Best Practices

ASecurity

React best practices for Benefriches (Vite + Redux). Covers code quality, component patterns, state management, and performance. Use when writing, reviewing, or refactoring React components, debugging slow interactions, or implementing Redux patterns.

  • 45 stars
  • 0 votes
  • 0 copies
  • 3 views
  • Added February 7, 2026
developmentjavascripttypescriptgojavareactnodedebuggingrefactoringapiperformance

Works with

  • cli
  • api

Security analysis

A100/100

Pro scans all 2 files and shows the line behind each finding

Scanned September 30, 2026

npx -y skills add incubateur-ademe/benefriches --skill react-best-practices --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of React Best Practices?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for React Best Practices
[![Security: A β€” Skills Directory](https://www.skillsdirectory.com/api/skills/incubateur-ademe-react-best-practices/badge)](https://www.skillsdirectory.com/skills/incubateur-ademe-react-best-practices)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: react-best-practices
description: React best practices for Benefriches (Vite + Redux). Covers code quality, component patterns, state management, and performance. Use when writing, reviewing, or refactoring React components, debugging slow interactions, or implementing Redux patterns.
effort: medium
allowed-tools: Read, Grep, Glob
---

# React Best Practices for Benefriches

> **Guidelines for React 19+ SPA with Vite + Redux**
>
> **Philosophy**: Code quality and maintainability first, performance optimization when measured
>
> **Adapted for**: Client-side rendering with Redux + Clean Architecture

---

## When to Apply This Skill

Use these practices when:

- Writing new React components
- Designing component architecture
- Implementing Redux patterns (reducers, selectors, thunks)
- Reviewing code for quality or performance issues
- Refactoring existing React code
- Debugging slow interactions

---

## Categories by Priority

| Priority | Category                   | Focus Area                             |
| -------- | -------------------------- | -------------------------------------- |
| πŸ”΄       | **Code Quality**           | Readability, maintainability, SRP      |
| 🟠       | **Component Patterns**     | Container/Presentational, composition  |
| 🟑       | **State Management**       | Local-first, derived state, colocation |
| 🟒       | **Anti-Patterns**          | Common mistakes to avoid               |
| πŸ”΅       | **Bundle Optimization**    | Lazy loading, dynamic imports          |
| 🟣       | **Async Patterns**         | Parallel fetching, Suspense            |
| 🟀       | **Form Handling**          | react-hook-form patterns, DSFR         |
| ⬜       | **Accessibility**          | Keyboard nav, ARIA, focus management   |
| ⬛       | **Error Boundaries**       | Catch errors, prevent app crashes      |
| ⚫       | **Performance (Measure!)** | Only when needed, after profiling      |
| βšͺ       | **React 19 & Future**      | React Compiler, new APIs               |

---

## πŸ”΄ CRITICAL: Code Quality & Readability

| Practice               | Description                               |
| ---------------------- | ----------------------------------------- |
| Single Responsibility  | Each component does ONE thing well        |
| Component Size         | Keep components focused (< 200 lines)     |
| Descriptive Naming     | Clear names for components, hooks, props  |
| Props Destructuring    | Improve readability at function signature |
| Explicit over Implicit | Avoid magic values, use named constants   |
| Extract Custom Hooks   | Share logic via hooks, not copy-paste     |

### Benefriches Examples

- βœ… **ViewData pattern**: see [Containers and ViewData](../../../apps/web/AGENTS.md#containers-and-viewdata)
- βœ… **Container/Presentational**: Separation in `views/` folders
- βœ… **Clean Architecture**: Core has no framework dependencies

---

## 🟠 HIGH: Component Design Patterns

| Pattern                  | When to Use                                  |
| ------------------------ | -------------------------------------------- |
| Container/Presentational | Redux connection in `index.tsx`, pure render |
| Component Composition    | Prefer over deep prop drilling               |
| Children Pattern         | Flexible content injection                   |
| Custom Hooks             | Extract reusable stateful logic              |
| Render Props (rare)      | Dynamic child rendering needs                |

### Benefriches Already Follows

- βœ… Containers use one ViewData selector β€” see [Containers and ViewData](../../../apps/web/AGENTS.md#containers-and-viewdata)
- βœ… Presentational components receive all data via props
- βœ… Gateway pattern for external services

---

## 🟑 HIGH: State Management Principles

| Principle         | Description                                   |
| ----------------- | --------------------------------------------- |
| Local State First | Don't lift state unless truly shared          |
| Derived State     | Compute in selectors/render, don't store      |
| Colocate State    | Keep state close to where it's used           |
| Single Source     | One authoritative location per piece of data  |
| Immutability      | Always use `toSorted()`, spread, not `sort()` |

### Redux Specifics

- βœ… Derived values in selectors (not duplicated in state)
- βœ… Functional updates in reducers
- βœ… ViewData selector per container β€” see [Containers and ViewData](../../../apps/web/AGENTS.md#containers-and-viewdata)

---

## 🟒 HIGH: Anti-Patterns to Avoid

| Anti-Pattern             | Problem                    | Solution                         |
| ------------------------ | -------------------------- | -------------------------------- |
| Massive Components       | Hard to test/maintain      | Split into focused pieces        |
| Prop Drilling            | Coupling, maintenance      | Use composition or context       |
| Array Index as Key       | Bugs with reordering       | Use stable IDs                   |
| Mutating State           | React won't re-render      | Immutable updates (`toSorted()`) |
| Over-Engineering         | Complexity without benefit | YAGNI - only what's needed       |
| Premature Optimization   | Wasted effort              | Measure first, then optimize     |
| Effect for Derived State | Sync issues, extra renders | Compute during render            |

---

## πŸ”΅ MEDIUM: Bundle Optimization

| Practice                     | Impact                 | When to Apply                   |
| ---------------------------- | ---------------------- | ------------------------------- |
| Avoid Barrel File Imports    | 200-800ms reduction    | Use direct `@/` path imports    |
| Dynamic Imports (`lazy`)     | Reduce initial bundle  | Maps, charts, modals, forms     |
| Defer Non-Critical Libraries | Faster initial load    | Analytics, error tracking       |
| Preload on User Intent       | Reduce perceived delay | Hover/focus before heavy action |

---

## 🟣 MEDIUM: Async Patterns

| Practice                   | Impact              | When to Apply                   |
| -------------------------- | ------------------- | ------------------------------- |
| `Promise.all()` Parallel   | 2-10x improvement   | Independent async operations    |
| Defer Await Until Needed   | Skip wasted work    | Conditional logic before fetch  |
| Strategic Suspense         | Progressive loading | Wrap data-dependent sections    |
| Conditional Module Loading | On-demand bundles   | Charts, PDFs, advanced features |

---

## 🟀 MEDIUM: Form Handling

| Practice             | Description                               |
| -------------------- | ----------------------------------------- |
| react-hook-form      | Preferred library for all forms           |
| DSFR Components      | Use @codegouvfr/react-dsfr for inputs     |
| Validation in Schema | Use react-hook-form validation rules      |
| Error State Display  | Map formState.errors to DSFR error states |
| Controlled Inputs    | Prefer controlled via `register()`        |

### Benefriches Form Pattern

```typescript
// Standard form component pattern
import { useForm } from "react-hook-form";
import { Input } from "@codegouvfr/react-dsfr/Input";

type FormValues = { name: string; email: string };

function MyForm({ onSubmit }: { onSubmit: (data: FormValues) => void }) {
  const { register, handleSubmit, formState } = useForm<FormValues>();

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <Input
        label="Email"
        state={formState.errors.email ? "error" : "default"}
        stateRelatedMessage={formState.errors.email?.message}
        nativeInputProps={{
          ...register("email", {
            required: "Email requis",
            pattern: { value: /^[^@]+@[^@]+$/, message: "Email invalide" },
          }),
        }}
      />
    </form>
  );
}
```

### Reference Files

- `src/features/onboarding/views/pages/identity/CreateUserForm/CreateUserForm.tsx`
- `src/features/create-site/views/custom/naming/SiteNameAndDescription.tsx`

---

## ⬜ MEDIUM: Accessibility

| Practice            | Description                                   |
| ------------------- | --------------------------------------------- |
| Semantic HTML       | Use appropriate elements (button, nav, main)  |
| ARIA Labels         | Add when semantic HTML isn't sufficient       |
| Keyboard Navigation | Support Tab, Enter, Escape for interactive UI |
| Focus Management    | Manage focus for modals and dynamic content   |
| Icon Accessibility  | Use `aria-hidden="true"` for decorative icons |

### Keyboard Navigation Example

```typescript
// Handle Escape key in modals/dropdowns
function Modal({ onClose, children }) {
  useEffect(() => {
    const handleEscape = (e: KeyboardEvent) => {
      if (e.key === "Escape") onClose();
    };
    document.addEventListener("keydown", handleEscape);
    return () => document.removeEventListener("keydown", handleEscape);
  }, [onClose]);

  return <div role="dialog" aria-modal="true">{children}</div>;
}
```

### Icon Accessibility

```typescript
// Decorative icons should be hidden from screen readers
<i className="fr-icon-check-line" aria-hidden="true" />

// Informative icons need labels
<button aria-label="Fermer">
  <i className="fr-icon-close-line" aria-hidden="true" />
</button>
```

### DSFR Provides Accessibility

DSFR components handle most accessibility concerns. Rely on:

- Built-in ARIA attributes in DSFR components
- Proper focus management in modals via `createModal()`
- Keyboard support in form controls

---

## ⬛ CONSIDER: Error Boundaries

Error boundaries catch JavaScript errors in component trees and display fallback UI.

| When to Use            | Example                               |
| ---------------------- | ------------------------------------- |
| Async data sections    | Wrap data-fetching components         |
| Third-party components | Isolate potentially failing libraries |
| Feature boundaries     | Prevent one feature from crashing app |

### Basic Pattern

```typescript
import { Component, ErrorInfo, ReactNode } from "react";

type Props = { children: ReactNode; fallback: ReactNode };
type State = { hasError: boolean };

class ErrorBoundary extends Component<Props, State> {
  state = { hasError: false };

  static getDerivedStateFromError() {
    return { hasError: true };
  }

  componentDidCatch(error: Error, info: ErrorInfo) {
    console.error("Error boundary caught:", error, info);
  }

  render() {
    return this.state.hasError ? this.props.fallback : this.props.children;
  }
}

// Usage
<ErrorBoundary fallback={<p>Une erreur est survenue</p>}>
  <RiskyComponent />
</ErrorBoundary>
```

**Note**: Not yet implemented in Benefriches. Consider adding for critical sections.

---

## ⚫ LOW: Performance Optimization (Measure First!)

**CRITICAL**: Only apply these when you've **measured** a performance problem.

### Memoization: Usually NOT Needed

**Default stance**: Don't memoize. It adds complexity without benefit in most cases.

| When NOT to Memoize            | Why                               |
| ------------------------------ | --------------------------------- |
| Props change every render      | Memoization is wasted             |
| Component is already fast      | No perceptible benefit            |
| Simple components              | Overhead may exceed savings       |
| Object/array literals as props | Creates new reference each render |

| When to Consider Memoization     | Requirements            |
| -------------------------------- | ----------------------- |
| Measured lag during re-renders   | Profile first!          |
| Expensive rendering (long lists) | And props rarely change |
| Heavy computations in render     | And dependencies stable |

### Better Alternatives to Memoization

1. **Move state down**: Keep state in component that needs it
2. **Lift content up**: Use children pattern for static content
3. **Component composition**: Split into smaller, focused pieces
4. **Selector optimization**: Derive booleans in selectors

### React Compiler (Coming Soon)

React Compiler will auto-memoize, making manual `useMemo`, `useCallback`, and `React.memo` largely redundant. Avoid adding new memoization unless solving a measured problem.

---

## βšͺ React 19 & Future

| Feature         | Impact                                        |
| --------------- | --------------------------------------------- |
| React Compiler  | Auto-memoization (manual memo becomes legacy) |
| `useTransition` | Non-blocking UI updates for heavy operations  |
| `use()` hook    | Simplified async data fetching                |

---

## Benefriches-Specific Integration

### Redux Patterns

**Already following best practices:**

- βœ… Derived state in selectors (not duplicated)
- βœ… ViewData selector per container β€” see [Containers and ViewData](../../../apps/web/AGENTS.md#containers-and-viewdata)
- βœ… Functional updates in reducers
- βœ… `toSorted()` for immutability

**Keep doing:**

- πŸ”΄ **Parallel async in thunks** with `Promise.all()`
- 🟒 **Passive action names** (events: `stepCompleted`, not commands)

### Clean Architecture

- **Core layer**: Pure functions, no framework deps
- **Infrastructure layer**: Gateways with InMemory mocks for tests
- **Views layer**: Container/Presentational separation

### Path Aliases

- πŸ”΄ **Use `@/` for imports** - avoid barrel files
- Example: `import { X } from '@/features/create-site/core/createSite.reducer'`

---

## See Also

- **Code examples**: [examples.md](examples.md) in this skill directory
- **Web app guide**: `apps/web/AGENTS.md`
- **Monorepo guide**: Root `AGENTS.md`

---

**END OF QUICK REFERENCE** - For code examples and detailed patterns, see [examples.md](examples.md).

Files in this skill

  • AGENTS.md33.8 KB
  • SKILL.md10.2 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.