Files and folders
Status: 🟩 COMPLETE Last updated: 2026-06-20 Plain-English tagline: Paths, extensions, hidden files, and why
.envis special. The structure on your disk that everything else assumes you understand.
In plain English
A file is a chunk of named data stored on a disk. A folder (also called a directory) is a container for files — and for more folders. Together they form a tree-shaped structure on every computer.
You see them in Windows Explorer or macOS Finder as icons. Underneath, they’re addresses in a giant tree:
C:\Users\georg\Documents\my-project\app\page.tsx
└──┬──┘ └────┬────┘ └───┬───┘ └─┬┘ └──┬──┘
drive user folder project app file
That whole string is a path. Each piece between the slashes is a folder name; the last piece is a file name. Together they uniquely identify one file on the disk.
The tree is what every other system assumes. Programs save their data in specific paths. Code editors open files by path. Your shell navigates folders by path. Git tracks files by their path within the repo. Understanding paths well makes everything else easier.
Why it matters
You don’t need to be a power user of the file system, but several specific concepts come up daily in webapp work:
- Relative vs absolute paths (and why scripts break when run from the wrong folder)
- File extensions (
.tsxvs.tsvs.jsand what each means) - Hidden files (the dotfiles where so much config lives —
.env,.gitignore,.claude/) - Case sensitivity (why your code may break on Linux but not Windows)
- The home directory (
~and where~/.claude/lives)
Paths — absolute vs relative
Absolute paths
Start from the root of the file system. Always unambiguous, always work the same regardless of where you are.
- Windows:
C:\Users\georg\encyclopedia\README.md - macOS/Linux:
/Users/georg/encyclopedia/README.md(or/home/georg/...on Linux)
The leading C:\ or / makes it absolute.
Relative paths
Start from your current location (the “working directory”). What they resolve to depends on where you are.
README.md→ the README in the current folder./README.md→ same (the./explicitly means “current folder”)../README.md→ the README in the parent folder../../README.md→ up two levelssubfolder/file.txt→ in a subfolder of the current location
Relative paths are how almost all code imports work in modern projects:
import { Button } from "./components/Button"; // relative
import { Button } from "@/components/Button"; // alias (configured to map to a base path)Why the difference matters
A script that opens ./data.csv works when run from the folder containing data.csv — and fails from anywhere else. This is a constant source of “it works on my machine but not in CI” bugs.
The home directory and ~
Every user has a “home” folder. On macOS/Linux: /Users/yourname or /home/yourname. On Windows: C:\Users\yourname.
The shortcut ~ (tilde) refers to it:
~/Documents/foo.txt=/Users/george/Documents/foo.txt~/.claude/CLAUDE.md=/Users/george/.claude/CLAUDE.md
Useful because your home path doesn’t change across sessions but is different per user.
File extensions
The last bit after a . in a filename, conventionally indicating the file type:
| Extension | What it is |
|---|---|
.md | Markdown (text with light formatting) |
.txt | Plain text |
.json | JSON data |
.js | JavaScript |
.ts | TypeScript |
.tsx | TypeScript with JSX (React components) |
.css | CSS stylesheet |
.html | HTML web page |
.png / .jpg / .gif / .webp | Images |
.svg | Vector image (XML) |
.pdf | PDF document |
.exe | Windows executable |
.app | macOS application bundle |
.zip | Compressed archive |
.env | Environment variables (text file) |
.gitignore | Files Git should ignore (text file) |
.config.ts | TypeScript config file (e.g. next.config.ts, tailwind.config.ts) |
The extension is just a convention. The OS uses it to pick a default app to open the file. Programs may also use it as a hint. But renaming foo.png to foo.jpg doesn’t change the file’s content — and tools that read the actual bytes will know it’s still a PNG.
For source code, extensions matter a lot to compilers and bundlers:
.jsfiles are JavaScript.tsfiles are TypeScript (compiled to JS).tsx/.jsxfiles have JSX (React markup) and need a JSX-aware compiler
A TypeScript compiler will reject a .js file with TS-specific syntax; a JavaScript bundler may fail on a .ts file. The extension tells tools what to expect.
Hidden files (dotfiles)
Files whose names start with a . are considered “hidden” on Unix-like systems (and somewhat on Windows). Examples:
.git/— Git’s internal data.gitignore— what Git ignores.env/.env.local— environment variables.vscode/— VS Code project settings.next/— Next.js build cache.claude/— Claude Code per-project config.DS_Store— macOS folder metadata (annoying).bashrc/.zshrc— shell config in your home
By convention, hidden = “not for general browsing.” ls doesn’t show them by default; ls -a does. File managers usually have a “show hidden files” toggle.
This convention is the entire reason dotfiles exist. You don’t want your home folder cluttered with config files you never directly touch, so they’re hidden by default.
Why .env is special
.env files contain secrets (database passwords, API keys). They are:
- Hidden (the leading dot)
- Always gitignored (you don’t commit secrets to version control)
- Loaded automatically by frameworks (Next.js reads
.env.localon startup)
Three variants you’ll see:
.env— defaults for all environments.env.local— local override, gitignored.env.development/.env.production— environment-specific
Treat any .env* file as containing secrets unless you’ve verified otherwise. Never commit them. Never share them in chat.
Path separators
The character between folder names in a path differs by OS:
| OS | Separator | Example |
|---|---|---|
| Linux, macOS | / (forward slash) | /Users/george/file.txt |
| Windows | \ (backslash) — also accepts / | C:\Users\georg\file.txt |
Modern Node.js, Git, and most cross-platform tools accept both. But some scripts and shells require the native separator. Use the path module in Node:
import path from "node:path";
const filepath = path.join("folder", "subfolder", "file.txt");
// Returns the right separator for the current OSFor paths you write in code, forward slash is usually safer (works on both). For paths you type in a shell, use whatever the shell expects.
Case sensitivity
A subtle source of bugs:
- Linux — case-sensitive.
Button.tsxandbutton.tsxare DIFFERENT files. - macOS — case-INsensitive by default (but case-preserving).
Button.tsxandbutton.tsxare the SAME file. - Windows — case-INsensitive. Same.
Most production environments (Vercel, AWS, etc.) run Linux. If you write import Btn from './button' on Windows where the file is actually Button.tsx, it works locally. On Vercel, it fails: “Module not found.”
Habit: match imports exactly to filenames. Always.
What’s where on a typical Next.js project
The standard layout:
my-project/
├── .git/ Git data (hidden)
├── .next/ Build cache (hidden, gitignored)
├── .vscode/ VS Code settings (hidden)
├── .env.local Secrets (hidden, gitignored)
├── .gitignore What Git skips (hidden)
├── node_modules/ Dependencies (gitignored)
├── app/ Next.js App Router pages
│ ├── layout.tsx
│ ├── page.tsx
│ └── ...
├── components/ Reusable UI components
├── lib/ Helper code
├── public/ Static files (images, fonts)
├── package.json Project metadata + dependencies
├── package-lock.json Locked dep versions
├── tsconfig.json TypeScript config
├── tailwind.config.ts Tailwind config
├── next.config.ts Next.js config
├── README.md Human docs
├── CLAUDE.md Project-specific Claude rules
├── AGENT_GUIDE.md Map for AI agents
└── PROGRESS.md Build log
Knowing this layout makes Claude Code sessions faster — you can refer to files by relative path knowing where they live.
A concrete example: relative paths in imports
// app/post/[id]/page.tsx
import { Button } from "@/components/Button"; // alias → ./components/Button
import { getPost } from "../../../lib/posts"; // relative — fragile
import { getPost as get2 } from "@/lib/posts"; // alias — preferredAliases (configured in tsconfig.json) let you write @/lib/posts regardless of where the importing file is. Relative paths (../../../) break if you move files.
Modern Next.js projects come with @/* aliased to the project root. Use it.
Common gotchas
-
Hidden files can hide problems. A misconfigured
.gitignoremay exclude files you wanted to commit. A leaked.envmay be in your commit history. Always check.git-related dotfiles when joining a project. -
node_modules/is huge. Often hundreds of MB to GB. NEVER commit it. The.gitignoreshould always include it. -
Spaces in folder names cause shell trouble. “Program Files” needs to be quoted:
cd "Program Files". Avoid spaces in your own project folder names. -
macOS hides extensions by default. “my-file.txt” might show as “my-file” in Finder. Toggle “show all filename extensions” in Finder preferences to see the truth.
-
Case-changes confuse Git. Renaming
Button.tsxtobutton.tsxon a case-insensitive OS doesn’t change anything from Git’s view (it still thinks the file isButton.tsx). You needgit mv -f Button.tsx button.tsxor a two-step rename. -
Long paths on Windows. Windows historically had a 260-character path limit. Modern Windows can be configured to accept longer paths, but some tools still fail. Keep your project path short (
C:\Users\georg\my-projectnotC:\Users\georg\Documents\Personal Projects\Work In Progress\my-project). -
./is needed for explicit current directory. Some shells require./script.shto run a script in the current folder; justscript.shmay not find it (depends onPATH). -
~only expands in shells, not in code. In JavaScript,'~/foo'is a literal string. Useprocess.env.HOMEoros.homedir()to get the actual home path. -
Renaming files doesn’t update imports. If you rename
Button.tsxtoIconButton.tsx, every place that importsButtonbreaks. VS Code’s “rename symbol” handles this; raw OS rename does not. -
Pasting paths from Windows Explorer uses
\. When you paste a path from Windows Explorer into code, it has backslashes. In JavaScript strings,\is the escape character (\n,\t). You need to either use forward slashes or double-escape backslashes (\\). -
Symbolic links (symlinks) blur paths. A symlink is a file that points to another path. Some tools follow symlinks transparently; others don’t. Mostly relevant for advanced users.
-
.gitignoreonly ignores untracked files. If a file is already tracked by Git, adding it to.gitignoredoesn’t remove it. Usegit rm --cached <file>first. -
File permissions matter on Linux. When deploying scripts, sometimes you need
chmod +xto make them executable. Windows has no exact equivalent — execute permission is implicit.
See also
- command line 🟩 — how you navigate files
- Operating systems intro 🟩 — what manages the file system
- What is a program? 🟩 — programs are files too
- Git basics 🟩 — Git tracks files
- Environment variables 🟩 —
.envfiles in detail - The memory system 🟩 🟦 — uses files heavily
- Glossary: Path
Sources
- MDN — File paths
- The Linux Filesystem Hierarchy — the canonical Linux file layout
- Microsoft — Long paths in Windows