React: Better SVG Support for namespaced attributes

Created on 26 Sep 2014  Â·  32Comments  Â·  Source: facebook/react

We are trying to use React for a SVG document. But we are facing problems for namespaced attributes.

For instance, this element cant be constructed with React:

<image xlink:href="firefox.jpg" x="0" y="0" height="50px" width="50px"/>

We cant build a JSX for a xmlns:xlink attribute. Is there any way to solve this?

SVG Bug

Most helpful comment

In the master branch I managed to get namespaced attributes to work by using camelCase.
<svg xlinkHref="http://i.imgur.com/w7GCRPb.png" /> renders to <svg xlink:href="http://i.imgur.com/w7GCRPb.png" />

All 32 comments

The exception that is raised, when we try to put some namespaced attributes:
"Error: Namespace attributes are not supported. ReactJSX is not XML."

I already faced the same issue for image element. I opened an issue and
meanwhile I'm using the following:

{__html:

"

"' fill='#090' stroke='#000' style='cursor:pointer' xlink:href='" +
this.props.image +

"'/>"

}

}/>

2014-09-28 15:20 GMT+01:00 Bruno Ledesma [email protected]:

The exception that is raised, when we try to put some namespaced
attributes:
"Error: Namespace attributes are not supported. ReactJSX is not XML."

—
Reply to this email directly or view it on GitHub
https://github.com/facebook/react/issues/2250#issuecomment-57086899.

Paulo Jorge Dias
: à procura do erro certo
: looking for the right error

+1 on this. React could be a really great platform for doing SVG, but it's hard to use SVG to full effect without xlink:href and friends.

Hi @edmspjp I had the same issue, but it seems add dangerouslySetInnerHTML to tag still has the same problem. And take the whole svg tag as dangerouslySetInnerHTML attr to its parent tag works for me. I am not sure if you met the problem before.

var svgTag='<svg><image..></svg>';

<div dangerouslySetInnerHTML={__html: svgTag} />

Any updates on or further plans for supporting SVG namespaced elements and attributes?

Seems it's still necessary to implement SVGs using dangerouslySetInnerHTML.

I can recommend to use a combination of d3 and react. When ever I need to use namespaced attributes I use d3 in the componentDidMount method.

+1 for this request. I need React to render an inline SVG that has an image inside a clipping path, and it's getting hung on the xmlns:xlink="http://www.w3.org/1999/xlink" attr in the <svg> tag.

@dujuanxian's workaround helped me get through this, provided double (not single) braces are used, of course:

var svgString = '<svg /* React-unfriendly code here... */ </svg>';
<div dangerouslySetInnerHTML={{ __html: svgString }} />

Just to show an example using d3 for the non-react-svg-stuff:

https://github.com/FH-Potsdam/shifted-maps/blob/master/app/client/components/place-map.js#L21-L40

I used d3 here, because React is not able to set href attribute of image tags.

+1 Any updates on this? Thx for the workaround. Hope it'll work until this is fixed.

See in How SVG Fragment Identifiers Work
https://css-tricks.com/svg-fragment-identifiers-work/ a different technic.

2015-07-29 23:12 GMT+01:00 Lennart Hildebrandt [email protected]:

Just to show an example using d3 for the non-react-svg-stuff:

https://github.com/FH-Potsdam/shifted-maps/blob/master/app/client/components/place-map.js#L21-L40

I used d3 here, because React is not able to set href attribute of image
tags.

—
Reply to this email directly or view it on GitHub
https://github.com/facebook/react/issues/2250#issuecomment-126111526.

Paulo Jorge Dias
: à procura do erro certo
: looking for the right error

In the master branch I managed to get namespaced attributes to work by using camelCase.
<svg xlinkHref="http://i.imgur.com/w7GCRPb.png" /> renders to <svg xlink:href="http://i.imgur.com/w7GCRPb.png" />

Do you also get the proper namespace attribute for the namespace itself?

