HTML Link Element

The link element is how an HTML document declares a relationship to an external resource — most commonly a CSS stylesheet, but also icons, web fonts, alternate versions of the page, and performance hints. It lives in the head of your document, produces no visible content of its own, and yet it has enormous influence over how a page looks and how fast it loads.

Unlike most elements you’ve learned so far, link is a void element — it never wraps content and never gets a closing tag. Understanding exactly what it does, and how the browser treats it during parsing, is essential for building pages that render correctly and load efficiently.

Overview / How it works

The link element is metadata: it tells the browser “this document relates to that external resource in this specific way.” The nature of the relationship is described by the rel attribute (short for “relationship”), and the resource itself is identified by the href attribute. A single page can, and usually does, contain many link elements: one for the main stylesheet, several for favicons at different sizes, one for a web app manifest, one for a canonical URL, and possibly several for performance hints like preconnect or preload.

Because link belongs inside head, it is never part of the visible DOM tree that gets painted to the screen. It does, however, become a node in the full DOM tree, and the browser’s HTML parser processes it the moment it’s encountered in the markup — this matters a lot for stylesheets specifically. When the parser hits <link rel="stylesheet" href="styles.css">, it kicks off a network request for that CSS file immediately, and by default it will not finish constructing the render tree (and therefore will not paint anything on screen) until that stylesheet has been fetched and parsed. This is why link rel="stylesheet" is called a render-blocking resource — it’s a deliberate design choice so that the page doesn’t flash unstyled content before the CSS arrives.

Not every rel value causes blocking behavior. Resource hints such as preconnect, dns-prefetch, and preload are lightweight instructions that only influence network prioritization — they don’t block anything themselves, but a poorly chosen preload can waste bandwidth if the resource never actually gets used.

Syntax

<link rel="stylesheet" href="/css/main.css">

The general form is a single, self-contained tag (no closing tag) with one or more attributes:

Attribute Purpose
rel Required. Describes the relationship — e.g. stylesheet, icon, preload, canonical, manifest, alternate, preconnect.
href The URL of the linked resource.
type The MIME type of the resource, e.g. text/css or image/png. Optional for stylesheets in modern HTML but still useful for icons.
sizes Used with icons to specify the pixel dimensions available, e.g. 32x32 or any.
media A media query; the resource only applies (or is prioritized) when the query matches, e.g. media="print" or media="(max-width: 600px)".
crossorigin Configures CORS handling, required for some fonts and for verifying integrity.
integrity A subresource integrity hash so the browser can verify the fetched file hasn’t been tampered with.
as Required with rel="preload" to tell the browser what kind of resource is being preloaded (e.g. style, font, script).
hreflang Indicates the language of a linked resource, often used with rel="alternate".

Examples

Example 1: Linking a stylesheet

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>My Page</title>
  <link rel="stylesheet" href="/css/main.css">
</head>
<body>
  <h1>Welcome</h1>
  <p>This paragraph will be styled by main.css.</p>
</body>
</html>

Result: Nothing from the link tag itself is visible, but every rule in main.css now applies to this page — the heading and paragraph render with whatever colors, fonts, and spacing that stylesheet defines. The browser fetches main.css before finishing its initial paint of the body.

This is the single most common use of link: connecting a document to its styling. Because it’s in head, the browser knows about the stylesheet before it has parsed any body content, avoiding a jarring “flash of unstyled content.”

Example 2: Favicons at multiple sizes

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>My Page</title>
  <link rel="icon" href="/favicon.ico" sizes="any">
  <link rel="icon" type="image/png" sizes="32x32" href="/icon-32.png">
  <link rel="icon" type="image/png" sizes="192x192" href="/icon-192.png">
  <link rel="apple-touch-icon" href="/apple-touch-icon.png">
</head>
<body>
  <p>Page content here.</p>
</body>
</html>

Result: The browser tab shows the small icon (favicon) next to the page title. The 192×192 version gets used by devices that need a larger icon, such as when a user bookmarks the site to their phone’s home screen, and the apple-touch-icon is used specifically by iOS when a user adds the page to their home screen.

Browsers pick the most appropriate icon from the set based on context (tab size, bookmark, home screen), so providing several sizes is far more reliable than providing just one.

