Next.js render app inside html markup coming as string

19.1k Views Asked by At

I need to build my Next.js app inside a third-party html markup.

The markup is given as follows:

header.txt

<html>
  <head>
    <title>Some title</title>
  </head>
  <body>
    <header>Some header</header>
    <div>

footer.txt

    </div>
    <footer>Some footer</footer>
  </body>
</html>

This files are dynamically generated into a folder. When I render my next.js application, I need to wrap them around my application.

I created a working example using the package called: html-react-parser

I parse the markup from the files in the _document.js and I am looking for a custom element id which I am replacing with the next js app as follows:

const header = fs.readFileSync(path.resolve(ROOT + '/resources', 'header.txt'), 'utf8');
const footer = fs.readFileSync(path.resolve(ROOT + '/resources', 'footer.txt'), 'utf8');
const shell = `
    ${header}
    <main id="react-app"></main>
    ${footer}
`;


// later in the render method od _document.js:

render() {
        return (
            <React.Fragment>
                {parse(shell, {
                    replace: domNode => {
                        if (domNode.attribs && domNode.attribs.id === 'react-app') {
                            return (
                                <React.Fragment>
                                    <Main/>
                                    <NextScript/>
                                </React.Fragment>
                            )
                        }
                    }
                })}
            </React.Fragment>
        )
    }

Although this works, my problem is, that this is not the purpose of the html-react-parser because it converts the markup of the files to react components, and throws many warnings during the conversion about wrongly used html props which react can not use.

Maybe the solution would be to use dangerouslySetInnerHTML, but in this case I can not inject and .

// it fails because it wont treat the components as normal html
// <Main/><NextScript/> are not evaluated
      

const header = fs.readFileSync(path.resolve(ROOT + '/resources', 'header.txt'), 'utf8');
const footer = fs.readFileSync(path.resolve(ROOT + '/resources', 'footer.txt'), 'utf8');
const shell = `
    ${header}
      <React.Fragment>
        <Main/>
        <NextScript/>
      </React.Fragment>
    ${footer}
`;

// later in the render method od _document.js:

render(<html dangerouslySetInnerHTML={{__html: shell}}/>)

If anybody has an Idea how could I manage to wrap my next app around my markup coming from the files, please give me some advice.

2

There are 2 best solutions below

1
On BEST ANSWER

You can achieve that with custom server,

const express = require('express')
const next = require('next')

const header = fs.readFileSync(path.resolve(ROOT + '/resources', 'header.txt'), 'utf8');
const footer = fs.readFileSync(path.resolve(ROOT + '/resources', 'footer.txt'), 'utf8');

const port = parseInt(process.env.PORT, 10) || 3000
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()

app.prepare().then(() => {
  const server = express()

  server.all('*', async (req, res) => {
    const nextResponse = await handle(req, res);
    return mergeHTML(header, footer, nextResponse); // You will need to "merge" cause nextResponse has `head` & `body` as well.
  })

  server.listen(port, err => {
    if (err) throw err
    console.log(`> Ready on http://localhost:${port}`)
  })
})

mergeHTML should merge head from your header & next's head & the body. You can use cheerio to help you with that.

0
On

I solved the problem using dangerouslySetInnerHTML. Basically It does not care about the string syntax you pass into it, so I came up with the following solution in _document.js:

<Html>
  <html dangerouslySetInnerHTML={{__html: header}}/>
  <Head />
  <body>
      <Main />
      <NextScript />
  </body>
  <html dangerouslySetInnerHTML={{__html: footer}}/>
</Html>

Both calls of dangerouslySetInnerHTML are taking the odd number of elements and wrapping around the next.js components.

By the was if anybody has an another solution maybe without dangerouslySetInnerHTML please comment it.