Audit Your Frontend: How to Find and Remove Underused JS Components in Your Stack
toolingperformancemaintenance

Audit Your Frontend: How to Find and Remove Underused JS Components in Your Stack

jjavascripts
2026-03-11
10 min read
Advertisement

Practical checklist and runnable scripts to find and safely remove underused JS components—bundle analysis, telemetry, and CI steps.

Audit Your Frontend: How to Find and Remove Underused JS Components in Your Stack

Hook: Tool sprawl and hidden JavaScript weight slow feature delivery, inflate costs, and increase security risk. This guide gives a practical, step-by-step audit checklist plus runnable scripts for dependency scanning, usage telemetry, and bundle analysis so you can identify underused components and safely remove them without breaking production.

Quick summary — what you’ll get

  • Zero-fluff, prioritized audit checklist (identify → verify → remove)
  • Ready-to-run scripts for dependency scanning and bundle analysis
  • Telemetry patterns (React/Vue/vanilla/Web Components) to validate runtime usage
  • Safe removal plan with CI checks, canary rollout, and rollback steps
  • 2026 trends that affect removal decisions (ESM, edge runtimes, SBOM/SLSA)

Why this matters in 2026

By 2026 most teams have migrated to ESM-first builds, edge runtimes, and tighter supply-chain policies (SBOMs, SLSA). That reduces the cost of shipping small bundles — but it also makes underused components more visible and costly. A single unused UI library, polyfill, or analytics widget can add tens to hundreds of KB and multiply attack surface across thousands of client sessions.

"Removing underused code is not just an optimization — it's a security and maintenance strategy."

High-level audit checklist (inverted-pyramid)

  1. Inventory — list all packages, internal components, and third-party bundles.
  2. Measure — static dependency analysis + bundle mapping (which files add bytes).
  3. Observe — add lightweight runtime telemetry to measure real usage in production.
  4. Verify — run tests and simulated traffic to ensure no breakages when removing.
  5. Remove safely — feature-flagged rollout, canary, monitor, deprecate, clean up docs.

Tools you’ll use (2026-ready)

  • Static: depcheck, madge, custom AST scans (recasts with acorn / @babel/parser).
  • Bundle: webpack-bundle-analyzer, source-map-explorer, esbuild metafile, rollup-plugin-visualizer.
  • Telemetry: lightweight beacons to your observability platform (e.g., OTLP/otel, Datadog, internal endpoint).
  • Coverage: Istanbul/nyc for test coverage, and V8 code coverage for integration tests.
  • CI: threshold checks (bundle size regression), SBOM checks and SLSA verification tools.

1) Inventory: get a complete list

Start with package.json and internal components. Focus on both third-party packages and internal UI components that can become stale.

// npm script you can run
npm run --silent list:deps

// package.json scripts
// "list:deps": "jq -r '.dependencies,.devDependencies | keys[]' package.json | sort -u"

For monorepos use a script to aggregate all package.json files. Example Node script to list all deps in a repo:

// scripts/collect-deps.js
const fs = require('fs');
const path = require('path');
const glob = require('glob');

const pkgs = glob.sync('**/package.json', {ignore: 'node_modules/**'});
const deps = new Set();
for (const p of pkgs) {
  const obj = JSON.parse(fs.readFileSync(p));
  ['dependencies','devDependencies','peerDependencies'].forEach(k => {
    if (obj[k]) Object.keys(obj[k]).forEach(d => deps.add(d));
  });
}
console.log([...deps].sort().join('\n'));

2) Static dependency scanning

Use automated tools to find likely-unused packages and import graphs.

  1. Run depcheck to find possibly unused npm packages.
  2. Run madge to produce import graphs and detect circular deps and orphaned modules.
# depcheck (may need config for false positives)
npx depcheck --json > depcheck-report.json

# madge - generate a graph and find orphans
npx madge --orphans src > madge-orphans.txt

Notes: depcheck reports false positives for dynamic imports and CSS-only assets. Confirm suspicious results with a search for imports or JSX usage.

// scripts/scan-unused.js
const {execSync} = require('child_process');
const fs = require('fs');
const out = execSync('npx depcheck --json', {encoding:'utf8'});
const report = JSON.parse(out);
fs.writeFileSync('tmp/depcheck.json', JSON.stringify(report, null, 2));
console.log('Unused by depcheck:', Object.keys(report.dependencies || {}));

3) Bundle analysis — find who causes weight

Static dependency lists don't tell you impact. Use bundle analysis and source maps to map bytes back to modules.

Webpack

# run webpack-bundle-analyzer
npx webpack --profile --json > stats.json
npx webpack-bundle-analyzer stats.json --port 8888

