Skip to main content

Stop Importing React This Way - Switch to the Wrapper Pattern Instead


Introduction


While working on a real-life project, I encountered an inefficient React.js import strategy. In this blog, I’ll walk you through the problem and how I solved it. Read how I improved the design by creating a more flexible approach using the Wrapper Pattern.

The problem


In one project, I saw lodash & Framer Motion(or any other lib) imported like this:

import _ from 'lodash' -> imported 71.78KB
import { motion } from 'framer-motion' -> imported 111,19KB

In the examples above, you are importing all the libraries.

So, without any other dependencies, you have already included in your build a total of ~180KB. And where are UI libraries, icons library, maybe some chart library?

If your total application size is around 1MB, Framer Motion and Lodash alone could be responsible for 18% of it!

Instead, you should import it more concretely like this:

import methodName from 'lodash/methodName';

// before:
import _ from 'lodash' -> imported 71.78KB
// after:
import debounce from "lodash/debounce"; -> imported 3.41KB
import merge from 'lodash/merge'; -> imported 16KB

Now this looks like a solved thing. Well, it’s not totally. Imagine the following scenario. You imported this in a total of 10+ files. Now you want to change something. You will issue the following problems:

  1. Refactoring all files where you imported it: If you’re using lodash in 10+ different files, you’ll have to change how you import lodash in each of those files. This means you would have to refactor 10 files, which can be very tedious. This will result in a big unnecessary PR.
  2. Potential for missing changes: It’s easy to miss some files that require updates, leading to inconsistent behavior or errors in certain parts of the codebase.
  3. Difficulty in managing multiple branches: Imagine you get PR merge conflicts. And the conflict is the different way of importing in all 10+ modified files. You need to check each of them. With a wrapper, you only see that file and know which version to proceed with.

More flexible way | Wrapper Pattern


Create a LodashWrapper.tsx file:

import debounce from "lodash/debounce"; -> 3.41KB
const lodashWrapper = {
debounce
};
export default lodashWrapper;

And now you can use it wherever you need through your codebase:

import lodashWrapper from './lodashWrapper';
const SearchInput = () => {
const [query, setQuery] = useState('');
  const handleSearch = useCallback(
lodashWrapper.debounce((searchTerm) => {
console.log('Searching for:', searchTerm);
}, 500),
[]
);
// other code

Wrapper is not for reducing the bundle size. Wrapper just offers additional flexibility and easier maintenance. If you want to reduce the bundle size, be sure to use direct imports in your project.


Visual representation


Because we used direct imports, now our build looks like this:

With Gzip, it shrinks to 13.88 KB

Generated using vite-bundle-visualizer.

But why?


Why should you always create a wrapper for your main libraries?

  1. Developers in the codebase know what to use and import -With a wrapper, developers don’t need to think about the best way to import a library each time. They just use the predefined optimized wrapper.
  2. Avoiding Redundant Imports — If every component manually imports a specific thing from lodash, for example, there’s a risk that different components may import different parts of the library.
  3. Ease of Maintenance — If a library updates its exports or introduces a more optimized way to import, you only need to update the wrapper file instead of refactoring every single component.

But be careful. There are some downsides, such as:

  • Added Complexity — Wrappers introduce an extra layer of abstraction.
  • Increased File Count — If you create multiple specialized wrappers (e.g., lodashWrapper.tsx, motionWrapper.tsxYou might end up with too many additional files, making navigation harder.

How to choose the right library?


When selecting a library, it’s essential to consider how it handles imports. For example, if you’re deciding between Recharts and Nivo chartsOne major factor is how they manage imports.

Recharts does not yet support direct imports for individual components, meaning you must import the entire library, which increases the bundle size.

Nivo, on the other hand, allows tree-shakable imports, meaning you can import only the specific chart components you need, reducing the final bundle size.

Example of how Recharts forces large imports:

import { BarChart } from 'recharts'; // Includes unnecessary code

With Nivo, you can optimize your imports:

import { ResponsiveBar } from '@nivo/bar' // smaller size

This flexibility makes Nivo a better choice for performance-conscious applications.

Notes:

  1. This section was not intended to criticize Recharts but was used for a practical demonstration.
  2. Recharts will address this issue in the upcoming v3. You can check my open issue in their repository.

How to track the import size?


You can easily track how much you’ve imported in Visual Studio Code using the VS Code “Import Cost” extension.

Example:

Thank you for reading.

Comments

Popular posts from this blog

CSS only Click-handlers You Might not be using, but you should

  You’re building a simple website, a good-looking landing page with a “See More” button. Instinctively, you reach for JavaScript to handle the button click event. But wait — what if I told you that CSS alone could do the job? Yes. CSS is often underestimated, but it can handle click interactions without JavaScript. In this guide, you’ll learn how to create CSS-only click handlers using the :target pseudo-class, and explore scenarios where this approach makes perfect sense. The :target Pseudo-Class CSS offers several pseudo-classes that let you style elements based on different states ( :hover , :focus , :checked ). But there’s one you might not have used before —  :target . The :target pseudo-class applies styles to an element when its ID matches the fragment identifier in the URL (the part after # ). This behavior is commonly seen when clicking an anchor link that jumps to a section on the same page. Here’s a simple example : <a href="#contact">Go to Contact</...

Sharpen Your Front-End Skills: Quick HTML, CSS & React Interview Challenges

  The source of this image is Chat GPT based on writing! Are you preparing for front-end developer interviews and looking for practical, hands-on ways to improve your HTML, CSS, and React skills? Whether you’re a beginner aiming to build confidence or an experienced developer brushing up on UI skills, small, targeted challenges can make a huge difference. In this article, I’ll walk you through some of the best free and low-cost resources that offer real-world front-end tasks — perfect for interview prep, portfolio building, and daily practice. 1. Frontend Mentor frontendmentor.io Frontend Mentor is one of the most popular platforms for hands-on HTML, CSS, and JavaScript challenges. You get beautifully designed templates (in Figma or image formats) and are asked to bring them to life using clean code. The platform offers difficulty levels ranging from newbie to expert, and it’s perfect for practicing responsiveness and semantic HTML. Bonus : You can even filter for React-based ...

6 Essential JavaScript Concepts Every Developer Should Understand

It’s the only language I’ve used where [] == ![] it's true and where you can, typeof null and somehow get 'object' . But despite all its quirks (and there are many), there are a few core concepts that make life with JS not just easier, but saner. This isn’t some computer science flex. These are practical concepts that, once you understand them, make you write better, cleaner, and less buggy code. 1. Hoisting  Before you rage at your variables being undefined , understand this: JS hoists variable and function declarations to the top of their scope. But —  and this is important  —  only the declarations , not the assignments. Why? Because JS reads it like: This is also why let and const behave differently — they’re hoisted too, but live in the “Temporal Dead Zone” until declared. 2. Closures Closures are like little memory vaults for your functions. They allow functions to remember variables from the scope they were created in, even after that scope has gone. Why care? T...