Build, Join, and Parse Filesystem Paths — Safely
The path Module
Building paths with string concat breaks on Windows and double slashes. The path module handles it correctly.
What you'll learn
- Join and resolve paths
- Extract parts (basename, dirname, extname)
- Use path.sep correctly
Building paths by string concat is fine until it isn’t:
"foo/" + "/bar" gives foo//bar. Different OSes use different
separators. The node:path module fixes both.
Join
import { join } from "node:path";
const p = join("src", "components", "Button.tsx");
// "src/components/Button.tsx" (or "src\\components\\Button.tsx" on Windows) join normalizes slashes — never doubles them, never leaves
trailing.
Resolve
resolve builds an absolute path from segments, walking up
from process.cwd() if needed:
import { resolve } from "node:path";
resolve("src", "index.js");
// → "/Users/me/project/src/index.js"
resolve("/etc", "host.conf");
// → "/etc/host.conf" Parsing
import { basename, dirname, extname, parse } from "node:path";
const p = "/Users/me/project/src/Button.tsx";
basename(p); // "Button.tsx"
basename(p, ".tsx"); // "Button"
dirname(p); // "/Users/me/project/src"
extname(p); // ".tsx"
parse(p);
// {
// root: '/',
// dir: '/Users/me/project/src',
// base: 'Button.tsx',
// ext: '.tsx',
// name: 'Button'
// } __dirname Replacement in ESM
CJS has __dirname for free. ESM doesn’t — derive it:
import { fileURLToPath } from "node:url";
import { dirname, join } from "node:path";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const data = join(__dirname, "data", "users.json"); Memorize this snippet — you’ll write it on every ESM project.
Cross-Platform
Always use path.join / path.resolve instead of "a/" + "b/".
Same code runs on macOS, Linux, and Windows.