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
href | xlink:href | |
|---|---|---|
| Status in SVG 2 | The attribute to use | Deprecated, kept for old files |
Needs xmlns:xlink on the file | No | Yes, in a standalone file |
Browser support, from MDN’s data for <image> and <a> | Chrome 50, Firefox 51, Safari 12.1 | Chrome 1, Firefox 1.5, Safari 3 (3.1 on <a>) |
| In an HTML page | Works | Works, 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
| Element | What 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
- Replace every
xlink:href=withhref=. - Remove
xmlns:xlink="http://www.w3.org/1999/xlink"from the<svg>tag, once nothing else in the file uses thexlink:prefix. Some files also carryxlink: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 see | Cause | Fix |
|---|---|---|
| Nothing is drawn | No # before the id, or the id is spelled differently. Ids are case sensitive | href="#dot", matching the id exactly |
| The file fails with a namespace error | xlink:href with no xmlns:xlink | Add the declaration, or switch to href |
| A copy set from script stays empty | setAttribute('xlink:href', ...) | setAttribute('href', ...) |
| An icon from another file is missing | The file is on another origin, or the page was opened from disk: Chrome reports “Unsafe attempt to load URL” for both | Serve the page and the sprite from one web server |
getAttribute('href') returns null | The element uses xlink:href | Read el.href.baseVal |
| “href.match is not a function” | href is an object on SVG elements | el.href.baseVal |
| Works in the browser, not in older software | The program predates plain href | Write xlink:href, with its declaration |
A gradient is missing though its url(#id) is right | The gradient is inside an <svg> hidden with display: none | Hide 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.