Not quite sure I understand what you mean. Can you give me an explanation/example?

To be able to get links working, you also need an attribute for the namespace itself.

<svg width="800" height="600">
  <a xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="http://www.github.com/"><!-- ... --></a>
</svg>

Does React handle this in your example?

No, it does only pass xlink:href to the DOM node.

@lennerd My understanding is that the xmlns:xlink is not required in HTML.

After looking through the SVG config and some tests it seems like the namespace is passed to the DOM node through the setAttributeNS method.

SVGDOMPropertyConfig.js
DOMPropertyOperations-test.js

Sounds promising. Would be great to remove the d3 dependency.

Yes, as xlinkHref this should work in the upcoming 0.14 release.

xmlns:xlink is not required in HTML only if all the SVG is inline, but it is necessary for some tricky case: In my case, I need to have an image inside defs, to be used then by and to do that xmlns:xlink is apparently necessary.

Just confirming that xlinkHref does work in the 0.14.x releases! Hooray.

I'm still getting this error, even when using react 0.14.x or the newer react 15.0.2. I'm not sure if I'm just missing something else that needs to be updated. I'm writing a library so I'm only using react, not react-dom...I'm not sure if it's babel or something else.

Here's the code:

import React, { PropTypes } from 'react'
import {doesBrowserSupportSVG} from '../../utils/doesBrowserSupportSvg.js';
var empty_png = require('../../assests/images/star-empty.png');
var whole_png = require('../../assests/images/star-whole.png');
var half_png = require('../../assests/images/star-half.png');

class RenderIcon extends React.Component {
  static propTypes = {
    data: PropTypes.shape({
      fill: PropTypes.oneOf(['whole', 'half', 'empty']).isRequired,
      size: PropTypes.string,
      cursor: PropTypes.string
    })
  };

  constructor(props, context) {
    super(props, context);
    const { size, cursor } = this.props;
    const SIZE = size ? size : '16px';
    const CURSOR = cursor ? cursor : 'auto';
    // options.fill ? options.fill : 'empty';

    this.state = {
      svgStyle: {
        cursor: CURSOR,
        fontSize: SIZE,
        width: SIZE,
        height: SIZE,
        lineHeight: SIZE,
        overflow: 'hidden'
      }
    }
  }

  render() {
    console.log('icon ----> ', `#star-${this.props.data.fill}`);
    if (doesBrowserSupportSVG()) {
      return (
        <svg style={this.state.svgStyle}>
          <use xlink:href={`#star-${this.props.data.fill}`} />
        </svg>
      );
    } else {
      switch (this.props.data.fill) {
        case "whole":
          return <img src={whole_png}/>
        case "half":
          return <img src={half_png}/>
        default:
          return <img src={empty_png}/>
      }
    }
  }
}



export default RenderIcon;

