When using Next.js, an error may occur when trying to use DOMPurify.sanitize(), displaying a TypeError message saying that dompurify__WEBPACK_IMPORTED_MODULE_6___default

Utilizing DOMPurify.sanitize() within dangerouslySetInnerHTML={{}} to render the innerHtml retrieved from the database. Initially, I'm employing getServersideProps() alongside next-redux-wrapper for this specific page.

Installed dompurify using: npm i -S dompurify, current version being: "^2.2.6".

Snippet of 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;

However, upon running npm run dev, an error is thrown:

TypeError: dompurify__WEBPACK_IMPORTED_MODULE_1___default.a.sanitize is not a function
.

What seems to be the issue here? Tried simplifying the code but encountered the same error consistently! What am I overlooking?

Answer №1

Try using Isomorphic dompurify

This tool is capable of rendering on both server side and browser

Answer №2

I recently switched over to utilizing js-xss library and created a custom component like this:

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

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

Answer №3

I came across a solution that worked for me: rather than sanitizing the innerHtml on the server side, I found it necessary to sanitize it on the client side immediately after submitting the rich-text blog. In my specific case, using 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>,
})

// define quill modules
//...

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>
    );
};

Answer №4

Encountered this issue recently while working with TypeScript and React.

import DOMPurify from 'dompurify';

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

Although the code passed Jest tests without any problems, it failed in the Browser because the DOMPurify object was undefined. After investigating further, I discovered that there was a DOMPurify object linked to the window scope. To resolve this inconsistency between running in node and in the browser, I had to implement a workaround:

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);

While there is a compiler warning, the solution works effectively. I could avoid the warning by directly importing the es.js version of the library:

import DOMPurify from 'dompurify/dist/purify.es.js';
. However, this caused issues with Jest compatibility since it requires vanilla JavaScript.

Despite its capabilities, this library can be challenging to use with TypeScript when running in a browser environment.

Answer №5

After drawing inspiration from the solutions provided in this discussion on isomorphic-dompurify and the one shared on Github, I devised a unique approach.


  1. To set up DOMPurify with Jest, create a file named jest-setup.ts (referenced 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 configuration ensures that DOMPurify functions correctly with Jest.

  1. In your application code, handle DOMPurify for both the window and global contexts to ensure compatibility with browsers as well as jest / NodeJS environments. For instance, 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);
}

Answer №6

To utilize DOMPurify in Next.js, you can achieve this by installing the jsdom and canvas libraries to create a window object and then passing it to the DOMPurify() function.

Check out the CodeSandbox Demo (using the app directory): https://codesandbox.io/p/sandbox/lingering-river-j5fpi7?file=%2Fapp%2Fpage.tsx

Step-by-step Guide

Begin by installing these libraries:

npm install jsdom canvas

App Router version (Server Component)

In the app/ directory, you can use React Server Components to execute jsdom on the server side:

import DOMPurify from 'dompurify';
import { JSDOM } from 'jsdom';

export default function Home() {
  return (
    <div
      dangerouslySetInnerHTML={{
        __html: DOMPurify(new JSDOM('<!DOCTYPE html>').window).sanitize(
          '<img src=x onerror=alert(1)//>'
        ),
      }}
    />
  );
}

Pages Router version

import DOMPurify from 'dompurify';
import { JSDOM } from 'jsdom';

export default function Home(props) {
  return (
    <div
      dangerouslySetInnerHTML={{
        __html: props.sanitizedHtml,
      }}
    />
  );
}

export function getServerSideProps() {
  const sanitizedHtml =
    DOMPurify(new JSDOM('<!DOCTYPE html>').window).sanitize(
      '<img src=x onerror=alert(1)//>'
    );
  return {
    props: {
      sanitizedHtml: sanitizedHtml,
    },
  };
}

In the pages/ directory, ensure to configure webpack to designate jsdom and canvas as externals (add the webpack key to your next.config.js file):

/** @type {import('next').NextConfig} */
module.exports = {
  reactStrictMode: true,
  webpack: (config) => {
    config.externals = [...config.externals, 'canvas', 'jsdom'];
    return config;
  },
};

For more detailed instructions, refer to:

https://github.com/vercel/next.js/issues/46893

Answer №7

In reference to the following source: https://github.com/cure53/DOMPurify/issues/29#issuecomment-466626162

You can attempt the following code snippet (as demonstrated above):

import { JSDOM } from 'jsdom'
import DOMPurify from 'dompurify'
const { window } = new JSDOM(html_body);
const domPurify = DOMPurify(window);
console.log(domPurify.sanitize(html_body));

Answer №8

dompurify is specifically designed for client-side components, so in order to use it, you'll need to include the line "use client"; at the top of your (tsx, jsx) file. See the example below for more clarity:

"use client";
import React, { useEffect, useState } from "react";
import DOMPurify from 'dompurify';

