Links
How anchor elements work, when to use them versus buttons, and how to write links that work for everyone.
Links are the fundamental building block of the web. Every page you visit, every article you read, every resource you download: you got there through a link. Getting them right matters more than almost any other HTML element.
What <a> is for
The <a> element (anchor element) is for navigation. A link takes you somewhere: to another page, to a section of the current page, or to a resource like a file.
This is different from a button, which performs an action. The distinction sounds subtle, but it has concrete consequences:
- Links can be opened in a new tab (right-click, middle-click, or browser extension)
- Links appear in browser history
- Links are announced by screen readers as "link" with their destination implied
- Buttons are announced as "button" with no destination
- The keyboard activation differs: pressing Enter activates a link; pressing Enter or Space activates a button
When you use a link where a button is needed (or vice versa), you break these conventions for all users, including keyboard users and screen reader users.
The href attribute
The href (hypertext reference) attribute gives the link its destination. A link without href is not really a link.
Absolute URLs
An absolute URL includes the full address: protocol, domain, and path.
<a href="https://developer.mozilla.org/en-US/docs/Web/HTML">MDN Web Docs</a>Use absolute URLs for links to external websites. The browser needs the full address to navigate outside your own site.
Relative URLs
A relative URL is resolved relative to the current page's location. The browser fills in the missing parts.
<!-- Relative to the site root -->
<a href="/about">About Us</a>
<a href="/courses/html">HTML Course</a>
<!-- Relative to the current directory -->
<a href="page.html">Another Page</a>
<a href="../index.html">Up one level</a>For links within your own site, relative URLs are preferred. They work regardless of the domain name and do not need updating if you change your domain.
Fragment links
A fragment link navigates to an element with a specific id on the page. The href value starts with # followed by the element's id.
<!-- The link -->
<a href="#installation">Jump to Installation</a>
<!-- The target -->
<h2 id="installation">Installation</h2>Fragment links are used for:
- In-page navigation (a table of contents at the top of a long article)
- "Skip to main content" links that let keyboard users bypass the header
When the fragment link is activated, the browser scrolls to the target element and moves keyboard focus to it. Screen readers announce the content of the target element.
Email and phone links
href="mailto:" opens the user's default email application with a new message pre-addressed.
<a href="mailto:support@example.com">Contact support</a>href="tel:" dials the number on mobile devices and may open a call application on desktop.
<a href="tel:+15551234567">Call us: +1 555 123 4567</a>The phone number in href should use the international format (with country code and no spaces or dashes). The display text can be formatted however you like.
What happens when href is missing
An <a> without an href attribute is not in the tab order. Keyboard users cannot focus it. Screen readers do not announce it as a link. Browsers do not show a pointer cursor.
<!-- Not a link — no href -->
<a class="nav-item">Home</a>
<!-- Is a link — has href -->
<a class="nav-item" href="/">Home</a>Sometimes href is intentionally omitted on a placeholder link during development. That is fine temporarily, but ship it without href and you have broken navigation for keyboard and screen reader users.
The download attribute
Adding the download attribute signals the browser to download the file rather than navigate to it.
<a href="/report.pdf" download>Download the Q1 report</a>You can give the download attribute a value to set the filename the browser saves the file as:
<a href="/report.pdf" download="Q1-2024-Report.pdf">Download the Q1 report</a>Without download, clicking a link to a PDF will open the PDF in the browser (or in the OS PDF viewer). With download, it triggers a save dialog.
The download attribute only works for same-origin files. You cannot force a download of a file from another domain.
target="_blank" and the security risk
target="_blank" opens the link in a new tab instead of the current one.
<!-- Opens in new tab -->
<a href="https://example.com" target="_blank">Visit Example.com</a>There are two problems with target="_blank" used alone.
First, it overrides the user's choice. Users can already open any link in a new tab by middle-clicking or right-clicking. Forcing it takes that choice away. The user might have wanted to navigate in the current tab. Many users find unexpected new tabs disorienting, particularly screen reader users who have to track which tab they are in.
Second, it is a security risk. When a page opens another page using target="_blank", the opened page can access your page via window.opener. A malicious site could use window.opener.location to redirect your page to a phishing site while the user is not looking.
The fix is to add rel="noopener noreferrer":
<!-- Wrong: missing rel — security risk -->
<a href="https://example.com" target="_blank">Visit Example.com</a>
<!-- Correct: rel closes the opener reference -->
<a href="https://example.com" target="_blank" rel="noopener noreferrer">
Visit Example.com (opens in new tab)
</a>noopener prevents the opened page from accessing window.opener. noreferrer additionally prevents the browser from sending the Referer header to the destination, which protects the user's browsing history.
Also consider telling the user the link opens in a new tab, either in the link text or with a visually hidden note. Screen reader users are navigating what they think is the current page, and suddenly finding themselves in a new tab is disorienting.
Writing good link text
Link text should describe the destination. Screen reader users can pull up a list of every link on the page and navigate between them. If every link says "click here" or "read more", that list is useless.
<!-- Wrong: meaningless out of context -->
<p>To learn more about CSS Grid, <a href="/css/grid">click here</a>.</p>
<p>To read the full article, <a href="/blog/flexbox">read more</a>.</p>
<!-- Correct: describes the destination -->
<p>Learn more about <a href="/css/grid">CSS Grid layout</a>.</p>
<p><a href="/blog/flexbox">Read the full guide to Flexbox</a>.</p>A useful test: read all the link text on the page aloud in a list. Each one should make sense without the surrounding sentence.
The same rule applies to image links. If a link contains only an image and no text, the <img> element's alt attribute becomes the link text. Leave alt empty and a screen reader announces the link as "link" with nothing else.
<!-- Wrong: empty alt on an image-only link -->
<a href="/"><img src="logo.png" alt="" /></a>
<!-- Correct: descriptive alt serves as the link text -->
<a href="/"><img src="logo.png" alt="SAE Academy home" /></a>Links versus buttons
This is the distinction that trips up beginners the most.
| Use case | Correct element |
|---|---|
| Navigate to another page | <a href="..."> |
| Navigate to a section on this page | <a href="#section-id"> |
| Download a file | <a href="..." download> |
| Submit a form | <button type="submit"> |
| Open a modal | <button> |
| Toggle a menu | <button> |
| Run JavaScript | <button> |
The most common mistake is <a href="#" onclick="doSomething()">:
<!-- Wrong: multiple problems -->
<a href="#" onclick="showModal()">Open settings</a>Three things go wrong here:
href="#"scrolls the page to the top as a side effect when clicked.- The URL gains a
#in the address bar, which affects browser history. - Pressing Space on a link does not activate it (it scrolls the page). On a button, Space works correctly.
If the element performs an action rather than navigating somewhere, use <button>:
<!-- Correct -->
<button type="button" onclick="showModal()">Open settings</button>A <button> without a type attribute submits the nearest <form>. Always write type="button" for non-submit buttons to prevent accidental form submission.
Wrapping large content in a link
HTML5 allows an <a> to contain block-level elements, including headings, paragraphs, and images. This makes it possible to turn an entire card into a clickable link.
<!-- Technically valid in HTML5 -->
<a href="/courses/html">
<article>
<h2>HTML Fundamentals</h2>
<p>Learn how to structure content with semantic HTML.</p>
<p>8 lessons</p>
</article>
</a>The problem is accessibility. When a screen reader announces this link, the link text is the entire content of the card: "HTML Fundamentals Learn how to structure content with semantic HTML. 8 lessons." That is a lot to listen through, especially when the user is scanning through many links.
Better alternatives:
- Put the link only on the heading, and style the card separately
- Use a visually hidden span to extend the click area while keeping the link text short
<!-- Better: link on the heading only -->
<article>
<h2><a href="/courses/html">HTML Fundamentals</a></h2>
<p>Learn how to structure content with semantic HTML.</p>
<p>8 lessons</p>
</article>Common mistakes
<a href="#"> for actions. Use <button> for actions that do not navigate. href="#" causes a visible page scroll and URL change.
Missing rel="noopener noreferrer" with target="_blank". Always pair target="_blank" with rel="noopener noreferrer".
Generic link text. "Click here", "here", "read more", and "learn more" are meaningless when read out of context. The link text should describe the destination.
No href on placeholder links. A link without href is not focusable and is not announced as a link by screen readers.
Empty <a> tags. An anchor containing only an icon image with no alt text, or only a background-image icon, is invisible to screen readers. Add text or aria-label.
<!-- Wrong: icon-only link, invisible to screen readers -->
<a href="/settings"><img src="gear-icon.png" alt="" /></a>
<!-- Correct: descriptive alt text -->
<a href="/settings"><img src="gear-icon.png" alt="Settings" /></a>Wrapping an entire card in <a>. Valid HTML, but creates long, verbose link announcements. Put the link on the heading instead.
Exercise
Build a navigation section and a content area with well-formed links.