And here's my package.json file:


  "name": "ui-elements",
  "version": "0.6.0",
  "description": "A collect of commonly used ui elements/components written in react and built using webpack",
  "main": "./dist/ui.elements.js",
  "scripts": {
    "build:examples": "babel-node --presets es2015 examples/build.js",
    "build:umd": "webpack src/index.js dist/ui.elements.js --config webpack.config.development.js",
    "build:umd:min": "webpack src/index.js dist/ui.elements.min.js --config webpack.config.production.js",
    "watch": "npm run build:umd -- --watch",
    "build": "npm run build:umd && npm run build:umd:min",
    "check": "npm run lint && npm run test",
    "clean": "rimraf lib dist coverage",
    "lint": "eslint src test examples",
    "preversion": "npm run clean && npm run check",
    "postversion": "git push && git push --tags && npm run clean",
    "prepublish": "npm run clean && npm run build",
    "test": "mocha --compilers js:babel/register --recursive",
    "test:examples": "babel-node examples/tests.js",
    "test:watch": "npm test -- --watch",
    "test:cov": "babel-node $(npm bin)/isparta cover $(npm bin)/_mocha -- --recursive",
    "version": "npm run build"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/QuietOmen/ui-elements.git"
  },
  "keywords": [
    "ui",
    "components",
    "elements",
    "react",
    "rating",
    "star",
    "button",
    "dropdown",
    "modal"
  ],
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/QuietOmen/ui-elements/issues"
  },
  "homepage": "http://quietomen.github.io/ui-elements",
  "peerDependencies": {
    "react": ">15.0.2"
  },
  "devDependencies": {
    "babel-cli": "^6.4.5",
    "babel-core": "^6.4.5",
    "babel-eslint": "^6.0.4",
    "babel-loader": "^6.2.1",
    "babel-plugin-flow-comments": "^6.3.19",
    "babel-plugin-transform-decorators-legacy": "^1.3.4",
    "babel-preset-es2015": "^6.0.0",
    "babel-preset-react": "^6.0.0",
    "babel-preset-stage-0": "^6.0.0",
    "chai": "^3.2.0",
    "css-loader": "^0.23.1",
    "eslint": "^2.10.2",
    "eslint-config-airbnb": "9.0.1",
    "eslint-plugin-import": "^1.8.0",
    "eslint-plugin-jsx-a11y": "^1.2.0",
    "eslint-plugin-react": "^5.1.1",
    "expect": "^1.13.4",
    "extract-text-webpack-plugin": "^1.0.1",
    "file-loader": "^0.8.5",
    "isparta": "^4.0.0",
    "mocha": "^2.3.4",
    "node-libs-browser": "^1.0.0",
    "node-sass": "^3.4.2",
    "postcss-loader": "^0.9.1",
    "react": "^15.0.2",
    "rimraf": "^2.5.0",
    "sass-loader": "^3.1.2",
    "skin-deep": "^0.16.0",
    "style-loader": "^0.13.0",
    "url-loader": "^0.5.7",
    "webpack": "^1.12.11"
  },
  "npmName": "ui-elements",
  "npmFileMap": [
    {
      "basePath": "/dist/",
      "files": [
        "*.js"
      ]
    }
  ]
}

and finally the error (which is the same as others were getting):

ERROR in ./src/components/render-rating/index.js
Module build failed: SyntaxError: /Users/bhayden/Documents/personal/ui-elements/src/components/render-rating/index.js: Namespace tags are not supported. ReactJSX is not XML.

@QuietOmen you want <use xlinkHref= not <use xlink:href=

@zpao thanks, for some reason I missed that commented above, so thanks for repeating it.

Apologies if I missed this somewhere, but what about styling SVG inline?

<style type="text/css" >

      <![CDATA[
       circle {
         stroke: #006600;
         fill:   #cc0000;
    }

      ]]>
    </style>

<style> needs to use dangerouslySetInnerHTML (and you probably always needed to if you were putting them inline).

<style type="text/css" dangerouslySetInnerHTML={{__html: `
  <![CDATA[
    circle {
      stroke: #006600;
      fill:   #cc0000;
    }
  ]]>
`}}/>

@zpao But then it's only static, though, right? Like, you can't eval props in there?

e.g.

stroke: `${props.color}`
stroke: {props.color}

@kevinSuttle

What you pass is just a string. You can generate the string dynamically—either with template literals or good ol’ concatenation.

<style type="text/css" dangerouslySetInnerHTML={{__html: `
  <![CDATA[
    circle {
      stroke: ${props.color};
      fill:   #cc0000;
    }
  ]]>
`}}/>

should work fine.

Here's a JSFiddle with a working example: https://jsfiddle.net/yp0q6okn/

A mi me funciono reemplazando xmlns:xlink por xlinkHref y elimine un atributo mas xml:space="preserve" con eso me ando perfecto la referencia lo tome de un pagina

https://itnext.io/using-react-for-xml-svg-470792625278

Was this page helpful?
0 / 5 - 0 ratings