# eslint jsx-a11y label-has-associated-control failed - how to fix it

## TL;DR

Associate every label with its control: wrap the input in the label, use htmlFor with a matching id, or use aria-labelledby. Naked label elements next to inputs fail. One line of why: the association is what makes clicking the label focus the input and what tells screen readers which label belongs to which field.

## The error, verbatim

```text
/app/src/components/Form.jsx
  11:5  error  A form label must be associated with a control  jsx-a11y/label-has-associated-control

```

## Fix it step by step

### Step 1: Reproduce on one file

```bash
npx eslint src/components/Form.jsx --rule '{"jsx-a11y/label-has-associated-control": "error"}'
```

Expected: ESLint prints the label-has-associated-control error.

### Step 2: Find every unassociated label

```bash
npx eslint src | rg 'label-has-associated-control' | head -20
```

Expected: Full list of labels missing a control association.

### Step 3: Wire each label to its control

```bash
rg -n --glob '*.{jsx,tsx}' 'label' src | rg -v 'htmlFor' | head -20
```

Expected: Shows labels without htmlFor; wrap-or-wire each one.

### Step 4: Fix and re-lint

```bash
npx eslint src | rg -c 'label-has-associated-control' || echo 0
```

Expected: Prints 0. Clicking a label now focuses its input, and screen readers announce the pairing.

### Step 5: Enforce it in CI

```bash
npx eslint src --max-warnings 0 | tail -3
```

Expected: Clean output; add the lint command to CI with max-warnings 0 so the violation cannot come back.

## When to use this skill

- Your linter output names this exact jsx-a11y / a11y rule
- You want the violation fixed at author time so it never reaches axe or CI
- You are adding the rule to a shared eslint config and need the fix pattern

## When NOT to use this skill

- The failure comes from axe-core or lighthouse at runtime, not from lint
- You plan to disable the rule project-wide, this skill shows the fix, not the escape hatch

## Compatibility

eslint-plugin-jsx-a11y 6.7+ with eslint 8 or 9. Configure controlComponents and labelComponents if you wrap inputs in custom components. Pin the tool version in the lockfile so scans stay reproducible across machines.

## Variant phrasings

### a form label must be associated with a control

The default message, same fix.

### jsx-a11y label without htmlFor

The usual shape: label and input as siblings with no wiring.

### the rule fires in the editor too

With the eslint extension installed, the same error appears inline while typing; the fix is identical.

## Why it happens

Form layouts separate labels from inputs for styling, breaking the association, or custom input components do not forward ids so htmlFor has nothing to point at. The rule accepts nesting, htmlFor plus id, and aria-labelledby. It fails on label elements with no association strategy at all. Placeholder attributes are not labels and never satisfy the rule. Linter rules catch the violation at author time, which is cheaper than any runtime fix. When this rule fires across many files, fix the shared component and mention it in team onboarding so new code does not reintroduce it.

## Edge cases

- If your input is a custom component, forward the id prop or configure controlComponents so the rule sees through it.
- Wrapping the input inside the label is the simplest pattern and needs no ids.
- aria-label on the input is an alternative, but visible labels are better for everyone, prefer the label element.
- After fixing, run the whole lint suite once; a11y rules interact and one fix can surface the next violation.

## Provenance

Resolved from the public thread: https://vectle.com/posts/pst_ermnap3NJUerrvdG1Nd_zA
