Unused JavaScript: Why Your Website Is Carrying Dead Weight

8 min read

What is Unused JavaScript?

Imagine paying for a moving truck that's twice as large as you need, then filling half of it with furniture you'll never use in your new home. That's essentially what happens when your website contains unused JavaScript—you're downloading, processing, and storing code that never actually runs.

Unused JavaScript refers to any JavaScript code that's included in your website but never executed during a typical user visit. This includes functions that are never called, conditional code that's never triggered, or entire libraries where only a small fraction of the features are used.

The state of JavaScript bloat:

  • Optimized: Less than 20% unused JavaScript, with under 150KB total payload
  • Partially Optimized: 20-40% unused JavaScript, or 150KB-300KB total payload
  • Unoptimized: More than 40% unused JavaScript, or over 300KB total payload

Why Unused JavaScript Is a Performance Killer

JavaScript is the most expensive asset your website delivers, byte for byte. Unlike images or CSS, JavaScript must be downloaded, parsed, compiled, and executed before it can do its job. Each of these stages impacts performance:

  • Download Time: Every kilobyte of JavaScript must be transferred over the network, which can be particularly slow on mobile connections.
  • Parse and Compile Time: Before execution, JavaScript engines must parse the code into an abstract syntax tree and compile it to bytecode—processes that can take 2-5 times longer on mobile devices than on desktop.
  • Execution Time: When JavaScript runs, it occupies the main thread, potentially blocking user interactions.
  • Memory Usage: Unused JavaScript still consumes memory, which is especially problematic on memory-constrained devices.

The performance impact is significant. Studies have shown that every 100KB of JavaScript adds approximately 100ms of load time on midrange mobile devices. When you consider that many sites ship over 400KB of JavaScript (with up to 50% being unused), that's potentially adding half a second of delay for your mobile users—just in processing time.

Where Unused JavaScript Comes From

Unused JavaScript accumulates in your codebase through several common pathways:

  • Full Library Imports: Including entire libraries when only a small portion of the functionality is needed (like importing all of jQuery for a simple DOM manipulation).
  • Historical Features: Code written for features that have been redesigned or removed, but the original code was never deleted.
  • Just-in-Case Code: Functions and utilities added "just in case" but never actually used.
  • Legacy Browser Support: Polyfills and compatibility code for browsers that may represent a tiny fraction of your actual user base.
  • Deprecated Frameworks: Keeping old framework code alongside newer implementations during gradual migrations.
  • Third-Party Scripts: Analytics, advertising, and marketing tools that include far more functionality than your site actually uses.

The problem compounds over time, especially in larger teams or long-running projects where developers may be hesitant to remove code they didn't write or don't fully understand.

The Compounding Problem

A common pattern emerges in many websites: The initial product launches with lean, purpose-built JavaScript. As new features are added and different teams contribute to the codebase, the JavaScript bundle grows. When features are revised or removed, the old code often remains "just in case." After several years of development, it's not uncommon to find that 30-50% of a site's JavaScript is never executed.

How to Identify Unused JavaScript

Before you can optimize your JavaScript, you need to identify what's going unused. Several tools can help with this analysis:

  • Chrome DevTools Coverage Tab: This built-in tool shows exactly which lines of JavaScript are executed during your browsing session. To use it, open DevTools (F12), open the Command Menu (Ctrl+Shift+P), type "Show Coverage," and then click the record button to start analyzing.
  • Bundle Analyzers: Tools like Webpack Bundle Analyzer or Source Map Explorer provide visualizations of your JavaScript bundles, making it easier to spot large dependencies.
  • Performance Monitoring Tools: Services like Lighthouse, WebPageTest, or Greadme include unused JavaScript detection in their analysis.

When performing your analysis, be sure to:

  • Test multiple user flows, not just the homepage
  • Analyze different types of pages (e.g., product pages, blog posts)
  • Consider both first-visit and return-visit scenarios
  • Test on mobile devices or with mobile emulation enabled

This comprehensive approach will give you a more accurate picture of what JavaScript is truly necessary for your site's functionality.

6 Effective Strategies to Eliminate Unused JavaScript

1. Implement Tree Shaking

Tree shaking is a technique that eliminates dead code by only including the parts of your dependencies that are actually used.

Simple fix: Use modern bundlers like Webpack, Rollup, or esbuild that support tree shaking, and ensure your code is using ES modules (import/export syntax) which enables better dead code elimination:

// Bad: Imports entire library
import _ from 'lodash';

