Next.js DOMPurify.sanitize() shows TypeError: dompurify__WEBPACK_IMPORTED_MODULE_6___default.a.sanitize is not a function

Viewed 10716

I am using DOMPurify.sanitize() inside dangerouslySetInnerHTML={{}} to display innerHtml returned from the database. For initial purpose I'm using getServersideProps() with next-redux-wrapper for this page.

I installed dompurify with: npm i -S dompurify, present version is: "^2.2.6".

My code:

    import DOMPurify from 'dompurify';
    import { useSelector } from 'react-redux';
    import { END } from 'redux-saga';
    import wrapper from '../redux/storeSetup';

    const EmployeePage = () => {
    
        const blog = useSelector(state => state.data);

        const html_body = blog[0].body_html;
    
        const clean = DOMPurify.sanitize(html_body);
    
        return(
           <div className="mainContainer">
                <div dangerouslySetInnerHTML ={{ __html: clean }} />
                <ul>
                    {blog.map((item) => (
                        <li key={item.author}>
                            <span>{item.author}</span><br/>
                            <span>{item.title}</span>
                            <h4>{item.body_delta}</h4>
                            <p>{item.body_html}</p>
                            <p>{item.created_on}</p>
                        </li>
                    ))}
                </ul>
            </div>
        );
    }

    export const getServerSideProps = wrapper.getServerSideProps( async ({store}) => {
        store.dispatch({type: GET_DATA});
        store.dispatch(END);
        await store.sagaTask.toPromise();    
    });
    export default EmployeePage;

But when I'm running this with npm run dev I'm getting this error: TypeError: dompurify__WEBPACK_IMPORTED_MODULE_1___default.a.sanitize is not a function.

What is wrong here? I tried with even simpler codes but everything shows the same error! What am I doing wrong?

6 Answers

I just switch to js-xss and made a component like that :

import React from "react";
import xss from "xss";

const Html = ({ unsafeHtml }: {unsafeHtml: string}): JSX.Element => {
  return (
    <div
      {...props}
      dangerouslySetInnerHTML={{
        __html: xss(unsafeHtml),
      }}
    />
  );
};

I found a working solution: instead of sanitizing the innerHtml on the server side, I had to sanitize it on the client side right after submitting the rich-text blog, in my case, react-quill:

import dynamic from 'next/dynamic'
import {useState} from 'react'
import DOMPurify from 'dompurify';

const QuillNoSSRWrapper = dynamic(import('react-quill'), {
    ssr: false,
    loading: () => <p>Loading...</p>,
})

// quill modules definitions
//...

export default function articles() {
    const [text, setText] = useState(preData);

    function handleTextChange(content, delta, source, editor) {
        //let str = JSON.stringify(editor.getContents());
        //let parsed = JSON.parse(str)
        setText(editor.getContents());
        const cleaned = DOMPurify.sanitize(editor.getHTML());
        console.log('Cleaned Html: ', cleaned);

    return (
        <div className="quill_container">
            <div id="editor" className="editor">
                <QuillNoSSRWrapper
                    id="quilleditor"
                    modules={modules}
                    formats={formats}
                    theme="snow"
                    value={text}
                    onChange={handleTextChange}
                  />
             </div>
        </div>
    );
};

Ran into this yesterday with a TypeScript/React set up.

import DOMPurify from 'dompurify';

export const sanitize = (html: string): string => DOMPurify.sanitize(html);

This code worked perfectly inside Jest tests but failed in the Browser as the DOMPurify object was undefined. Upon digging I found that there was a DOMPurify object attached at the window scope. I wound up having to put a hack inplace to handle the differing behavior between running in node and in the browser:

import DOMPurify from 'dompurify';

interface IDOMPurifyWindow extends Window {
    DOMPurify: typeof DOMPurify;
}
const purify =
    ((window as unknown) as IDOMPurifyWindow)?.DOMPurify || DOMPurify;

export const sanitize = (html: string): string => DOMPurify.sanitize(html);

There is a compiler warning but it's functional. I could eliminate the compiler warning by importing the es.js version of the library directly: import DOMPurify from 'dompurify/dist/purify.es.js'; But that resulted in Jest being unable to run as it needs vanilla JavaScript.

This library does great things, but it's not super friendly to use if you're using TypeScript and running in a browser.

Inspired from the two other solutions of this thread - isomorphic-dompurify and the one provided on Github.


  1. In a jest-setup.ts file (provided in the jest.config as setupFilesAfterEnv: ["<rootDir>/jest-setup.ts"]):
const DOMPurify = require("dompurify");
const { JSDOM } = require('jsdom');
const { window } = new JSDOM("<!DOCTYPE html>");
global.DOMPurify = DOMPurify(window);

This will make DOMPurify work with Jest.

  1. In your app code, handles DOMPurify for both window and global sides so that it works both in the browser and in jest / NodeJS contexts. e.g. in TypeScript
import { sanitize as DOMPurifySanitize } from "dompurify";

export const sanitize = (text: string): string => {
  const isBrowser = typeof window !== 'undefined';
  return isBrowser ? DOMPurifySanitize(text) : global.DOMPurify.sanitize(text);
}
Related