---
description: Conventions for React component files (.tsx). Covers component structure, hooks, state, performance, forms, data fetching, i18n, error handling, and patterns to avoid.
paths:
  - "src/**/*.tsx"
---

# React Conventions

Rules for writing React components and hooks.

> **Styling rules** → `react-styling.md`
> **Accessibility rules** → `react-accessibility.md`
> **Form conventions** → `forms.md`

---

## 1. Component structure

- Use functional components
- Keep components small and focused
- Extract reusable logic into custom hooks
- Use TypeScript for props
- **No logic dangling** in components; move to utils or hooks
- Keep render logic light; move complex maps/conditions to utils

### Component size

Keep component files under **~300 lines**. When a component grows beyond that, split before continuing:

- Constants → `constants.ts` in the same folder
- Hooks → `hooks.ts` or `hooks/` subfolder
- Pure helpers → `utils.ts`
- Sub-components → `components/<SubComponentName>/index.tsx`

---

## 2. Hooks

- Follow Rules of Hooks
- Keep hooks focused and simple
- Use appropriate dependency arrays
- Implement cleanup when needed

---

## 3. State management

- **React Query** for server/async state (data fetched from an API). See **`file-types/react-query.md`** for details.
- **`useState`** for local UI state (toggle, input value, etc.).
- **Context API** for shared client-side state that multiple components need.
- Keep state close to where it's used; avoid prop drilling.

---

## 4. Performance

- **Avoid `useCallback` and `useMemo`** unless there is a clear, measurable performance need (e.g., preventing expensive re-renders in large lists, stabilizing references for dependencies). Do not wrap every handler or value preemptively.
- Use `React.memo` for expensive components
- Avoid unnecessary re-renders
- Use proper `key` props in lists

---

## 5. Forms

Use **react-hook-form** for all forms. See `forms.md` for full conventions.

- Use **Controller** instead of `register`
- Form inputs in `components/FormInputs/` named `Form<Name>`
- Use `values` in `useForm` for API sync; `defaultValues` for creation flows

---

## 6. Data fetching

- Use **React Query** for data fetching when possible
- Use **`select`** option to map responses (runs after query, not on re-render)

---

## 7. Drag and drop

- Use **dnd-kit** for new features (react-beautiful-dnd is deprecated)

---

## 8. i18n

- All user-facing text uses i18n keys: `t('namespace:key')`
- Keys and translations go in **hu-translations** repo

---

## 9. Error handling

- Handle async errors (mutations, data fetching) at the call site or via React Query's `onError`.
- Handle edge cases gracefully.

---

## 10. Component declaration style

- Use **arrow function** components consistently:

  ```tsx
  // YES
  const MyComponent = ({ title }: MyComponentProps) => { ... };
  export default MyComponent;
  ```

---

## 11. Event handler naming

- Props that accept a handler → **`on` prefix**: `onChange`, `onSubmit`, `onClose`.
- Internal functions that handle an event → **`handle` prefix**: `handleChange`, `handleSubmit`, `handleClose`.

---

## 12. What to avoid

- **Business logic in `index.tsx`** — use hooks or utils
- **Wrapper components for styles** — use `sx` on the component
- **Ternary with `null` for single condition** — when rendering a single component conditionally with no else branch, use `condition && <Component />` instead of `condition ? <Component /> : null`
- **Ternary with two JSX branches** — when both branches render JSX as children, prefer `cond && <A />` / `!cond && <B />` on separate lines over `cond ? <A /> : <B />` so each branch reads independently (does not apply to ternaries inside props, attributes, or template strings)
- **`&&` with numeric conditions** — coerce to boolean (`items.length > 0 && <List />` or `!!items.length && <List />`) since React renders `0`/`NaN` to the DOM

---

## 13. Side effects

**Side effects only in `useEffect` or event handlers.** Functions that React may call multiple times or in any order — render body, `setState` updaters, `useMemo`, `useCallback`, reducers — must be pure. No external calls, no callbacks, no mutations of external state inside them.

If you need to react to a state change or trigger an external action after state updates, use `useEffect`.

```tsx
// NO — side effect inside setState updater (React may call this multiple times)
const handleChange = (id: number | null) => {
  setState(prev => {
    const next = { ...prev, id };
    onExternalCallback(next); // ← side effect inside updater
    return next;
  });
};

// YES — pure updater; side effect lives in useEffect
const handleChange = (id: number | null) => {
  setState(prev => ({ ...prev, id }));
};

useEffect(() => {
  onExternalCallback(state);
}, [state]);
```