esbuild / Vite

# esbuild metafile (Vite uses esbuild under the hood)
esbuild src/main.js --bundle --metafile=meta.json --outdir=build
node -e "console.log(JSON.stringify(require('./meta.json'), null, 2));"

# source-map-explorer (requires source maps from your build)
npx source-map-explorer build/*.js --html bundle-report.html

Tip: generate per-page reports for SPAs where route-level code-splitting exists. Compare entrypoint sizes across pages to locate components that always ship.

4) Runtime telemetry — measure actual usage in production

Static scans and bundle maps are insufficient. You must measure real user interactions. Add lightweight beacons that record component mounts or API calls. Keep telemetry tiny (<50B) and GDPR/consent-aware.

Pattern: on mount, send a non-identifying event to your collector. Aggregate per component over 30–90 days (seasonality matters).

React example (hook)

import {useEffect} from 'react';

export function useComponentBeacon(name) {
  useEffect(() => {
    if (!window.__COMPONENT_BEACON) return;
    // non-blocking navigator.sendBeacon preferred for unload cases
    const payload = JSON.stringify({component:name, ts:Date.now()});
    navigator.sendBeacon('/__component-usage', payload);
  }, [name]);
}

// usage
function MyWidget(){
  useComponentBeacon('MyWidget');
  return <div>...</div>
}

Vue 3 example (composition API)

import { onMounted } from 'vue'
export function useComponentBeacon(name){
  onMounted(()=>{
    try{ navigator.sendBeacon('/__component-usage', JSON.stringify({component:name,ts:Date.now()})) }catch(e){}
  })
}

Vanilla / Web Component

class MyElement extends HTMLElement{
  connectedCallback(){
    try{ navigator.sendBeacon('/__component-usage', JSON.stringify({component:'my-element',ts:Date.now()})) }catch(e){}
  }
}
customElements.define('my-element', MyElement);

Server: accept POST/BEACON to /__component-usage and only store counts per day, per component. Do not store PII.

Data retention & analysis

  • Collect for at least 30–90 days to cover weekly usage patterns.
  • Set thresholds — e.g., remove candidates are components with <1% of sessions or <100 daily hits depending on scale.
  • Correlate with routes and user segments — some features are low-frequency but business-critical.

5) Code coverage & synthetic tests

Use unit and integration coverage to confirm code paths. Instrument E2E tests with V8 coverage to find code not executed by tests.

# run test coverage
npx nyc --reporter=lcov npm test

# capture Puppeteer coverage in CI for critical flows
await page.coverage.startJSCoverage();
// exercise UI
const coverage = await page.coverage.stopJSCoverage();
console.log(coverage);

6) Prioritize removal candidates

Combine signals:

  • unused deps from depcheck
  • low runtime beacon counts
  • large byte-cost from bundle analysis
  • low or zero test coverage
  • security or license risk

Score each candidate and prioritize by risk × cost savings. Removing a 150KB dependency with no production hits but used in some internal dev tool scores high.

7) Safe removal workflow (step-by-step)

  1. Author a removal RFC — describe why, impact, and stakeholders.
  2. Create feature flag — gate removal behind a remote flag (launchdarkly, simple env switch) to toggle behavior in runtime.
  3. Remove in a branch — delete imports, run all tests locally.
  4. CI checks — run test suite, bundle-size regression, linter, SBOM/SLSA checks.
  5. Canary rollout — ship to 1–5% of users or internal team; monitor errors and telemetry.
  6. Full rollout — expand to 100% if stable; update docs and deprecation notes.
  7. Clean up — remove feature flag code paths after a cooldown (e.g., 30 days), update package.json and SBOM.

CI example: fail on bundle-size regression

// package.json scripts
"bundle:build": "vite build --sourcemap",
"bundle:analyze": "node scripts/check-bundle.js"

// scripts/check-bundle.js (simplified)
const fs = require('fs');
const report = JSON.parse(fs.readFileSync('dist/meta.json'));
let total = 0; // sum bytes
for(const f in report.outputs){ total += report.outputs[f].bytes }
const threshold = 300_000; // 300KB
if(total > threshold){ console.error('Bundle too large', total); process.exit(1) }
console.log('Bundle OK', total);

8) Rollback plan and monitoring

Always prepare a rollback plan. For frontends this is typically:

  • Re-enable feature flag
  • Revert the PR in the repo (fast) and redeploy
  • Use CDN cache invalidation or versioned assets to ensure old bundles remain available until removal is safe

Set monitoring alerts for:

  • JS exceptions rate spike
  • API error rate changes
  • Page load or Core Web Vitals regressions

Examples: Removing a UI library with minimal risk

Scenario: You ship a small charting widget library that is 120KB, and telemetry shows <0.5% of sessions use it.

  1. Flag the widget behind feature flag “chart-v1” and default to off for 95% users.
  2. Leave server-side endpoints intact; return a small placeholder when flag is off.
  3. Remove the import from main bundle and create a lazy-loaded chunk that only loads if the flag is enabled.
  4. Run canary for 7 days; watch for errors.
  5. When stable, remove the package and update package.json.

Edge cases: low-frequency but critical components

Not every low-usage component should be removed. Critical-but-rare flows (e.g., payments, legal flows, admin UIs) must be reviewed manually and may need long maintenance windows and stakeholder sign-off.

Security, licensing, and supply-chain checks

2026 best practices expect SBOM generation and SLSA verification for deployables. When you remove packages, update your SBOM and verify that removals don’t break provenance. Also check licenses — removing a GPL or commercial library may simplify legal exposure.

Automation: put checks in CI

Add these pipeline steps:

  • depcheck scan and fail only on high-confidence unused packages
  • bundle-size check (per-entrypoint)
  • telemetry sanity (ensure you have coverage for beacon receiver)
  • SBOM regenerate and SLSA verification
// .gitlab-ci.yml or GitHub Actions snippet
- name: Bundle check
  run: npm run bundle:build && npm run bundle:analyze
- name: Depcheck
  run: node scripts/scan-unused.js

Documentation and team alignment

Communicate removals early. Add a PR template for removals that requires:

  • List of pages/features impacted
  • Telemetry data summary
  • Test plan and rollback steps
// PR template snippet
- [ ] RFC linked
- [ ] Telemetry summary attached (30d)
- [ ] Canary plan <1% done
- [ ] CI bundle checks passed

Advanced strategies (2026)

  • Module federation / remote components: Host rarely-used components as remote modules that are only loaded for certain tenants.
  • Edge compute shipping: Push route rendering to edge functions to avoid shipping client code for rare flows.
  • SBOM-driven pruning: Use your SBOM to detect packages that are no longer referenced anywhere in the build output.
  • AI-assisted scanning: Use code-indexing AI to suggest unused exports and refactor candidates (validate suggestions manually).

Common pitfalls and how to avoid them

  • False positives from static tools: dynamic imports, string-based lookups. Confirm with runtime telemetry.
  • Removing dev-only artifacts: devtools or storybook-only components could be reported as unused; keep them in devDependencies.
  • Seasonal usage: some features spike seasonally — check at least 90 days for seasonal apps.

Actionable checklist (copy + run)

  1. Run inventory script (collect-deps.js).
  2. Run depcheck & madge (depcheck-report.json, madge-orphans.txt).
  3. Build with source maps; run source-map-explorer / bundle-analyzer and save HTML reports.
  4. Deploy lightweight telemetry beacons; collect 30–90 days.
  5. Cross-check against coverage and tests.
  6. Score candidates; open RFCs for top 5 removals.
  7. Follow the safe removal workflow (feature flag → canary → full rollout → cleanup).

Case study (short)

At a mid-size SaaS in late 2025, an audit found a legacy datepicker library (95KB) imported in the app shell. Static scans flagged it; bundle analysis showed it loaded on every page. Runtime telemetry showed <0.2% of sessions used the legacy control. The team lazy-loaded a modern native-date input for 99% users, gated the legacy picker behind an admin flag, and removed the dependency after a 30-day canary. Result: 80KB median page reduction and fewer security maintenance tickets.

Final takeaways

  • Combine signals: static analysis + bundle mapping + runtime telemetry = high-confidence candidates.
  • Prioritize safety: feature flags, canaries, and CI checks prevent regressions.
  • Automate: integrate scans into CI and SBOM generation to keep debt from creeping back.

Next steps — templates & scripts

Clone our starter audit kit (scripts above) into your repo. Add telemetry receivers and a CI job to fail on bundle regressions. If you want, run a 30-day pilot: pick three candidates, instrument beacons, and watch the data. The ROI is typically immediate — smaller bundles, faster deploys, fewer dependencies to monitor.

Call to action

Start your audit today: run the inventory script in this article, deploy the component beacon, and open one RFC for the highest-impact candidate. If you want a tailored checklist or CI templates for your stack (React/Vue/Vite/webpack/monorepo), contact our team at javascripts.shop for an audit consultation and starter kit.

Advertisement

Related Topics

#tooling#performance#maintenance
j

javascripts

Contributor

Senior editor and content strategist. Writing about technology, design, and the future of digital media. Follow along for deep dives into the industry's moving parts.

Advertisement
2026-02-04T08:57:35.258Z