The path Module
File paths look deceptively simple until your code has to run on both Linux and Windows. Separators differ (/ vs \), relative segments like .. need normalizing, and string concatenation quietly produces broken paths. Node’s built-in path module handles all of this for you, giving you reliable, platform-aware helpers for building, parsing, and inspecting paths. It is purely a string-manipulation utility — it never touches the filesystem — which makes it fast and safe to use everywhere.
Importing the module
path is a core module, so there is nothing to install. Use the node: prefix to make the import unambiguous and protect against npm packages shadowing it.
import path from 'node:path';
// CommonJS equivalent:
// const path = require('node:path');
Joining path segments
path.join() concatenates segments using the platform’s separator and normalizes the result, collapsing redundant slashes and resolving . and .. segments. It is the right tool for building a path from known pieces.
import path from 'node:path';
const full = path.join('/var', 'www', 'app', 'index.html');
console.log(full);
const messy = path.join('uploads/', '/2026', '..', 'images', 'logo.png');
console.log(messy);
Output:
/var/www/app/index.html
uploads/images/logo.png
Resolving to an absolute path
path.resolve() processes its arguments from right to left, prepending segments until it has built an absolute path. If no absolute segment is supplied, it anchors the result to the current working directory. Reach for resolve when you need a guaranteed absolute path — for example, before passing it to fs.
import path from 'node:path';
console.log(path.resolve('src', 'config.json'));
console.log(path.resolve('/etc', 'app', 'settings.yml'));
console.log(path.resolve('/etc', '/usr', 'local'));
Output:
/home/dev/project/src/config.json
/etc/app/settings.yml
/usr/local
The key difference:
joinsimply glues segments together, whileresolveproduces an absolute path and lets a later absolute segment discard everything before it. Usejoinfor relative composition,resolvewhen you need an absolute anchor.
Inspecting parts of a path
These functions extract individual pieces of a path string. They are purely lexical — the file does not need to exist.
| Function | Returns | Example input | Result |
|---|---|---|---|
path.basename(p) | Last portion (file name) | /app/logo.png | logo.png |
path.basename(p, ext) | File name without extension | /app/logo.png, .png | logo |
path.dirname(p) | Directory portion | /app/logo.png | /app |
path.extname(p) | Extension including the dot | /app/logo.png | .png |
import path from 'node:path';
const file = '/var/www/app/report.final.pdf';
console.log(path.basename(file)); // report.final.pdf
console.log(path.basename(file, '.pdf')); // report.final
console.log(path.dirname(file)); // /var/www/app
console.log(path.extname(file)); // .pdf
Output:
report.final.pdf
report.final
/var/www/app
.pdf
Note that extname reports only the final extension: for archive.tar.gz it returns .gz, not .tar.gz.
Parsing and formatting
path.parse() returns an object with every component at once, which is handy when you need several pieces. path.format() is its inverse, rebuilding a path string from such an object.
import path from 'node:path';
const parsed = path.parse('/var/www/app/report.pdf');
console.log(parsed);
const rebuilt = path.format({
dir: '/var/www/app',
name: 'report',
ext: '.pdf',
});
console.log(rebuilt);
Output:
{
root: '/',
dir: '/var/www/app',
base: 'report.pdf',
name: 'report',
ext: '.pdf'
}
/var/www/app/report.pdf
The platform separator
path.sep is the segment separator for the current platform (/ on POSIX, \ on Windows). It is occasionally useful for splitting a path into its segments, though prefer the dedicated parsing functions where they fit.
import path from 'node:path';
const segments = '/var/www/app'.split(path.sep).filter(Boolean);
console.log(segments);
Output:
[ 'var', 'www', 'app' ]
There is also path.delimiter — : on POSIX, ; on Windows — for splitting environment variables like PATH.
path.posix vs path.win32
By default, the path module operates in the style of the platform your program is running on. Sometimes you need to force a specific convention regardless of the host OS — for instance, generating POSIX-style URLs on a Windows build server, or parsing a Windows path on Linux. Two sub-modules give you that control:
path.posixalways uses forward slashes and POSIX rules.path.win32always uses backslashes and Windows rules (including drive letters).
import path from 'node:path';
console.log(path.posix.join('assets', 'css', 'site.css'));
console.log(path.win32.join('assets', 'css', 'site.css'));
console.log(path.win32.basename('C:\\Users\\dev\\app.js'));
Output:
assets/css/site.css
assets\css\site.css
app.js
When building paths that end up in URLs or are stored in cross-platform files (like manifests), use
path.posixexplicitly so a Windows machine doesn’t emit backslashes.
Best Practices
- Build paths with
joinorresolveinstead of string concatenation — they handle separators and redundant segments correctly. - Use
path.resolve()whenever a downstream API needs an absolute path, anchoring relative inputs to a known directory. - Import core modules with the
node:prefix to avoid accidental shadowing by npm packages. - For module-relative paths in ES modules, combine
import.meta.dirname(Node 20.11+) withpath.joinrather than hardcoding directories. - Use
path.posixwhen generating URL-style paths so the output stays consistent across operating systems. - Reach for
path.parse()when you need multiple components at once instead of callingbasename,dirname, andextnameseparately.