// Good: Imports only what you need
import { debounce, throttle } from 'lodash-es';

// Even better: Import specific functions directly
import debounce from 'lodash/debounce';
import throttle from 'lodash/throttle'; 

Most modern JavaScript bundlers will automatically eliminate unused exports when properly configured, dramatically reducing your final bundle size.

2. Implement Code Splitting

Code splitting breaks your JavaScript into smaller chunks that are loaded only when needed, rather than forcing users to download everything upfront.

Simple fix: Use dynamic imports to load JavaScript on demand:

// Instead of loading everything upfront
import { heavyFeature } from './heavyFeature';

// Load it only when needed
button.addEventListener('click', async () => {
  const { heavyFeature } = await import('./heavyFeature');
  heavyFeature();
}); 

For React applications, you can use React.lazy and Suspense for component-level code splitting:

// Before: Importing directly
import HeavyComponent from './HeavyComponent';

// After: Code-split with dynamic import
const HeavyComponent = React.lazy(() => import('./HeavyComponent'));

function MyComponent() {
  return (
    <React.Suspense fallback={<Spinner />}>
      <HeavyComponent />
    </React.Suspense>
  );
} 

3. Replace Heavy Libraries with Lighter Alternatives

Many popular JavaScript libraries have smaller alternatives that provide similar functionality with a fraction of the code.

Simple fix: Consider these lightweight alternatives to common libraries:

  • Instead of jQuery (87KB), use modern browser APIs or smaller utilities like umbrella.js (4KB)
  • Instead of Moment.js (329KB), use Day.js (2KB) or date-fns (with tree-shaking)
  • Instead of Lodash/Underscore (71KB), use just the specific utilities you need or native JavaScript methods
// Before: Using Moment.js (329KB)
import moment from 'moment';
const formattedDate = moment(date).format('YYYY-MM-DD');

// After: Using Day.js (2KB)
import dayjs from 'dayjs';
const formattedDate = dayjs(date).format('YYYY-MM-DD');

// Or just use native APIs when possible
const date = new Date();
const formattedDate = date.toISOString().split('T')[0]; // 'YYYY-MM-DD'

4. Audit and Optimize Third-Party Scripts

Third-party scripts for analytics, advertising, or marketing often add significant JavaScript bloat.

Simple fix: Implement these practices for third-party scripts:

  • Load non-critical third-party scripts with the defer or async attribute
  • Use tag management systems to control which scripts load and when
  • Regularly audit your third-party scripts and remove those no longer providing value
  • Consider self-hosting third-party scripts to have more control over caching and loading
<!-- Before: Render-blocking script -->
<script src="https://third-party.com/analytics.js"></script>

<!-- After: Deferred loading -->
<script defer src="https://third-party.com/analytics.js"></script>

<!-- Even better: Load based on user interaction -->
<script>
  // Only load when needed
  document.getElementById('consent-button').addEventListener('click', () => {
    const script = document.createElement('script');
    script.src = 'https://third-party.com/analytics.js';
    document.body.appendChild(script);
  });
</script>

5. Implement Effective Bundler Configurations

Modern bundlers have numerous optimization options that can dramatically reduce JavaScript size.

Simple fix: Ensure your Webpack, Rollup, or other bundler configuration is optimized for production:

// Example webpack.config.js optimization
module.exports = {
  mode: 'production', // Enables built-in optimizations
  optimization: {
    usedExports: true, // Enables tree shaking
    minimize: true, // Enables minification
    splitChunks: { // Extracts common dependencies
      chunks: 'all',
      maxInitialRequests: 25,
      minSize: 20000
    }
  }
};

Consider modern bundlers like esbuild or SWC which offer similar optimizations with significantly faster build times.

6. Set Up Size Budgets and Monitoring

Preventing JavaScript bloat is easier than cleaning it up later. Set clear limits on bundle size and monitor increases over time.

Simple fix: Implement bundle size limits in your build process:

// Example webpack configuration with size limits
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin(), // Visualize bundle size
    new webpack.performance.hints('error'), // Fail build on size issues
  ],
  performance: {
    maxAssetSize: 200000, // 200KB limit per file
    maxEntrypointSize: 250000, // 250KB total entry point size
  }
};

You can also use tools like Lighthouse CI to automatically test bundle sizes during your continuous integration process.

Special Considerations and Potential Pitfalls

When optimizing JavaScript, be aware of these common challenges:

Problem: Breaking Functionality When Removing Code

