10

I am embedding HTML inside SVG via foreignobject:

var SvgWithForeignObject = React.createClass({
  render: function() {
    return(
      <svg>
        <foreignobject><div>Hello From SVG</div></foreignobject>
      </svg>
    )
  }
});

ReactDOM.render( < SvgWithForeignObject / > ,
  document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id="container">
  <!-- This element's contents will be replaced with your component. -->
</div>

The "Hello From SVG" string is not rendered. However, I can make a minor whitespace edit in Chrome or FF and then it becomes visible:

enter image description here

(Note: The screen recording is from an example where I use React via Scala.js, but the behavior is exactly the same with plain React)

Rahel Lüthy
  • 6,837
  • 3
  • 36
  • 51
  • To illustrate that it is working without React: https://jsfiddle.net/8v8taqtz/ – Rahel Lüthy Sep 15 '16 at 07:15
  • svg is case sensitive and the element you want to use is called `foreignObject`. Note the upper cased O. Also, you **must** set a width and height attribute on this element. Finally, don't forget to set `xmlns="http://www.w3.org/1999/xhtml"` on your root HTML element. – Kaiido Sep 15 '16 at 07:22
  • Wow, what a stupid little mistake. Could you please write this as an answer so I can accept it? Thank you very much! – Rahel Lüthy Sep 15 '16 at 07:26

2 Answers2

19

SVG is case sensitive and the element you want to use is called foreignObject. Note the upper cased O.

Also, you must set a width and height attribute on this element.

Finally, don't forget to set xmlns="http://www.w3.org/1999/xhtml" on your root HTML element.

var SvgWithForeignObject = React.createClass({
  render: function() {
    return(
      <svg>
        <foreignObject width="400" height="400"><div xmlns="http://www.w3.org/1999/xhtml">Hello From SVG</div></foreignObject>
      </svg>
    )
  }
});

ReactDOM.render( < SvgWithForeignObject / > ,
  document.getElementById('container')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

<div id="container">
  <!-- This element's contents will be replaced with your component. -->
</div>
Kaiido
  • 123,334
  • 13
  • 219
  • 285
  • what's the point of setting xmlns attribute if reactjs doesn't support it? http://d.pr/i/Zo8b – El Kopyto Dec 15 '16 at 16:32
  • @ElKopyto when the element is creted via createElementNs the attribute is not set inthe markup, hence not visible on this live view of the markup, but it still has the correct namespace. You can check if it is set correctly by checking `yourElement.namespaceURI` – Kaiido Dec 15 '16 at 23:02
  • 4
    A note for those using TSX who are getting an error for the unrecognized property `xmlns`: You can put a line comment `// @ts-ignore` between `
    – Jacob May 01 '20 at 08:25
3

I've ran into the same issue when trying to add foreignObject element inside a D3 graph.

It is correct that you are missing the width and height attributes on the foreignObject element, but I also believe answering this requires further understanding on namespaces.

Here is an in depth explanation to namespaces, and how do they relate to SVG elements with foreignObject elements:

https://developer.mozilla.org/en-US/docs/Web/SVG/Namespaces_Crash_Course

To summarize:

SVG elements have a namespace. The namespace is for differentiating between xml dialects. For example, both SVG and XHTML (a stricter, more XML-based version of HTML) have a 'title' element. Using a namespace helps solve this issue.

When using a SVG element, you are inside the svg namespace. The 'xmlns' attribute defines the namespace. In this case, we are using the svg XML namespace (mentioned in the URI)

<svg xmlns="http://www.w3.org/2000/svg">
    <! -- svg namespace -->
</svg> 

The element foreignObject allows you to includes elements from a different XML namespace.

<svg xmlns="http://www.w3.org/2000/svg">
    <!-- svg namespace -->
    <foreignObject>
        <!-- Here you can add elements from a namespace other than the svg namespace -->
    </foreignObject>
</svg> 

Now inside the foreignObject element, add the non svg elements that you have with the namespace they belong to.

<svg xmlns="http://www.w3.org/2000/svg">
    <! -- svg namespace -->
    <foreignObject>
        <xhtml:div>Div element with the XHTML namespace</div>
    </foreignObject>
</svg> 

And the reason for using the XHTML namespace over html - XHTML is a XML-based version of HTML. To use XML namespaces, use the XHTML namespace.

For further reading on XML namespace: https://www.w3.org/TR/REC-xml-names/#sec-namespaces

Lior Ehrlich
  • 81
  • 1
  • 1
  • 3