When working with third-party NPM packages, you may find bugs, missing features, or specific needs that require modifying the package code. Instead of waiting for the package maintainers to merge a pull request (which can take weeks or months), you can directly modify the package in your node_modules and make your changes persist using patch-package.
In this blog, we'll go through the steps to modify an NPM package, create a patch, and ensure the changes are applied even after reinstalling dependencies.
Why Modify an NPM Package?
Fix Bugs: If a package has a bug that affects your project, you don't have to wait for the maintainer to fix it.
Add Features: Sometimes, packages lack features you need, and modifying them is quicker than waiting for an official update.
Avoid Forking: Instead of forking and maintaining a separate package, you can patch the existing one directly.
No Need for PR Approval: Pull requests can take weeks or months to get merged. With patch-package, you can apply changes immediately.
Step 1: Install patch-package
- First, install 
patch-packageas a development dependency: 
npm install patch-package postinstall-postinstall --save-dev
- Also, add a postinstall script in your 
package.jsonto ensure patches are reapplied after installing dependencies: 
"scripts": {
  "postinstall": "patch-package"
}
Step 2: Modify the Package Code
- Locate the package inside node_modules and make the necessary changes. For example, if you're modifying @kunalukey/react-image, edit the file:
 
node_modules/@kunalukey/react-image/main/LazyImage.js
import React, { useEffect, useRef, useState } from "react";
const LazyImage = ({
  placeholderSrc,
  placeholderClassName,
  placeholderStyle,
  src,
  alt,
  className,
  style
}) => {
  const [isLoading, setIsLoading] = useState(true);
  const [isPlaceholder, setIsPlaceholder] = useState(false);
  const [view, setView] = useState("");
  const placeholderRef = useRef();
  const imageRef = useRef();
  useEffect(() => {
    if (document.readyState === "complete") {
      const observer = new IntersectionObserver(entries => {
        if (entries[0].isIntersecting) {
          setView(src);
          observer.unobserve(placeholderRef.current);
        }
      });
      if (placeholderRef && placeholderRef.current && isPlaceholder) {
        observer.observe(placeholderRef.current);
      }
    }
  }, [src, isPlaceholder]);
  return /*#__PURE__*/React.createElement(React.Fragment, null, isLoading && /*#__PURE__*/React.createElement("img", {
    src: placeholderSrc,
    alt: "",
    className: placeholderClassName,
    style: placeholderStyle,
    ref: placeholderRef,
    onLoad: () => setIsPlaceholder(true)
  }), /*#__PURE__*/React.createElement("img", {
    src: view,
    alt: alt,
    className: className,
    onLoad: () => setIsLoading(false),
    style: isLoading ? {
      display: "none"
    } : style,
    ref: imageRef
  }));
};
export default LazyImage;
to
import React, { useEffect, useRef, useState } from "react";
import defaultPlaceholder from "./path-to-svg-file"; // import placeholder file
const LazyImage = ({
  placeholderSrc = defaultPlaceholder, // default placeholder
  placeholderClassName,
  placeholderStyle,
  src,
  alt,
  className,
  style
}) => {
  const [isLoading, setIsLoading] = useState(true);
  const [isPlaceholder, setIsPlaceholder] = useState(false);
  const [view, setView] = useState("");
  const placeholderRef = useRef();
  const imageRef = useRef();
  useEffect(() => {
    if (document.readyState === "complete") {
      const observer = new IntersectionObserver(entries => {
        if (entries[0].isIntersecting) {
          setView(src);
          observer.unobserve(placeholderRef.current);
        }
      });
      if (placeholderRef && placeholderRef.current && isPlaceholder) {
        observer.observe(placeholderRef.current);
      }
    }
  }, [src, isPlaceholder]);
  return /*#__PURE__*/React.createElement(React.Fragment, null, isLoading && /*#__PURE__*/React.createElement("img", {
    src: placeholderSrc,
    alt: "",
    className: placeholderClassName,
    style: placeholderStyle,
    ref: placeholderRef,
    onLoad: () => setIsPlaceholder(true)
  }), /*#__PURE__*/React.createElement("img", {
    src: view,
    alt: alt,
    className: className,
    onLoad: () => setIsLoading(false),
    style: isLoading ? {
      display: "none"
    } : style,
    ref: imageRef
  }));
};
export default LazyImage;
Step 3: Create a Patch File
- Once you've made your changes, run the following command to create a patch:
 
npx patch-package @kunalukey/react-image
- This will generate a patch file inside a 
patches/directory: 
The patch file contains a diff of the changes you made.
Step 4: Handle Binary Files (SVG, GIF, etc.)
- 
patch-packageworks best on text-based files like.svgand may not work perfectly on.gifor.mp4files. 
Step 5: Apply Patches After Installing Packages
- The 
postinstallscript will automatically apply patches whenever you run npm install. If needed, you can manually reapply them with: 
npx patch-package name-of-package
Conclusion:
Using patch-package, you can quickly fix bugs, add features, and modify NPM packages without waiting for upstream changes. This is a powerful tool for improving your workflow while keeping your project up-to-date with third-party dependencies.
PS - I am open for freelance work or a remote job π€.
Contact me: kunalukey32@gmail.com
              
    
Top comments (1)
I am open to freelance or remote work π€: Portfolio