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-package
as a development dependency:
npm install patch-package postinstall-postinstall --save-dev
- Also, add a postinstall script in your
package.json
to 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-package
works best on text-based files like.svg
and may not work perfectly on.gif
or.mp4
files.
Step 5: Apply Patches After Installing Packages
- The
postinstall
script 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: [email protected]
Top comments (1)
I am open to freelance or remote work 🤓: Portfolio