VOOZH about

URL: https://blog.logrocket.com/how-detect-dead-code-frontend-project/

โ‡ฑ How to detect dead code in a frontend project - LogRocket Blog


2022-04-26
1513
#typescript#webpack
Iva Kop
105646
๐Ÿ‘ Image

See how LogRocket's Galileo AI surfaces the most severe issues for you

No signup required

Check it out

Having a large amount of dead code in a project can be detrimental to your app for many reasons. Dead code makes the codebase substantially harder to maintain at scale. It also has the potential to create confusion within the development team as to which pieces of code are relevant and actively worked on and which ones can be safely ignored.

๐Ÿ‘ Tool Logos Over Code Background

The best way to avoid these pitfalls is to ensure we have the appropriate tooling in place to allow us to detect dead code both reliably and automatically.

In this article, weโ€™ll cover three ways to detect dead code in your frontend project, plus an extra bonus section:

๐Ÿš€ Sign up for The Replay newsletter

The Replay is a weekly newsletter for dev and engineering leaders.

Delivered once a week, it's your curated guide to the most important conversations around frontend dev, emerging AI tools, and the state of modern software.

Using ESLint to detect and remove dead code

ESLint is perhaps the most widely used JavaScript linter, with nearly 25 million weekly downloads on npm. It is โ€” or at least should be โ€” an integral part of every JavaScript project. Among many other useful things, ESLint allows us to detect unused variables in our files with its aptly named no-unused-vars rule.

This rule protects us from introducing dead code in two ways. First, it will tell us if we have declared a variable that is not used elsewhere in the file:

// Variable is defined but never used
let x;

// Write-only variables are not considered as used.
let y = 10;
y = 5;

// A read for a modification of itself is not considered as used.
let z = 0;
z = z + 1;

Second, it will tell us if there are unused arguments in our functions:

// By default, unused arguments cause warnings.
(function(foo) {
 return 5;
})();

// Unused recursive functions also cause warnings.
function fact(n) {
 if (n < 2) return 1;
 return n * fact(n - 1);
}

// When a function definition destructures an array, unused entries from the array also cause warnings.
function getY([x, y]) {
 return y;
}

To enable the rule, you can simply add it to the rules object in your ESLint configuration file. The "extends": "eslint:recommended" property in the same file also enables the rule. Find more information on ESLint rules and how to configure them here. Most of the behavior described above is configurable and can be tweaked to fit specific project needs.

The no-unused-vars ESLint rule is an excellent tool for dead code detection. Its usage is strongly recommended both for local development and as part of a continuous integration pipeline.

But on its own, this rule is not enough to ensure we detect all dead code in our project. How come?

The vast majority of modern frontend projects use ECMAScript modules to organize and reuse code via imports and exports. This means that even if a variable or function is not used within the file, as long as it is exported, it is no longer considered unused.

// Variable is defined but never used
const x = 10

// The no-unused-vars rule is not broken
export const y = 20

We seem to have hit the limit of this ESLint rule. So, how can we know all exported code is imported and used elsewhere in our project and, therefore, not dead?

We can continue using our linter and take advantage of a plugin called eslint-plugin-import and, more specifically, its rule no-unused-modules, which allows us to detect both modules without exports, as well as exports that are not imported in other modules.

To set it up, simply install it:

npm install eslint-plugin-import --save-dev

Then, add the plugin and the rule to the ESLint configuration file:

"plugins: {
 ...otherPlugins,
 'import',
},
"rules: {
 ...otherRules,
 "import/no-unused-modules": [1, {"unusedExports": true}]
}

While the plugin is excellent and actively supported, there still might be good reasons to use another approach. For example, what if our project is not using ESLint at all? Perhaps we want to detect unused imports and exports only in our CI pipeline to make our linter lighter for speedier local development, and we donโ€™t want to support different configurations for both environments. Or maybe we ran into some of the open issues with this setup.

Whatever the case may be, there is an alternative solution: webpack

Using webpack for dead code detection

Webpack is a module bundler that is widely used in modern web apps. Its main purpose is to bundle JavaScript files for usage in a browser. Essentially, webpack is used to create a dependency graph of your application and combine every module of your project into a bundle. This makes the tool perfectly positioned to detect unused imports and exports, i.e., dead code.

While webpack will automatically attempt to remove unused code in the bundle it produces (learn more about tree-shaking here), there is a handy plugin to help us detect unused files and exports in our code in the process of writing it โ€” webpack-deadcode-plugin.

To add the plugin to your project, first install it:

npm install webpack-deadcode-plugin --save-dev

Now add it to your webpack configuration file, like so:

const DeadCodePlugin = require('webpack-deadcode-plugin');

const webpackConfig = {
 ...
 optimization: {
 usedExports: true,
 },
 plugins: [
 new DeadCodePlugin({
 patterns: [
 'src/**/*.(js|jsx|css)',
 ],
 exclude: [
 '**/*.(stories|spec).(js|jsx)',
 ],
 })
 ]
}

The plugin will then automatically report unused files and unused exports into your terminal. Itโ€™s a useful tool that can enhance the development process and ensure we are not introducing dead code in a project.

But it has its limitations.

First, the toolโ€™s output in the terminal can get lost or be difficult to parse, depending on other outputs shown in the same place. This can make it inconvenient to use.

