All posts
4 min read

File Paths Explained: Absolute, Relative, and Why 'File Not Found' Happens

Cannot find module './components/Button'? ENOENT: no such file or directory? Most of the time it's a path problem. How file paths work on Mac, Linux, and Windows, absolute vs relative paths, ./ and ../, the working directory, case sensitivity, and import aliases like @/.

getting starteddebuggingtoolingbeginner

Cannot find module './components/Button'. ENOENT: no such file or directory. 404 on an image that's definitely there. A large share of beginner errors come down to one thing: a file path that points somewhere slightly different from where the file actually is.

What a path is

A path is the address of a file or folder: the list of folders you walk through to reach it, separated by slashes.

/Users/ana/projects/plant-tracker/src/components/Button.tsx

macOS and Linux use forward slashes /, and everything starts from /, the root.

Windows traditionally uses backslashes and drive letters:

C:\Users\ana\projects\plant-tracker\src\components\Button.tsx

Most development tools on Windows also accept forward slashes, and in code — imports, config files — always use forward slashes. They work everywhere.

Absolute paths

An absolute path is the full address, starting from the root:

/Users/ana/projects/plant-tracker/.env
C:\Users\ana\projects\plant-tracker\.env

It means the same thing no matter where you are. But absolute paths don't belong in your project's code: they only exist on your computer. Deploy the app and /Users/ana/... doesn't exist on the server. That's a classic "works locally, breaks in production" bug. (Why Does My App Work Locally but Not in Production?.)

Relative paths

A relative path is directions from where you are now:

Path Means
Button.tsx or ./Button.tsx in this folder
./components/Button.tsx in the components folder inside this one
../Button.tsx in the folder one level up
../../lib/utils.ts two levels up, then into lib

. means "this folder." .. means "the parent folder."

In code imports, relative paths are relative to the file doing the importing:

// in src/app/dashboard/page.tsx
import { Button } from "../../components/Button"
// → up to src/app, up to src, into components

Count the folders carefully. Off-by-one ../ is the most common cause of Cannot find module.

Import aliases: @/

Deep ../../../ chains are hard to read and break when files move. Many projects — including most Next.js projects — set up an alias so @/ means "the src folder" (or the project root):

import { Button } from "@/components/Button"

The alias is defined in tsconfig.json (or jsconfig.json):

{
  "compilerOptions": {
    "paths": { "@/*": ["./src/*"] }
  }
}

If @/ imports fail, check that this setting exists and points at the right folder — and that any other tool involved (a test runner, a bundler) knows about the alias too.

The working directory

When a program runs, it has a current working directory: the folder it considers "here." In the terminal, it's the folder you cd'd into. (The Terminal for Beginners.)

pwd       # print working directory (macOS/Linux; also works in PowerShell)

Paths in code that reads files at runtime are often relative to the working directory — not to the source file:

fs.readFileSync("data/plants.json")
// looks in <working directory>/data/plants.json

Run the app from a different folder and it breaks. Safer: build the path from the source file's own location:

import { fileURLToPath } from "node:url"
import path from "node:path"

const here = path.dirname(fileURLToPath(import.meta.url))
const file = path.join(here, "data", "plants.json")

path.join also handles the / vs \ difference between systems for you.

Case sensitivity

This one causes endless confusion:

  • macOS and Windows usually treat Button.tsx and button.tsx as the same file.
  • Linux — which is what almost every server runs — treats them as different.

So import Button from "./button" works on your Mac, and fails on the server with Cannot find module. Always match the exact capitalisation of file names. If you rename a file only by changing case, Git may not notice on Mac or Windows; use git mv button.tsx Button.tsx.

Paths on the web

URLs have paths too, and the same ideas apply: /images/logo.png starts from the site's root; images/logo.png is relative to the current page — which breaks when you're on /blog/post-1. In most frameworks, files in a public/ folder are served from the site root: public/logo.png is at /logo.png. (Anatomy of a URL.)

Spaces in paths

In the terminal, a space separates arguments, so paths with spaces need quotes:

cd "My Projects/plant tracker"

Better still, avoid spaces in project folder and file names altogether.

Debugging a "not found" error

  1. Read the path in the error. What exactly was it looking for?
  2. Check the file exists at that exact path, with that exact capitalisation.
  3. Check where you're starting from — the importing file's folder, or the working directory.
  4. Check aliases in tsconfig.json.
  5. Check file extensions — some setups need .js in imports; others don't.

EasySpawn workspaces run on Linux — the same case-sensitive filesystem your production server uses — so path bugs show up while you're building, not after you deploy. See how it works or join the waitlist.

Related: The Terminal for Beginners · How to Read an Error Message · Debugging for Beginners

Keep reading