next.js icon indicating copy to clipboard operation
next.js copied to clipboard

Invalid HTML inside `dangerouslySetInnerHTML` breaks the page.

Open lfades opened this issue 5 years ago • 19 comments

Bug report

Describe the bug

If invalid HTML is added to dangerouslySetInnerHTML, Next.js will output a blank page without providing any feedback. This can be hard to track when working with a CMS provider or markdown files.

To Reproduce

Steps to reproduce the behavior, please provide code snippets or a repository:

  1. Clone https://github.com/lfades/nextjs-inner-html-bug
  2. Run yarn && yarn dev or npm i && npm run dev
  3. See that pages/index.js is a blank page with no errors

Expected behavior

Invalid HTML inside dangerouslySetInnerHTML should throw and/or let the user know that there's something wrong.

The demo also has an index.html and index.js in the root directory that shows how the same code works in React alone, it doesn't produce an error either, but it shows the content.

lfades avatar Jul 02 '20 14:07 lfades

Hey @lfades is it okay if I take this issue up?

I was able to reproduce the issue locally and noticed that the reason the page goes blank is because dangerouslySetInnerHTML seems to be auto closing the open tag while wrapping any HTML/Scripts etc.. underneath inside of the tag.

This does not cause the page to go blank if that invalid html tag is a or even invalid markup like but will go blank if the invalid html tag is a

I think the right behavior would be to ensure that the next.js scripts don't end up wrapped inside of the invalid tag so that the JS can execute and any valid content can still render on the page.

What are your thoughts, i'll attach screenshots for reference...

TatisLois avatar Jul 02 '20 16:07 TatisLois

Open tag will autoclose with all markup and scripts underneath the open tag wrapped inside of it Screen Shot 2020-07-02 at 12 30 48 PM

Same issue with

Screen Shot 2020-07-02 at 12 43 25 PM

NOT an issue with a open markup tag Screen Shot 2020-07-02 at 12 44 07 PM

TatisLois avatar Jul 02 '20 16:07 TatisLois

@lfades or anyone else..

I tried to run a forked/cloned version of next locally and have it point to the example app but I keep running into a mismatch react issue.

I tried to work through it but am stuck, any thoughts?

Screen Shot 2020-07-02 at 10 36 45 PM

TatisLois avatar Jul 03 '20 03:07 TatisLois

@TatisLois Follow the steps here to setup the Next.js repo: https://github.com/vercel/next.js/blob/canary/contributing.md

Once you run yarn dev inside the root directory (which compiles the Next.js packages), you can change your package.json and point it to the next package: https://github.com/vercel/next.js/blob/canary/contributing.md#running-your-own-app-with-locally-compiled-version-of-nextjs

lfades avatar Jul 03 '20 15:07 lfades

@TatisLois Follow the steps here to setup the Next.js repo: https://github.com/vercel/next.js/blob/canary/contributing.md

Once you run yarn dev inside the root directory (which compiles the Next.js packages), you can change your package.json and point it to the next package: https://github.com/vercel/next.js/blob/canary/contributing.md#running-your-own-app-with-locally-compiled-version-of-nextjs

~~I followed those steps and is how I ran into the issue. I’ll wipe the repo clean and try again from scratch, thanks!~~

got it working - thanks

TatisLois avatar Jul 03 '20 16:07 TatisLois

I'll take a look at this issue as well. I think the responsibility of putting in valid html in dangerouslySetInnerHTML is on the user, but I think I have a solution for outputting an error if the user does so. (Ping @lalmqvist)

sannamar avatar Sep 03 '20 07:09 sannamar

I don't have time to continue on this issue, so if anyone else wants to continue - feel free to do so!

My idea is to validate the HTML with perhaps html-validator package and logging the error to the console. I think adding something similiar to this in Main component could work:

export function Main() {
  const { inAmpMode, html, docComponentsRendered } = useContext(
    DocumentComponentContext
  )
+  const [invalidHtmlError, setInvalidHtmlError] = useState()

  docComponentsRendered.Main = true

+  useEffect(() => {
+    checkHtml()
+  })

+  const checkHtml = async () => {
+   try {
+      await htmlValidator(html)
+    } catch (error) {
+      setInvalidHtmlError(error)
+    }
+  }

  if (inAmpMode) return <>{AMP_RENDER_TARGET}</>

+  if (invalidHtmlError) {
+    Log.error(
+      "There's  an error with your HTML",
+      JSON.stringify(invalidHtmlError)
+    )
+  }

  return <div id="__next" dangerouslySetInnerHTML={{ __html: html }} />
}

