The rule
Buttons do. Links go.
If activating the control performs an action on this page — submit a form, open a dialog, toggle a thing, copy text, save a draft — it’s a <button>. If activating it takes the user to a different URL — another page on this site, a section of the same page, an external resource — it’s an <a href>.
That’s it. There are no exceptions worth memorising.
The pattern
Button — performs an action
Link — goes somewhere
Browse all patternsThe two controls behave differently because the user has different expectations of each:
| Button | Link | |
|---|---|---|
| Announced as | ”button" | "link” |
| Activated by | Enter or Space | Enter only |
| Right-click menu | (none useful) | “Open in new tab”, “Copy link address” |
| Browser history | not affected | adds a history entry |
| Visual default | filled chip with affordance | underlined inline text |
Use the wrong element and all of those expectations break at once. A “link” that runs JavaScript and stays on the page can’t be opened in a new tab. A “button” that navigates can’t be activated with Space. A screen reader announces the wrong role, so the user doesn’t know what’s about to happen.
<!-- Action: stays on this page, performs work -->
<button type="button">Save draft</button>
<!-- Navigation: goes somewhere -->
<a href="/patterns/">Browse all patterns</a> Anti-patterns
Link with href="#" running JavaScript
<a href="#" onclick="saveDraft(); return false;">Save draft</a> Three problems:
- Wrong role — screen readers announce “link”, users expect navigation.
- Right-click is broken — “Open in new tab” reloads the page with
#appended; “Copy link address” returns garbage. - History pollution — even with
return false, some browsers add a history entry. Back button now requires extra presses.
Button styled to look like a link
<button type="button" onclick="location.href='/patterns/'">
Browse all patterns
</button> The opposite mistake. Two problems:
- Wrong role — screen readers announce “button”, users don’t expect navigation.
- No right-click navigation tools — buttons don’t have “Copy link address” or “Open in new tab”.
If the visual design calls for a link-styled control that navigates, use a real <a href>. CSS doesn’t care what element it’s applied to.
Form button without type
<form>
<input type="text" name="email">
<button onclick="trackOpen()">Tell me more</button> <!-- whoops -->
</form> <button> defaults to type="submit" inside a form. The above button submits the form when clicked — even though the developer thought they were just running a tracking call. Always set type="button" on buttons that aren’t submitting.
Common situations
- “Open menu” / “open dialog” — button. Stays on the page; performs an action.
- “Read more” at the end of a teaser — link. Goes to the full article.
- “Add to cart” — button. Stays on the page; updates state.
- “Sign in” in a header — link to
/loginif it goes to a sign-in page; button if it opens a sign-in modal. - “Download report” — link with
hrefto the file. The browser handles the download. - “Print this page” — button that calls
window.print(). Action, not navigation. - “Submit form” — button with
type="submit". Performs the form submission.
Checklist
- Element matches behaviour:
<button>for actions,<a href>for navigation - Buttons have
type="button"unless intentionally submitting - Links have a real, meaningful
href— never#orjavascript:void(0) - No “buttons” using
<a>; no “links” using<button> - If you’re styling a link to look like a button (or vice versa), the underlying element still matches the behaviour