What's happening: Code that appears unused might actually be necessary in certain user flows or scenarios that weren't tested during analysis.

Simple solution: Implement comprehensive testing across all user flows before and after optimization. Consider using feature flags to gradually remove code, allowing for quick rollback if issues are discovered:

// Approach with feature flags
if (featureFlags.useNewImplementation) {
  // New optimized code
} else {
  // Original code (can be removed after testing)
}

Problem: Dynamically Generated Code

What's happening: Some JavaScript might be dynamically generated or evaluated at runtime, making it difficult for static analysis tools to determine if it's used.

Simple solution: Minimize the use of dynamic code generation (eval, new Function(), etc.) when possible. For necessary dynamic code, keep it separate from your main bundles to ensure optimization tools don't incorrectly remove dependencies.

Problem: Aggressive Tree Shaking Side Effects

What's happening: Tree shaking might remove code that has important side effects (like polyfills or self-registering modules).

Simple solution: Mark modules with side effects in your package.json to prevent them from being incorrectly tree-shaken:

// In package.json
{
  "name": "your-package",
  "sideEffects": [
    "./src/polyfills.js",
    "*.css"
  ]
}

Problem: Framework-Specific Dead Code Elimination Challenges

What's happening: Frameworks like React, Angular, or Vue may have specific patterns that complicate dead code elimination.

Simple solution: Use framework-specific optimization tools and patterns:

  • For React: Use React.lazy and React.memo where appropriate
  • For Angular: Use built-in tree-shaking and lazy loading
  • For Vue: Use async components and dynamic imports

The Performance Impact of Removing Unused JavaScript

The benefits of optimizing JavaScript can be substantial:

MetricBefore OptimizationAfter OptimizationImprovement
JavaScript Bundle Size450KB185KB59% reduction
JavaScript Parse Time590ms240ms59% faster
Time to Interactive4.8 seconds2.3 seconds2.5 seconds faster
First Input Delay230ms85ms63% improvement
Lighthouse Performance Score638724 point increase

These improvements are particularly significant on mobile devices and slower connections, where JavaScript processing can be the dominant performance bottleneck. By reducing unused JavaScript, you're not just saving bandwidth—you're freeing up CPU time that can be used to make your site more responsive to user interactions.

Real-World Success Stories

Companies across various industries have seen remarkable improvements from optimizing their JavaScript:

  • E-commerce platform reduced their JavaScript bundle from 410KB to 170KB through code splitting and tree shaking. This decreased mobile load times by 2.3 seconds and increased conversion rates by 15% for mobile users.
  • Media website removed unused third-party scripts and implemented lazy loading, cutting their JavaScript payload by 62%. This resulted in a 40% decrease in bounce rate and a 25% increase in pages per session.
  • SaaS application switched from a large UI framework to a more lightweight alternative, reducing initial JavaScript by 70%. User engagement metrics improved significantly, with a 30% increase in daily active users on mobile devices.
  • Travel booking site implemented code splitting to load only the JavaScript needed for each step of their booking flow. This reduced initial load time by 3.1 seconds and increased funnel completion rates by 17%.

These examples demonstrate that JavaScript optimization can have a direct impact on key business metrics, not just technical performance scores. The less time users spend waiting for JavaScript to download and execute, the more time they spend engaging with your content and converting into customers.

Conclusion: Every Byte Counts

Unused JavaScript is the silent performance killer that affects countless websites. Like carrying unnecessary weight on a long journey, it slows down every step of your users' experience—from initial loading to interactions throughout their visit.

The good news is that the tools and techniques for identifying and eliminating unused JavaScript have never been more accessible. Modern bundlers, frameworks, and development practices make it possible to deliver only the code that's actually needed, significantly improving performance without compromising functionality.

What makes JavaScript optimization particularly valuable is its compounding effect. When you reduce JavaScript payload, you're not just saving download time—you're also reducing parse time, compilation time, execution time, and memory usage. This creates a cascade of performance improvements that benefit every user, but especially those on mobile devices or slower connections.

Remember that JavaScript optimization isn't a one-time effort but an ongoing practice. As your website evolves, regularly audit your JavaScript to ensure you're not accumulating new sources of bloat. With thoughtful attention to your site's JavaScript footprint, you can deliver experiences that feel instantaneous rather than sluggish.

Ready to trim your JavaScript and speed up your site?

Greadme's easy-to-use tools can help you identify unused JavaScript on your website and provide simple, step-by-step instructions for optimization—even if you're not technically minded.

Optimize Your JavaScript Today