jsx-a11y/anchor-is-valid

Require links to have a real href, and buttons to be button elements

Also called jsx_a11y/anchor-is-valid

You can't reach an a without an href with the Tab key, and it isn't announced as a link. An a with href="#" or href="javascript:..." and an onClick is announced as a link but acts like a button. Space doesn't press it, and "open in new tab" leads nowhere.

Use a with a real address to go somewhere. Use button to do something.

  • An href is missing if it has no value, or is null or undefined.
  • An href is invalid if it's "" or "#", or starts with javascript:.
  • A link with spread props and no href isn't reported, because the href might be in the spread.

Configure#

Off by default. To turn it on:

bunfig.toml
[lint.rules]
"jsx-a11y/anchor-is-valid" = "error" # or "warn"

Every option, set to its default. The ones that are commented out are unset by default.

bunfig.toml
[lint.rules]
"jsx-a11y/anchor-is-valid" = ["error", {
  components = [], # list of strings
  specialLink = [], # list of strings
  # aspects = <list of strings>,
}]

Examples#

example.jsx3 problems
const home = <a>Home</a>;
const top = <a href="#">Back to top</a>;
const save = <a href="#" onClick={saveDraft}>Save</a>;
example.jsxNo problems
const home = <a href="/">Home</a>;
const top = <a href="#top">Back to top</a>;
const save = <button onClick={saveDraft}>Save</button>;
const tracked = <a href="/pricing" onClick={trackClick}>Pricing</a>;
const docs = <a {...linkProps}>Docs</a>;

Options#

OptionDefaultWhat it does
components[]Components to check like a.
specialLink[]Other attributes that count as href.
aspectsall threeWhich checks run: "noHref" (no href), "invalidHref" (an href that goes nowhere), "preferButton" (an onClick with a missing or invalid href).
example.jsxwith { "components": ["Link"], "specialLink": ["to"] }1 problem
const home = <Link>Home</Link>;
example.jsxwith { "components": ["Link"], "specialLink": ["to"] }No problems
const home = <Link to="/">Home</Link>;

With only "invalidHref", a link without an href is allowed:

example.jsxwith { "aspects": ["invalidHref"] }No problems
const home = <a>Home</a>;