sannamar avatar Sep 03 '20 08:09 sannamar

I also had this problem when allowing user generated HTML. If you wrap the dangerously set HTML code using the sanitize package linked below - it auto-fixes this broken HTML problem for you.

https://www.npmjs.com/package/sanitize-html

<div dangerouslySetInnerHTML={{ __html: (sanitizeHtml(yourHtmlStringHere, {
   allowedTags: ['*'],
   allowedAttributes: {'*': [ '*' ]},
   allowedAttributes: { '*': ["*"]},
   allowedIframeHostnames: ['*']
   })) }} 
/>

augustmuir avatar Oct 18 '21 17:10 augustmuir

I'll take this one on.

Christopher-Stevers avatar Jan 19 '22 14:01 Christopher-Stevers

Hey, is there any progress on the issue?

If there is no one that is not taking this on, I'll be ready to take this on.

cc @Timer @Christopher-Stevers @lfades

MarioSerano avatar Oct 05 '22 17:10 MarioSerano

If there is no progress on this issue,please assign this issue to me

karthiknadar1204 avatar Jan 22 '23 13:01 karthiknadar1204

Can't reproduce on latest commit. I see the heading but not the subtitle because of wrong tag. But the page works fine. Screenshot 2023-09-04 at 3 31 54 AM

abhinaypandey02 avatar Sep 03 '23 22:09 abhinaypandey02

Hello, can I fix this bug, if no one is working on it? @samcx @lfades

Mbistami avatar Oct 16 '24 16:10 Mbistami

@Mbistami Nobody is! Feel free to open a PR 🙇🏼

samcx avatar Oct 17 '24 14:10 samcx

@samcx should we use some library to validate the HTML or can't add packages I'm very new to contributing in next and I think adding html-validator can help fixing it but I'm not sure if its okay to install a whole library for this only bug?

Mbistami avatar Oct 17 '24 16:10 Mbistami

Hmm we should already have something like this.

If you use the command pnpm new-test, it'll create a folder with a test file like this →

import { nextTestSetup } from 'e2e-utils'

describe('test-test', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  // Recommended for tests that check HTML. Cheerio is a HTML parser that has a jQuery like API.
  it('should work using cheerio', async () => {
    const $ = await next.render$('/')
    expect($('p').text()).toBe('hello world')
  })

  // Recommended for tests that need a full browser
  it('should work using browser', async () => {
    const browser = await next.browser('/')
    expect(await browser.elementByCss('p').text()).toBe('hello world')
  })

  // In case you need the full HTML. Can also use $.html() with cheerio.
  it('should work with html', async () => {
    const html = await next.render('/')
    expect(html).toContain('hello world')
  })

  // In case you need to test the response object
  it('should work with fetch', async () => {
    const res = await next.fetch('/')
    const html = await res.text()
    expect(html).toContain('hello world')
  })
})

samcx avatar Oct 17 '24 18:10 samcx

I see, but I coulnd't find a way to get the HTML to validate It tried to implement it on Main in _document.tsx but seems confusing should I maybe add the handling on the Document class?

Mbistami avatar Oct 18 '24 08:10 Mbistami

@Mbistami I think we should try to validate what you're seeing is this error itself. Do you have a :repro: we can take a look at?

samcx avatar Oct 18 '24 15:10 samcx

@samcx

https://github.com/lfades/nextjs-inner-html-bug

Mbistami avatar Oct 18 '24 15:10 Mbistami

@Mbistami That :repro: is quite dated—I think we should try to create a fresh project on the latest and see if the issue is still relevant in Pages and/or App Router.

samcx avatar Nov 08 '24 01:11 samcx

I tested this with app routes, and it works fine. The issue with the provided code from @Mbistami in the repo https://github.com/lfades/nextjs-inner-html-bug seems to be that it contains invalid style properties mixed into the HTML:

const HTML = <div> <h1>This is a title</h1> <style> <h2>This is a subtitle</h2> </div>

After removing the invalid

awadhMS avatar Jan 27 '25 20:01 awadhMS

Closing because this has become stale!

If this issue arises, feel free to re-submit an issue via the bug report.

samcx avatar Feb 07 '25 22:02 samcx

This closed issue has been automatically locked because it had no new activity for 2 weeks. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.

github-actions[bot] avatar Feb 22 '25 00:02 github-actions[bot]