export default function MarkdownTextView({text}: { text: string }) {
    const [sanitizedHtml, setSanitizedHtml] = useState("");

    useEffect(() => {
        // This code will only run on the client side after the component is mounted
        if (text.length === 0) return;

        const domPurify = DOMPurify(window);
        const cleanHtml = domPurify.sanitize(text); // Make sure to pass your markdown-converted HTML here
        setSanitizedHtml(cleanHtml);
    }, [text]); // This effect will re-run if the 'text' prop changes

    if (!sanitizedHtml) return null; // You can also display a placeholder or loading indicator

    return (
        <div dangerouslySetInnerHTML={{__html: sanitizedHtml}}></div>
    );
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Utilize Javascript and JQuery to implement sending a custom header in an OPTIONS preflight API request

Snippet: $.ajax({ type: 'GET', dataType: 'json', url: api, xhrFields: { withCredentials: true }, beforeSend: function (xhr) { xhr.setRequestHeader('Authorization', "Basic [my auth token]"); }, ...

Unlocking the WiFi Security Key and Accessing Connected Devices with Javascript

When utilizing the command line netsh wlan show interfaces, it displays various information. However, I am specifically interested in extracting the data Profile : 3MobileWiFi-3D71. My goal is to retrieve only the content after the : so that ...

Exploring the capabilities of require() in nodeJS

I'm wondering about the inner workings of the require() function in a nodeJS application. What exactly does require() return? Let's say I want to utilize two third-party packages: lodash and request. After installing these packages, my code mig ...

Encountering issues with Vue routing while utilizing webpack. The main page is functional, however, subpaths are resulting in

Before implementing webpack, my vue routing was functioning properly. However, I encountered several loader issues and decided to use webpack. After setting up webpack, the main page loads correctly, but all of my routes now result in a 404 error. I have ...

Struggling to integrate the c3 chart library with Angular, encountering loading issues

I've been attempting to utilize the c3 angular charts, but unfortunately nothing is displaying on my page. Despite checking for console errors and following a tutorial, I still can't seem to get anything to show up. I have cloned the git repo an ...

Can you explain the concept of a function value?

In the world of JavaScript (ECMAScript 5), functions are highly esteemed (referred to as "first-class functions"). This unique characteristic allows us to treat functions as expressions, which means they can produce values and even include other expressio ...

The mouse pointer adjusts according to the event

I am working on an ajax request and I have implemented a feature where the cursor changes appearance. document.body.style.cursor = "wait"; This immediately shows the cursor as a spinning circle when the request starts. At the end of the request, I ch ...

The response from the ajax call is still pending

I am currently working on integrating MySQL data (select query) using Spring and MyBatis. I have a controller function that is called via ajax in JavaScript to retrieve the database data. For example: ajax url: /testmysql controller requestmapp ...

Guide to altering JSON using Javascript

https://github.com/smelukov/loftschool-example i am currently working on my project in this environment. I have created a friends.json file in the main folder. friends.json { "name": "John", "lastName": & ...

Attempting to replace the checkbox image resulted in no changes

<?php require 'includes/configs.inc.php'; ?> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <title><?php $site_name ?></titl ...

Getting the value of a button using JavaScript

Is there a way to retrieve the value of a button when multiple buttons are generated dynamically? I have a JavaScript function that creates buttons in a list based on my search history, with each button labeled as a city name. However, after clicking on o ...

Employing a function to concatenate promises

In my coding process, I have come across a situation where I need to fetch content and then save it using two separate functions. Each function performs a different task based on the type of content provided. These functions act as helper functions in my o ...

Tips for displaying a table with a button click

I am struggling to figure out how to embed a table inside a button in order to display the table when the button is clicked and hide it when clicked again. Below is the code I have been working with: function toggleTable(){ document.getElementById ...

Guide to positioning a THREE.js plane onto the exterior of a spherical object

Just starting out with Threejs and 3D graphics. I'm interested in learning how to position a plane of any dimensions on the surface of a sphere. Here's an example image of what I'm aiming for: example image ...

Obtain the Zero-width non-joiner character (‌) using the innerHTML attribute

I am attempting to retrieve a &zwnj; using the innerHTML method The desired output should be This section contains a zero-width‌&zwnj;non-joiner, a non-breaking&nbsp;space &amp; an ampersand However, the current output is: This part c ...

Avoid interrupting the code execution until it has finished completely

Let's discuss a common scenario: $('button').on('click',function(){ // Performing AJAX requests (which may take some time); }); If the user clicks the button multiple times, numerous ajax requests can be triggered. To prev ...

The controller persists in its loop as it utilizes $state.go

As a newcomer to Angular, I am uncertain if this is the most effective solution. Within my state/route, there is a button that triggers the following function when clicked: _this.foo = function () { save(); $state.go("next"); } The issue arises w ...

The function is not defined after the button is clicked when an if-else statement is added to the

Hello there, I am currently working on a form to submit data to the database using ajax and CodeIgniter. The form seems to work fine for inserting data, but it lacks validation to check whether the fields are empty or not. I am attempting to add an if-else ...

Having difficulty including a new key-value pair to a JSON object while using another JSON object

Looking to merge key value pairs from one JSON object into another. I've searched through various stackoverflow threads for solutions, but haven't found one that works for my specific scenario. const primaryObject = { name: "John Doe", ag ...

Struggling with organizing my code in node.js - it's all over the place and not very reliable. How should I tackle this

Can anyone help me troubleshoot an issue I'm facing with code that writes to the console late or in random order? var request = require('request'); var vFind = 'HelloWorld'; var vFound = false; var vSites = ['http://www.youtu ...