path.extname()

Returns the file extension of a path, from the last dot in the last component.

Signature
path.extname(path)
  • Added in Node.js v0.1.25

Parameters

ParameterTypeDescription
$pathstringThe path to take the extension from.

Return value

stringThe extension from the last occurrence of '.' to the end of the last portion of the path, or an empty string if there is none.

Errors

Throws a TypeError (ERR_INVALID_ARG_TYPE) if path is not a string.

Changelog

  • PHP v6.0.0Passing a non-string as the path argument now throws instead of coercing.

path.extname() returns the substring from the last . in the final path component through the end of the string, including the leading dot. Dots in directory names are ignored, so path.extname('/srv/v1.2/README') is an empty string.

What trips people up

The rule is deliberately narrow, and three cases catch people out:

  • Dotfiles have no extension. path.extname('.gitignore') is '', because a dot at the very start of the component begins the name rather than an extension. But path.extname('.env.local') is '.local' — the leading dot is exempt, later ones are not.
  • Only the last dot counts. path.extname('archive.tar.gz') is '.gz'. There is no built-in notion of a compound extension; match those yourself.
  • A trailing dot returns a lone dot. path.extname('notes.') is '.', which is truthy and one character long, so if (ext) checks treat it as a real extension.

The comparison you write afterwards is your own, and case is preserved verbatim — '.PNG' comes back as '.PNG', so lowercase before comparing against a whitelist of types.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Routing a file to the right parser based on whether it is .json, .yaml or .csv
  • Filtering a directory listing down to a set of allowed extensions
  • Setting a Content-Type header from an asset's suffix

Frequently asked questions

How do I get a file extension in Node.js?

Call path.extname(filePath), which returns the extension including its leading dot, such as '.json'. It reads only the last dot in the final path component, so 'archive.tar.gz' yields '.gz' and a file with no dot yields an empty string.

Why does path.extname return an empty string for .gitignore?

Because a dot at the start of the filename begins the name rather than an extension, so dotfiles are treated as having no extension. A later dot is still an extension, which is why '.env.local' returns '.local'.

Does path.extname include the dot?

Yes, the returned string always starts with the dot — path.extname('index.html') is '.html', not 'html'. Strip it with .slice(1) if you need the bare suffix, and remember that an empty result means there was no extension at all.

More examples

Filtering a directory listing by extension

Both a.json and b.JSON are matched because the extension is lowercased before comparison — extname() itself preserves case exactly as written. The .gitignore entry is excluded automatically, since its extension is the empty string.

Example · javascript
const path = require('node:path');
const fs = require('node:fs');

fs.mkdirSync('extname-demo', { recursive: true });
for (const f of ['a.json', 'b.JSON', 'c.txt', '.gitignore']) {
  fs.writeFileSync(path.join('extname-demo', f), '');
}

const json = fs.readdirSync('extname-demo')
  .filter((f) => path.extname(f).toLowerCase() === '.json');

console.log('json files:', json.sort());

fs.rmSync('extname-demo', { recursive: true, force: true });
console.log('cleaned up:', fs.existsSync('extname-demo'));

Handling compound extensions yourself

extname() returns '.gz' and '.ts' for the first two, which is rarely what you want for tarballs or TypeScript declaration files. Checking an explicit list of compound suffixes first and falling back to extname() covers both shapes.

Example · javascript
const path = require('node:path');

const COMPOUND = ['.tar.gz', '.tar.bz2', '.d.ts'];

function fullExtension(file) {
  const base = path.basename(file);
  const hit = COMPOUND.find((c) => base.toLowerCase().endsWith(c));
  return hit ?? path.extname(base);
}

for (const f of ['archive.tar.gz', 'types.d.ts', 'photo.jpeg', '.bashrc']) {
  console.log(f.padEnd(16), '->', JSON.stringify(fullExtension(f)));
}

Swapping one extension for another

The second line shows extname()'s single-dot rule in action: 'export.tar.gz' becomes 'export.tar.zip', not 'export.zip'. The third shows a dotfile keeping its whole name and gaining a suffix, since its extension was empty.

Example · javascript
const path = require('node:path');

function withExtension(file, ext) {
  return path.join(path.dirname(file), path.basename(file, path.extname(file)) + ext);
}

console.log(withExtension('src/pages/about.md', '.html'));
console.log(withExtension('data/export.tar.gz', '.zip'));
console.log(withExtension('config/.eslintrc', '.json'));

Discussion

  • Be the first to comment on this lesson.