01 — Foundation

Choose the right control

Use a button when something happens. Use a link when someone goes somewhere.

Teams still ship links that behave like buttons, buttons that navigate, and div elements with click handlers that recreate the browser badly. That creates accessibility problems, broken expectations, keyboard issues, and unnecessary JavaScript.

The rule is simple: match the element to the behaviour. GOV.UK uses buttons for actions and links for navigation — the same split we teach here. Everything else — styling, framework examples, what was fastest yesterday — comes second.

02 — Buttons

Actions use buttons

Buttons change state or trigger behaviour on the current page.

  • submitting forms
  • opening dialogs, drawers, or menus
  • toggling accordions, filters, or visibility
  • saving settings, starting uploads, or running in-page scripts

03 — Links

Links represent destinations — somewhere the user can go.

  • moving to another page or route
  • downloading files
  • jumping to in-page sections with a real href
  • opening external URLs users may bookmark or open in a new tab

04 — Decision

The simplest rule

One question settles most arguments.

Ask: Does this take the user somewhere? Use a link.Does this make something happen on the page? Use a button.

Not “what is easiest to style?”, not “what did the framework demo do?”, and not “we already built the component this way.” Use the correct element — then style it.

05 — Semantics

Why the distinction matters

Buttons and links ship with different browser and assistive technology behaviour.

  • different roles announced by screen readers
  • different keyboard behaviour users already know
  • links support history, bookmarking, and “open in new tab”
  • buttons support disabled and form submission properly

Predictability is usability. Breaking platform conventions creates friction immediately.

06 — Examples

Common mistakes and fixes

Same visual design is fine. Wrong semantics is not.

Action disguised as a link

Bad

<a href="#" onclick="openDialog()">
    Open settings
</a>

Fake destination, wrong affordance, and behaviour that depends on JavaScript for no good reason.

Good

<button type="button">
    Open settings
</button>

Navigation disguised as a button

Bad

<button type="button" onclick="window.location='/pricing/'">
    Pricing
</button>

This is navigation. The platform already has an element for that.

Good

<a href="/pricing/">
    Pricing
</a>

07 — Keyboard

Keyboard and disabled states

Do not fight conventions users already learned.

  • buttons activate with Enter and Space
  • links primarily activate with Enter
  • use real disabled on buttons — notpointer-events: none on fake links

Form submission

Bad

<a href="#" onclick="submitForm()">
    Save changes
</a>

Good

<button type="submit">
    Save changes
</button>

The browser already handles submit, Enter in fields, and disabled submit controls. Use that work.

08 — Styling

Styling, SPAs, and resilience

Appearance does not define semantics. Behaviour does.

A link styled like a button is still a link. A button styled like a link is still a button. CSS changes how it looks, not what it means.

Client-side routing does not change the rule. If it navigates, use your framework’s link component with a real URL. If it performs an action, use a button. Routing is still navigation.

Correct semantics improve progressive enhancement: links work without JavaScript, forms still submit, and you ship less custom event code fixing problems the platform already solved.

09 — Anti-patterns

What to avoid

Familiar mistakes that still ship in production.

  • div or span with click handlers instead ofbutton
  • anchors with href="#" and no real destination
  • buttons that call window.location for navigation
  • “disabled” links via CSS instead of proper button state
  • entire cards or rows made clickable without a clear, predictable control

10 — Review

Before you approve

A short checklist for buttons and links in code review.

  • does this navigate or perform an action?
  • is the semantic element correct?
  • does keyboard behaviour match user expectations?
  • does screen reader output make sense?
  • is native behaviour preserved instead of reimplemented in JavaScript?
  • would this still work sensibly if JavaScript failed to load?
  • are disabled states handled with buttons, not fake links?

When in doubt, ask: If JavaScript disappeared right now, would this still behave correctly? If the answer is no, you probably chose the wrong element.

Common questions

When do I use a button instead of a link?

Use a button for actions on the current page — submit, open a dialog, toggle visibility. Use a link when navigation changes the URL or loads a new document.

Can a link look like a button?

Yes, if it navigates. Style is separate from semantics — an anchor styled as a button is still a link and should not submit forms or toggle panels.

What is wrong with div click handlers for navigation?

Divs lack link semantics, keyboard activation, and predictable browser behaviour. Users and assistive tech expect real links for navigation.