Path Aliases That Don’t Break Production (@/ Without Regret)

How to use tsconfig paths safely across Next.js, bundlers, Node scripts, and tsc builds—without ‘works locally, fails in prod’.

F

Frontend Interview Team

March 01, 2026

3 min read
Path Aliases That Don’t Break Production (@/ Without Regret)

What you’ll learn

  • What baseUrl + paths actually do (and what they don’t do)
  • Why TS path aliases can compile but fail at runtime
  • Safe patterns for Next.js, bundlers, and Node scripts
  • A production checklist to keep aliases boring

30‑second interview answer

tsconfig.paths only affects TypeScript’s type-checker (and some tooling). It does not rewrite runtime imports. Your runtime (Node) or bundler (Next/Vite/Webpack) must also understand the alias, otherwise you get runtime MODULE_NOT_FOUND. The safe approach is to rely on framework-native aliases (like Next’s @/ convention), or ensure the same resolver is configured for TS and the runtime.


Mental model: TS resolves, runtime loads

When you write:

import { db } from "@/lib/db";

You need two different systems to agree:

  1. TypeScript: “where is @/lib/db?”
  2. Runtime: “where is @/lib/db?”

If only TS understands it, you get a build that type-checks but crashes when executed.


The TS config pieces

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

What this does

  • Helps TS + editors resolve @/x./src/x.

What this does NOT do

  • It does not transform emitted JavaScript.
  • Node won’t magically start understanding @/.

Safe setups by environment

Next.js already supports path aliases when configured, and it runs through its bundler.

Rule: keep paths aligned with your Next project root and avoid fancy multi-alias webs.

Example:

  • @/*./ or ./src/* (pick one and stick to it)

2) Vite/Webpack (bundler apps)

You must configure alias in the bundler too.

  • TS: paths
  • Bundler: resolve.alias

If these drift, you get “editor ok, runtime broken”.

3) Node scripts (no bundler)

If you execute TS/JS directly in Node (cron scripts, migrations):

  • Either don’t use aliases (boring but reliable)
  • Or use a runtime that supports TS path mapping (e.g. tsx/ts-node + path mapping support)
  • Or compile with a build step that rewrites paths (bundler)

Production bias: for Node scripts, prefer relative imports.


Anti-patterns

Anti-pattern 1: Aliases across package boundaries

In monorepos, @/ can mean different roots. You end up with imports that compile in one package and break in another.

Anti-pattern 2: Aliases to dist/

Never alias to build output. It creates circular build assumptions.


Debugging playbook

If something fails in prod:

  1. Inspect the emitted JS: does it still contain @/…?
  2. Identify the runtime: Node? Next server? Edge runtime?
  3. Confirm the resolver that runtime uses supports aliases

Production rule of thumb

  • If Node executes it (no bundler): avoid aliases.
  • If a bundler executes it: configure aliases in exactly one place and keep TS in sync.
  • Keep aliases minimal: one canonical alias beats five cute ones.

Interview questions

  1. Q: What does paths do in TypeScript?

    • A: It affects how TS resolves module specifiers for type-checking/tooling; it doesn’t rewrite runtime imports.
  2. Q: Why do aliases sometimes work in dev but fail in prod?

    • A: Dev bundler resolves aliases, but prod runtime path is different (or the build output still contains alias paths).

Quick recap

  • paths is not a runtime feature.
  • Ensure bundler/runtime resolver matches TS.
  • Prefer boring: one alias, consistent root, minimal magic.