TypeScript Path Aliases Guide for Vite, Next.js, Node, Jest, and ts-node
pathsimportstsconfigtoolingvitenextjsjestts-node

TypeScript Path Aliases Guide for Vite, Next.js, Node, Jest, and ts-node

TTypeScript Page Editorial
2026-06-10
9 min read

A practical checklist for setting up TypeScript path aliases across Vite, Next.js, Node, Jest, and ts-node without resolution surprises.

TypeScript path aliases make imports shorter and easier to refactor, but they also create one of the most common setup mismatches in modern projects: the editor understands an alias, while the bundler, test runner, or Node process does not. This guide gives you a reusable checklist for configuring baseUrl and paths across Vite, Next.js, Node, Jest, and ts-node, plus the practical checks to run when import resolution breaks after a tooling change.

Overview

Here is the main rule to keep in mind: TypeScript path aliases are not a single switch. They are a contract that several tools may need to honor separately.

In most projects, the first step happens in tsconfig.json. That makes your editor and the TypeScript compiler aware of imports such as @/components/Button or @server/utils/env. But TypeScript does not automatically teach every other tool how to resolve those imports at runtime.

That is why alias setups often feel inconsistent. You may see one of these patterns:

  • The IDE autocomplete works, but the dev server fails.
  • The app builds, but tests cannot resolve modules.
  • tsc --noEmit passes, but a Node script crashes at runtime.
  • Next.js works in app code, but a custom Jest config still needs mapping.

A reliable setup usually has four parts:

  1. TypeScript config defines the alias shape.
  2. Build or dev tool resolves the alias during bundling.
  3. Test runner resolves the same alias in tests.
  4. Runtime or script runner resolves the alias in direct execution paths.

Start with a simple example. In tsconfig.json:

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

This says that imports starting with @/ should point to src/. From there, each tool must either read that config directly or be configured with an equivalent mapping.

If you need a broader TypeScript config baseline, see tsconfig.json Best Settings by Project Type.

Checklist by scenario

Use this section as a setup checklist. Pick the scenario that matches your stack and verify each item in order.

1. Base TypeScript setup for any project