Example 3: Performance hints, canonical URL, and manifest

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Product Page</title>
  <link rel="canonical" href="https://example.com/products/shoes">
  <link rel="preconnect" href="https://fonts.example.com">
  <link rel="preload" href="/fonts/brand.woff2" as="font" type="font/woff2" crossorigin>
  <link rel="stylesheet" href="/css/main.css">
  <link rel="manifest" href="/app.webmanifest">
</head>
<body>
  <h1>Running Shoes</h1>
</body>
</html>

Result: Visually, only the heading “Running Shoes” appears. Behind the scenes: search engines are told the canonical (preferred) URL for this content, the browser opens an early connection to the fonts domain before it’s actually needed, the brand font file starts downloading early so it’s ready when the CSS references it, the main stylesheet is applied, and the page becomes installable as a web app thanks to the manifest link.

This example shows that link isn’t just about stylesheets — it’s a general-purpose declaration mechanism for relationships between your document and the outside world, including ones that exist purely to make the page faster or more discoverable.

How it works step by step

When the HTML parser encounters a link tag inside head, it performs roughly these steps:

1. It reads the rel attribute to determine what kind of relationship this is.

2. For resource-fetching relationships (stylesheet, preload, icon), it schedules a network request for the URL in href, often before the rest of the document has even finished downloading — this is the browser’s “preload scanner” at work, which looks ahead in the raw HTML for resources to fetch early.

3. If the relationship is stylesheet, the browser holds off finishing the render tree until the CSS is fetched and parsed, because it needs the CSS Object Model (CSSOM) combined with the DOM to know how anything should be painted.

4. Non-blocking hints like preconnect and dns-prefetch simply nudge the browser’s network layer to do DNS lookups or handshake early, without waiting for anything.

5. The link node itself becomes part of the DOM tree (queryable via JavaScript as an element), even though it never appears in the rendered page layout.

Common Mistakes

Mistake 1: Adding a closing tag

<link rel="stylesheet" href="/css/main.css"></link>

link is a void element — it can never have content and must never have a separate closing tag. Writing </link> is invalid markup.

Fix:

<link rel="stylesheet" href="/css/main.css">

Mistake 2: Omitting the rel attribute

<link href="/css/main.css">

Without rel, the browser has no idea what relationship this resource has to the document, so the stylesheet is silently ignored — no styling is applied and no error is shown to the user.

Fix:

<link rel="stylesheet" href="/css/main.css">

Mistake 3: Preloading a resource that’s never used

Adding <link rel="preload" as="font" href="/fonts/unused.woff2"> for a font that no CSS rule actually references wastes bandwidth and can trigger browser console warnings about unused preloads. Only preload resources you are certain will be needed for the current page render.

Best Practices

  • Place rel="stylesheet" links early in head so the browser can start fetching CSS as soon as possible.
  • Provide multiple favicon sizes (rel="icon", rel="apple-touch-icon") so the correct icon is used across tabs, bookmarks, and home screens.
  • Use rel="canonical" on pages with duplicate or near-duplicate content to tell search engines which URL is authoritative.
  • Use rel="preconnect" or rel="preload" only for resources you know the page will need — unnecessary hints can slow things down instead of speeding them up.
  • Always pair rel="preload" as="font" with the crossorigin attribute, or the font will be fetched twice.
  • Remember that actual visual styling (colors, layout, spacing) is the job of the CSS course, not the link element itself — link only makes the connection.
  • Never close a void element like link with a separate closing tag.

Practice Exercises

1. Create a document head that links an external stylesheet named styles.css located in a css folder.

2. Add two link elements for favicons: one generic .ico file and one 32×32 PNG, each with the correct attributes.

3. Add a rel="canonical" link pointing to https://example.com/articles/html-basics for a page that can also be reached through a query-string variant of the same URL.

Summary

  • The link element declares a relationship between the current document and an external resource, using rel and href.
  • It belongs in head, is a void element, and never has a closing tag or visible content.
  • rel="stylesheet" links block rendering until the CSS is fetched and parsed.
  • Other rel values (icon, canonical, preconnect, preload, manifest, alternate) cover icons, SEO, and performance hints.
  • Multiple link elements commonly appear together in a single page’s head.
  • Omitting rel, or adding a closing tag, are the two most common mistakes.