Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I render a <template /> tag with react?

Sometimes you might need to render web-components from your react app.

Web-components often use a special <template> ... </template> tag. But if I try to render such markup with react like this:

render() {
  return (
    <template>
      <div>some content</div>
    </template>
  )
}

then my web-components don't work correctly.

like image 944
timetowonder Avatar asked Mar 16 '17 21:03

timetowonder


1 Answers

The reason is that JSX does a different job than what the <template /> tags exists for. The idea of a template tag is to not render its children and pretty much handle it like unparsed text (the browser actually parses it just to make sure its valid html, but does nothing more)

But when you write this in JSX:

return (
  <template>
    <div>some content</div>
  </template>
)

you're basically instructing react to create a 'template' element and then create a 'div' element and then to append this div to the template as a child.

So under hood this happens:

const template = document.createElement('template')
const div = document.createElement('div')
const text = document.createTextNode('some text')
div.appendChild(text)
template.appendChild(div)

But what you want is to set the contents of the <template /> as a string. You can use innerHTML for that.


Solution

One solution would be:

render() {
  return (
    <template
      dangerouslySetInnerHTML={{
        __html: '<div>some content</div>'
      }}
    />
  )
}

Now you're asking react to create all those children tags as node elements but letting the browser decide what to do with them.

Nicer solution

You might not want to use dangerouslySetInnerHTML all the time. So let's create a helper component:

function Template({ children, ...attrs }) {
  return (
    <template
      {...attrs}
      dangerouslySetInnerHTML={{ __html: children }}
    />
  );
}

Now any time you need to use a template you can use it like this:

render() {
  return (
    <Template>
      {'<div>some content</div>'}
    </Template>
  )
}

Don't forget to put the inner content in quotes, because it should be a string.

like image 134
timetowonder Avatar answered Sep 28 '22 08:09

timetowonder