← Back to Blog
DevelopmentNext.jsperformanceReact

Cutting 44% of My JavaScript by Deleting Three Dependencies

The public pages shipped over a megabyte of JavaScript. Three packages accounted for most of it, and all three were doing work CSS already does.

SPSantosh Paudel· September 6, 2026· 5 min read
Table of contents

My public pages were shipping between 780 KB and 1.15 MB of JavaScript. For a portfolio and a blog. Pages that are, functionally, text.

Removing three dependencies cut that by 18–44% depending on the route. None of the removals cost a feature.

The three

A WebGL hero — 885 KB. three, @react-three/fiber and drei, fetched immediately after hydration, rendering an animated background inside the largest-contentful-paint viewport and running a permanent requestAnimationFrame loop for as long as the tab stayed open.

It was a gradient with movement. It is now a CSS gradient with movement. On a phone, the difference to the visitor is that the page arrives sooner and the battery lasts longer.

An animation library — ~190 KB, on every page. Eleven uses across the codebase. All eleven were a fade-in on scroll, a hover lift, or a progress bar. CSS transitions and @keyframes cover all three, and the browser runs them off the main thread.

A typewriter effect. Not a dependency, but the same category of mistake: a setTimeout chain re-rendering a React component once per character, forever, in a loop that never terminated. It is a CSS keyframe cycler now.

The pattern underneath

Each of these was a JavaScript solution to a problem the platform already solves. That is the whole diagnosis. Gradients, transitions, hover states, scroll-triggered reveals — the browser does all of this natively, and has for years.

The reason it happens is that reaching for a package is the fastest path at the moment you are writing the code. The cost does not appear until later, and it appears on someone else's device.

The one that was not a dependency

The scroll-reveal component was creating one IntersectionObserver per instance. With 165 instances site-wide, that is 165 observers, each holding a callback that set React state and triggered a re-render.

One shared observer that toggles a CSS class does the same job:

const observer = new IntersectionObserver((entries) => {
  for (const e of entries) {
    if (e.isIntersecting) {
      e.target.classList.add('is-visible')
      observer.unobserve(e.target)
    }
  }
}, { rootMargin: '0px 0px -10% 0px' })

No state, no re-render, and it stops watching each element once it has fired.

How to find yours

Do not guess. Build with the bundle analyser and sort by size — the biggest offender is usually one you forgot was installed. Then for each large package, ask a narrow question: what does this actually do on the page?

If the answer is a visual effect, CSS probably does it. If the answer is a date format, Intl probably does it. If the answer is real application logic, keep it.

What I would not cut

Worth saying, because this kind of post invites the opposite mistake: I did not remove anything that was doing real work. The Supabase client stays. The analytics stay. Accessibility behaviour stays.

The target was never "less JavaScript." It was JavaScript that was not earning its transfer cost. Three packages, one megabyte, zero features lost — the ones left are there because something would break without them.

Free resource

Get the AI Marketing Prompt Pack

30+ tested prompts for images, captions, video scripts, keywords, and full content systems, delivered instantly.

No spam. Unsubscribe anytime.

Browse all free guides →

Want to implement this with guidance?

Santosh helps founders turn insights like this into real systems.

AI Content Systems

External Resources

Further Reading & Tools

Related Posts

01
6 min
SEONext.js
TodaySEO

One Wrong Constant Cost Me Three Months of Indexing

Search Console reported 33 redirect errors and half my pages missing from the index. The cause was a single string, and every symptom traced back to it.

Read article
02
8 min
Next.jsSupabase
3mo agoDev Sprint

From Idea to Live Site: My Repeatable 48-Hour Website Framework

TheWestNepal.live went from idea to live in 48 hours. The reason was not speed for its own sake — it was a repeatable six-step framework that eliminates decision fatigue at every stage.

Read article
03
9 min
WordPressNext.js
2mo agoProject Deep-Dive

Case Study: Turning a WordPress News Site Into a Modern Platform in 48 Hours

TheWestNepal.live went from a slow WordPress site to a programmatic Next.js platform in 48 hours. Here is how the migration worked and what changed after.

Read article