SVG href and xlink:href: which one to use

In SVG, href points one element at another or at a file. xlink:href is the older spelling. Which to write, what happens with both, and the faults in script.

Published

In SVG, href is the attribute that points an element at something else: a <use> at the shape to copy, an <image> at a picture, an <a> at a page. xlink:href is the older spelling of the same attribute. Write plain href in anything new. Current browsers read both, and when an element has both, href wins.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
  <defs>
    <circle id="dot" r="10" fill="teal"/>
  </defs>
  <use href="#dot" x="30" y="50"/>
  <use href="#dot" x="70" y="50"/>
</svg>

Two teal dots, each a copy of the circle defined once at the top.

The two spellings

hrefxlink:href
Status in SVG 2The attribute to useDeprecated, kept for old files
Needs xmlns:xlink on the fileNoYes, in a standalone file
Browser support, from MDN’s data for <image> and <a>Chrome 50, Firefox 51, Safari 12.1Chrome 1, Firefox 1.5, Safari 3 (3.1 on <a>)
In an HTML pageWorksWorks, with no declaration needed

SVG 1.1 borrowed its linking from a separate standard called XLink, which is why the attribute carried a prefix. SVG 2 moved href into SVG itself and deprecated the prefixed form, saying that references “should be specified using the href attribute without a namespace”.

The practical difference is the declaration. In a standalone .svg file, a prefix must be declared before it is used:

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100">
  <defs>
    <circle id="dot" r="10" fill="teal"/>
  </defs>
  <use xlink:href="#dot" x="50" y="50"/>
</svg>

Leave out xmlns:xlink and the file is not valid XML. Chrome refuses the whole file with “Namespace prefix xlink for href on use is not defined”. Plain href needs no declaration, which removes that whole class of fault. SVG xmlns explains namespaces.

Inside an HTML page the rule is looser: the HTML parser knows xlink:href on SVG elements and accepts it with no declaration.

When both are present

The SVG 2 specification is exact about it: when href is present both with and without the XLink namespace, the one without is used and the other ignored. Tested in Chrome with a teal target in href and a gold one in xlink:href, the copy was teal, whichever attribute was written first.

That makes this form safe. The specification allows software to write both, so that a file works in old and new programs alike:

<use href="#dot" xlink:href="#dot" x="50" y="50"/>

Which elements take href

ElementWhat href points at
<use>An element to copy, by id: #dot or icons.svg#dot
<image>A picture file or a data: URI
<a>Any address, as in HTML
<linearGradient>, <radialGradient>Another gradient whose stops and settings to inherit
<pattern>Another pattern to inherit from
<textPath>The path the text runs along
<script>An external script file

Every element in the table accepted both spellings when tested in Chrome.

Paints and effects use a different notation. fill, stroke, clip-path, mask, filter and the marker properties refer to an element with url(#id), not with href.

A reference to something inside the same document is a # followed by the id. Leave the # off and the value is read as a file name, which finds nothing.

In JavaScript

Most faults with xlink:href happen in script, because the two spellings are two different attributes as far as the DOM is concerned.

Reading. getAttribute('href') returns null on an element written with xlink:href, and getAttribute('xlink:href') returns null on one written with href. The href property reads whichever is there:

const target = use.href.baseVal; // "#dot", for either spelling

Writing. Set plain href:

use.setAttribute('href', '#dot');

That worked in Chrome even on an element that already had an xlink:href, since href takes priority. What does not work is the obvious-looking use.setAttribute('xlink:href', '#dot'). It creates an attribute with a colon in its name and no namespace, which SVG does not recognise: the copy stayed empty. To write the old form from script, the namespace has to be given:

use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', '#dot');

href is not a string. On an HTML link, a.href is text. On any SVG element, href is an object, an SVGAnimatedString, and the text is in href.baseVal. Code written for HTML links fails on SVG ones with errors such as “href.match is not a function”. Use link.href.baseVal or link.getAttribute('href').

Selectors. In querySelectorAll and in CSS, [href] matches only the plain attribute. [xlink\:href] matched nothing in Chrome, even with elements that had the attribute. [*|href] matches both spellings:

const allUses = document.querySelectorAll('use[*|href]');

Saving. XMLSerializer adds the xmlns:xlink declaration when it writes out an element that uses the prefix. outerHTML does not, so a drawing saved from outerHTML with xlink:href in it produces a file that will not open.

Converting a file from xlink:href to href

  1. Replace every xlink:href= with href=.
  2. Remove xmlns:xlink="http://www.w3.org/1999/xlink" from the <svg> tag, once nothing else in the file uses the xlink: prefix. Some files also carry xlink:title, which SVG 2 deprecates too in favour of a <title> child element.

Whether to convert depends on where the file is going. For the web, plain href is right. For other software, the old form is the cautious choice: MDN’s data puts plain href in Safari only from version 12.1, and some older programs read only xlink:href. A file that must open everywhere can keep xlink:href, or carry both.

use href not working

What you seeCauseFix
Nothing is drawnNo # before the id, or the id is spelled differently. Ids are case sensitivehref="#dot", matching the id exactly
The file fails with a namespace errorxlink:href with no xmlns:xlinkAdd the declaration, or switch to href
A copy set from script stays emptysetAttribute('xlink:href', ...)setAttribute('href', ...)
An icon from another file is missingThe file is on another origin, or the page was opened from disk: Chrome reports “Unsafe attempt to load URL” for bothServe the page and the sprite from one web server
getAttribute('href') returns nullThe element uses xlink:hrefRead el.href.baseVal
“href.match is not a function”href is an object on SVG elementsel.href.baseVal
Works in the browser, not in older softwareThe program predates plain hrefWrite xlink:href, with its declaration
A gradient is missing though its url(#id) is rightThe gradient is inside an <svg> hidden with display: noneHide the holder with width="0" height="0" instead

The last row is a neighbouring fault worth knowing. In Chrome, a <use> could copy a symbol out of an SVG hidden with display: none, but a gradient referenced from the same hidden SVG painted nothing.

Questions

Is xlink:href going to stop working? It is deprecated, which means discouraged, not removed. The SVG 2 specification keeps it defined for backwards compatibility, and browsers still support it.

Does the order of definition matter? No. A <use> can refer to an element defined later in the document. This was tested with the symbol written after the <use>.

What about React? JSX accepts href on SVG elements as it stands. SVG in React lists the attributes that change.

How do I see which form a file uses? Open it in the SVG viewer and search its code for xlink.