skip to Main Content

I have HTML documents that I fetch from my backend. Those documents consist of 2 kinds of tags and I want to render them dynamically. This is my source code

const Content = ({ slug }) => {
  const [data, setData] = useState()
  useScript("https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js");

  useEffect(() => {

    api.posts.read({ slug: slug }, { formats: ["html", "plaintext"] }).then((resp) => {
      setData(resp)
    })
  }, [slug])

  if (data) {
    return (
      <>
        <SEO
          title={data.title}
          description={data.meta_description}
          canonical_url={data.canonical_url}/>
          <section className={'content'}>
            <h4 className={'content-title'}>{data.title}</h4>
            <div dangerouslySetInnerHTML={{ __html: data.html }}/>
          </section>
      </>
      )
  }
  return <Section>
    <CircularProgress style={{color: '#004D80'}}/>
    <p>Loading</p>
  </Section>
}

I tried these 2 but none of them works for my use case.

1 <script> is rendered but not executed

<div dangerouslySetInnerHTML={{ __html: data.html }}/>

2

React: Script tag not working when inserted using dangerouslySetInnerHTML

This is not working for my case. What if I have tags like
<script src="https://gist.github.com/ogyalcin/66d0785998588ab50cf1908f8d43bb7b.js"></script> in order to render a code block between two paragraphs? Besides, it is hard to handle if there are more attributes inside the tag.

2

Answers


  1. Not an answer, just a bit of feedback FYI. I did an experiment inside browser. Seems like the script tag is rendered. However the code inside isn’t executed, not sure why.

    // @jsx
    
    function App() {
      return <div><h1>React</h1><div dangerouslySetInnerHTML={{ __html: window.thatScript }} /><h2>JS</h2></div>
    }
    
    ReactDOM.render(<App />, document.getElementById('root'));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
    <div id='root'></div>
    <script>
    window.thatScript = '<script type="text/javascript">function foo() {console.log("yolo")} setTimeout(() => foo()};<script>'
    </script>
    Login or Signup to reply.
  2. May help:

    Sample code from a create-react-app project.

    import React, { useEffect } from 'react';
    import './App.css';
    
    function App() {
      const danger = '<script>alert("Hello world!")</script>';
    
      useEffect(() => {
        const el = document.querySelector('.danger');
        el.appendChild(document.createRange().createContextualFragment(danger));
      }, []);
    
      return (
        <div className='App'>
          <h1>Render and execute script tag</h1>
          <div className='danger'></div>
        </div>
      );
    }
    
    export default App;
    

    Edit trusting-dream-i9ozk

    There may also be a library for that:

    dangerously-set-html-content

    import React from 'react'
    import InnerHTML from 'dangerously-set-html-content'
    
    function Example {
    
      const html = `
        <div>This wil be rendered</div>
        <script>
          alert('testing')
        </script>
      `
    
      return (
        <InnerHTML html={html} />
      )
    }
    

    This will also work for scripts with the src attribute set it

    Sharing good article for more information:
    Render dangerous content with React

    Render the danger

    Now what happens when you want to use dangerouslySetInnerHTML but also need to execute any script tag that comes inside the html? That’s against HTML5 specifications, but if we dig a little bit more on what innerHTML do to inject the html we can found something interesting:

    The specified value is parsed as HTML or XML (based on the document type), resulting in a DocumentFragment object representing the new set of DOM nodes for the new elements.

    This DocumentFragment is a lightweight version of the document, it can have child nodes, the main difference is that since is a fragment, is not actually a part of the active/main document.

    We can create a DocumentFragment using the document.Range API.

    import React, { useEffect, useRef } from 'react'
    
    // InnerHTML component
    function InnerHTML(props) {
      const { html } = props
      const divRef = useRef(null)
    
      useEffect(() => {
        const parsedHTML = document.createRange().createContextualFragment> (html)
        divRef.current.appendChild(parsedHTML)
      }, [])
    
    
      return (
        <div ref={divRef}></div>
      )
    }
    
    // Usage
    function App() {
      const html = `
        <h1>Fragment</h1>
      `
    
      return (
        <InnerHTML html={html} />
      )
    }
    

    Reference on innerHTML

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search