Second, it might be slightly more difficult to include it in your CI pipeline. The reason is that, according to the documentation:

The plugin will report unused files and unused exports into your terminal but those are not part of your webpack build process, therefore, it will not fail your build

Finally, and perhaps most importantly, the pluginโ€™s output might be incorrect when using it in a TypeScript project.

Lucky for us, TypeScript itself can be used for dead code detection!


Over 200k developers use LogRocket to create better digital experiences

๐Ÿ‘ Image
Learn more โ†’

Using TypeScript to detect dead code

Using TypeScript in our project has a number of advantages. One of them is that it provides us with an easy way to detect dead code.

First, we can configure TypeScript in a way that doesnโ€™t allow for unused local variables and function parameters. This is similar to the no-unused-vars ESLint rule above. To enforce these rules via TypeScript, we can add them to our tsconfig file:

{
 "compilerOptions": {
 ...otherOptions,
 "noUnusedLocals": true,
 "noUnusedParameters": true,
 }
}

But, even with these checks in place, we still face the issue of unused exports. Letโ€™s use ts-prune for the job. It is easy to use and requires very little (if any) configuration.

To use it, we need to install it:

npm install ts-prune --save-dev

Then, add a script for it to our package.json file:

{
 "scripts": {
 "find-deadcode": "ts-prune"
 }
}

Now, every time we run npm run find-deadcode in our project, we will have a list of all unused exports.

Note that the script above will detect unused exports, even if they are used internally within the module. To opt out of this behavior, modify your script to exclude these:

"find-deadcode": "ts-prune | grep -v '(used in module)'"

Finally, if you want to use ts-prune in CI, you will need to change the exit code so that an error occurs when there are unused exports. Hereโ€™s the final modification:

"find-deadcode": "ts-prune | (! grep -v 'used in module')"

Of the presented options, TypeScript can arguably enforce dead code detection in the strictest way. So, if you are working on a TypeScript project, using Typescript-specific tooling โ€” including the compile options in tsconfig , @typescript-eslint/eslint-plugin (to combine with ESLint) and ts-prune โ€” is often going to be the optimal approach.

Bonus: Detect unused dependencies with depcheck

While weโ€™re on the subject of dead code detection, letโ€™s briefly discuss how to ensure we donโ€™t have unused dependencies in our project. Letโ€™s use depcheck, a tool for analyzing the dependencies in a project. It can tell us:

  • How each dependency is used
  • Which dependencies are useless
  • Which dependencies are missing from package.json

Install it with:

npm install -g depcheck

Next, run the check.

npx depcheck

Depcheck uses a special component that allows us to recognize dependencies used outside of the regular import/export flow. These include dependencies used in configuration files, npm commands, scripts, and more.

Conclusion

In this article, we explored different approaches to detect dead code in your frontend project. These approaches can be used both interchangeably and in combination with one another. As is often the case, choosing our ideal setup depends heavily on the particular use case.

Itโ€™s also important to note that these are not the only existing dead code detection tools. They were selected based on the prevalence and popularity of the underlying tools (ESLint, webpack, and TypeScript). But, depending on the particularities of the project, the optimal solution might not be on this list.

If you found this article useful, visit my blog and follow me on Twitter for more tech content.

Happy coding! โœจ

LogRocket understands everything users do in your web and mobile apps.

๐Ÿ‘ LogRocket Dashboard Free Trial Banner

LogRocket lets you replay user sessions, eliminating guesswork by showing exactly what users experienced. It captures console logs, errors, network requests, and pixel-perfect DOM recordings โ€” compatible with all frameworks, and with plugins to log additional context from Redux, Vuex, and @ngrx/store.

With Galileo AI, you can instantly identify and explain user struggles with automated monitoring of your entire product experience.

Modernize how you understand your web and mobile apps โ€” start monitoring for free.

๐Ÿ‘ Image
๐Ÿ‘ Image
๐Ÿ‘ Image

Stop guessing about your digital experience with LogRocket

Get started for free

Recent posts:

Debug Next.js apps with AI agents and next-browser

Learn how next-browser gives AI agents runtime context for debugging Next.js apps, including React props, hydration, PPR, forms, and performance.

๐Ÿ‘ Image
Emmanuel John
Jun 17, 2026 โ‹… 9 min read

Stop hardcoding LLM SDKs: Dynamic LLM routing with OpenRouter and Next.js

Build dynamic LLM routing in Next.js with OpenRouter, TanStack AI, task classification, model fallbacks, and cost-aware routing.

๐Ÿ‘ Image
Chizaram Ken
Jun 16, 2026 โ‹… 13 min read

What is TSRX?: What JSX would look like if it were designed today

TSRX adds first-class control flow, conditional hooks, and scoped styles to React via a TypeScript compiler extension โ€” no new framework required.

๐Ÿ‘ Image
Ikeh Akinyemi
Jun 12, 2026 โ‹… 6 min read

How to add authentication to a React Native app with Better Auth

Learn how to build a full React Native auth system using Better Auth and Expo โ€” with email/password login, Google OAuth, session persistence, and protected routes.

๐Ÿ‘ Image
Chinwike Maduabuchi
Jun 9, 2026 โ‹… 13 min read
View all posts

Hey there, want to help make our blog better?

Join LogRocketโ€™s Content Advisory Board. Youโ€™ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.

Sign up now