Before touching framework-specific settings, make sure the core alias definition is sound.

  • Set baseUrl, usually to . for project-root-relative mappings.
  • Define paths using a consistent prefix such as @/*.
  • Make sure the target path matches your actual source layout.
  • Prefer one clear alias over many overlapping aliases at first.

Example:

\{
  "compilerOptions": \{
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "baseUrl": ".",
    "paths": \{
      "@/*": ["src/*"],
      "@server/*": ["server/*"]
    \}
  \}
\}

Checklist:

  • Does @/* really map to src/*?
  • Are you editing the correct tsconfig file in a multi-config project?
  • Does your app use src, project root, or a framework-specific directory such as app?

2. Vite path aliases

Vite projects often compile TypeScript correctly in the editor but still need explicit alias handling in the Vite config unless a plugin is used.

You generally have two approaches:

  1. Mirror aliases manually in vite.config.ts.
  2. Use a plugin that reads tsconfig paths.

Manual example:

import { defineConfig } from 'vite'
import path from 'node:path'

export default defineConfig({
  resolve: {
    alias: {
      '@': path.resolve(__dirname, 'src')
    }
  }
})

Checklist for Vite:

  • Confirm the alias in vite.config.ts points to the same folder as tsconfig.json.
  • Restart the Vite dev server after changing aliases.
  • If using Vitest, verify whether your test config inherits Vite resolution as expected.
  • If you use a plugin for tsconfig paths, confirm it is loaded in the active config file.

For a broader starter reference, see React + TypeScript Setup Guide for Vite, Next.js, and CRA Alternatives.

3. Next.js TypeScript alias setup

Next.js generally works smoothly with TypeScript aliases when they are defined in the project config, but you still need to verify the exact directories you are targeting.

Typical example:

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

Checklist for Next.js:

  • Use the directory structure your app actually follows: src/*, app/*, or root-level folders.
  • Check whether custom tooling around Next.js, such as Jest or storybook-style tooling, also needs alias mapping.
  • If imports fail only in tests, the Next.js app config may be fine and the test runner may be the real problem.

If your project mixes React conventions and framework-specific config, keep one canonical alias structure and adapt the tools to it, not the other way around.

4. Node.js TypeScript setup

This is where many developers get caught. TypeScript path aliases help type checking and authoring, but plain Node.js does not automatically understand tsconfig path mappings.

That means this may compile:

import { loadEnv } from '@server/config/env'

But direct execution can still fail unless your runtime path resolution is configured.

Checklist for Node:

  • Decide whether your code is bundled before execution or run directly by Node.
  • If bundled, confirm the bundler rewrites or resolves aliases.
  • If run directly, confirm your runtime strategy supports aliases.
  • Be extra careful in scripts, CLIs, migration files, and one-off jobs, because they often bypass the main app build flow.

For Node project foundations, see Node.js + TypeScript Setup That Still Works in 2026.

5. Jest with tsconfig paths

Jest commonly needs its own alias map. Even when your app and editor work, Jest may still report module resolution errors unless the alias is translated into moduleNameMapper.

Example pattern:

module.exports = {
  moduleNameMapper: {
    '^@/(.*)$': '<rootDir>/src/$1'
  }
}

Checklist for Jest:

  • Map regex patterns carefully. A small mismatch can break all aliased imports.
  • Use <rootDir> consistently so paths stay stable across environments.
  • If using multiple aliases, add explicit mappings for each one.
  • Clear Jest cache if the config changed but old failures persist.

If your error looks more like a missing type than a path problem, compare it with How to Fix "Cannot find name" and Other Missing Type Errors in TypeScript.

6. ts-node path aliases

ts-node is another place where path aliases may appear to work in the editor but fail in execution. The key question is whether the runtime process is loading support for tsconfig path resolution.

Checklist for ts-node:

  • Confirm which command actually runs your script in development.
  • Check whether alias support is being registered explicitly.
  • Verify the active tsconfig is the one you expect, especially in monorepos or server/client splits.
  • Test a minimal aliased import in a small script rather than debugging the whole app at once.

In practice, ts-node, Node, and Jest issues often come down to the same root cause: TypeScript knows the alias, but the runtime environment does not.

7. Monorepo or multi-tsconfig projects

In larger repositories, path alias problems are often caused by configuration drift rather than bad syntax.

Checklist for monorepos:

  • Identify the source of truth: root tsconfig.base.json or package-local configs.
  • Make sure extending configs do not accidentally overwrite baseUrl or paths.
  • Avoid defining slightly different meanings for the same alias in different packages.
  • Confirm each tool points at the intended config file.

If you must vary aliases between packages, keep the differences deliberate and documented.

What to double-check

When aliases break, the fix is usually small. The hard part is finding which layer is responsible. Run through these checks before changing multiple files at once.

1. Is the import path valid under the alias?

Check the physical file location and the mapped folder. An import like @/utils/date only works if @/* points to the folder that actually contains utils/date.

2. Are you using the right wildcard pattern?

This is a frequent source of subtle problems:

  • "@/*": ["src/*"] maps nested imports.
  • "@": ["src"] is a different pattern and may not match what you expect.

Use wildcard pairs consistently unless you have a specific reason not to.

3. Are all tools reading the same project root?

In one tool, . may mean the repository root. In another, it may effectively behave relative to a package or config location. If an alias works in one workspace but not another, verify the working directory assumptions.

4. Did you restart the relevant process?

Editors, dev servers, and test runners often cache config. After changing tsconfig.json, restart:

  • the TypeScript server in your editor
  • the dev server
  • the test runner
  • any long-running Node process

5. Are you mixing ESM, CJS, and tool-specific resolution rules?

Module system choices can make alias issues look worse than they are. If you are already dealing with import/export compatibility problems, isolate those from path alias debugging. Fix one axis at a time.

6. Does linting understand the same imports?

If ESLint reports unresolved imports while the app runs fine, you may need to align linting resolution with the project config. That is a separate layer from TypeScript itself. See ESLint + TypeScript Flat Config Guide.

7. Is the error really about paths?

Some errors that look like alias issues are actually missing type declarations, wrong file extensions, or package export mismatches. If you are getting TypeScript diagnostic codes, check TypeScript Error Codes List: Meaning, Common Causes, and Fixes.

Common mistakes

These are the mistakes that tend to repeat across frameworks and upgrades.

Defining aliases only in tsconfig.json

This is the most common mistake. tsconfig.json is necessary, but often not sufficient. Build tools, test runners, and runtime execution may need their own resolution settings.

Using too many aliases too early

A small app rarely needs five custom prefixes. Start with one or two:

  • @/* for main source code
  • possibly one extra alias for server-only or shared code

The more aliases you add, the more places must stay synchronized.

Pointing aliases at unstable folder layouts

If your directory structure changes often, aliases can become a moving target. Prefer mapping to stable top-level source roots instead of temporary feature folders.

Creating overlapping alias patterns

For example, combining @/*, @components/*, and @shared/* can work, but overlapping patterns make debugging harder. Be intentional about priority and naming.

Ignoring test and script entry points

Main app code may work perfectly while background jobs, migrations, test files, and code generation scripts fail. These paths are often maintained by different tools and deserve their own checks.

Forgetting monorepo inheritance rules

In extended configs, a package-local compilerOptions block may replace or reshape inherited behavior in ways that are easy to miss. Always inspect the effective config, not just the root file.

Treating relative imports as a problem in every case

Aliases are useful, but not every deep import needs them. Relative imports are often clearer inside the same feature directory. Use aliases mainly for cross-cutting imports that would otherwise climb multiple levels.

That balance matters for maintainability. The goal is not zero relative paths. The goal is predictable import resolution.

When to revisit

Path alias setup is worth revisiting whenever the surrounding toolchain changes. Use this short maintenance checklist before a migration, framework upgrade, or project restructuring.

  1. Before upgrading Vite, Next.js, Jest, or Node tooling
    Review whether any config files changed format, location, or defaults.
  2. When moving files between src, root, or package folders
    Verify your existing alias targets still match the real layout.
  3. When adding tests, CLI scripts, or background workers
    Check whether those execution paths resolve aliases the same way as the main app.
  4. When splitting a project into packages
    Define a clear source of truth for shared TypeScript config and decide whether aliases stay local or become package imports.
  5. When editor behavior and runtime behavior diverge
    Assume config drift and audit each tool layer one by one.

A practical review routine looks like this:

  • Keep one canonical alias design in tsconfig.
  • List every tool that executes or analyzes your code.
  • Confirm each tool either reads tsconfig paths directly or has an equivalent mapping.
  • Add one small aliased import test in app code and one in tests or scripts.
  • Document the setup in your project README so the next upgrade starts from a known baseline.

If you want the shortest version of this article to keep next to your config files, use this final checklist:

  1. Define baseUrl and paths correctly.
  2. Match your bundler or framework resolver to the same alias.
  3. Match your test runner to the same alias.
  4. Match your runtime or script runner to the same alias.
  5. Restart tools after config changes.
  6. Revisit the setup after upgrades or folder restructures.

That is the durable lesson behind most TypeScript path alias bugs: import shortcuts are easy to add, but stable resolution requires every layer of the toolchain to agree on what the shortcut means.

Related Topics

#paths#imports#tsconfig#tooling#vite#nextjs#jest#ts-node
T

TypeScript Page Editorial

Senior SEO Editor

Senior editor and content strategist. Writing about technology, design, and the future of digital media. Follow along for deep dives into the industry's moving parts.

2026-06-09T07:46:28.078Z