Back to Rabbit Holes
RemindersPublishedv0.0.1.0

Preference for Shortcuts in Config to Absolute Paths

`../../../components/basics/DeckHeader.astro` breaks the moment you move the importer; `@components/…` reads the same from anywhere.

Michael StatonUpdated Astro KnotsView on GitHub
ReminderTypeScriptAstroConfigImport AliasesDeveloper Experience

Preference for Shortcuts in Config to Absolute Paths

Don't: import DeckHeader from "../../../components/basics/DeckHeader.astro"

Do: import DeckHeader from "@components/basics/DeckHeader.astro"

Why

Deep relative paths get crazy to follow. Reading ../../../ tells you how far up to climb, not where you land — you have to know the importing file's own depth before the import means anything. The alias form is absolute from the project root and reads identically from every file, at any nesting level.

The second cost is that relative paths are positional, so they rot on every move. Relocating one component invalidates every ../ count that pointed at it, and the failure is a build error per import rather than one place to fix.

The canonical alias set

Shared verbatim by astro-knots/splash, lfm/splash, and ai-labs/splash:

JSONC
// tsconfig.json
{
  "extends": "astro/tsconfigs/strict",
  "include": [".astro/types.d.ts", "**/*"],
  "exclude": ["dist"],
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*":           ["src/*"],
      "@components/*": ["src/components/*"],
      "@content/*":    ["src/content/*"],
      "@layouts/*":    ["src/layouts/*"],
      "@lib/*":        ["src/lib/*"],
      "@loaders/*":    ["src/loaders/*"],
      "@pages/*":      ["src/pages/*"],
      "@styles/*":     ["src/styles/*"]
    }
  }
}

baseUrl is required — paths are resolved relative to it.

No Vite config is needed. Astro reads tsconfig.json paths and wires them into the bundler itself. Confirmed 2026-08-17: none of the splashes declare a vite.resolve.alias, and their aliased imports resolve at build.

The competing pattern — and why tsconfig wins

astro-knots/sites/mpstaton-site declares aliases in astro.config.mjs instead:

JS
// Build aliases conditionally so standalone deployments don't depend on monorepo paths.
const aliases = { /* … */ };
// → vite: { resolve: { alias: aliases } }

That exists for a real reason — conditional resolution so the site can deploy standalone, outside the monorepo. But it has a cost that is easy to miss: vite.resolve.alias is a bundler concern. TypeScript and your editor know nothing about it. On mpstaton-site, @layouts, @components, and @lib resolve at build time but not in the editor, because its tsconfig.json declares only @brand. You get working builds and broken go-to-definition.

Default to tsconfig paths. Reach for vite.resolve.alias only when resolution genuinely has to vary by environment — and when you do, mirror the aliases into tsconfig.json anyway so tooling keeps up.

Current adoption

SiteDeep relative (../../../)Aliasedtsconfig paths
ai-labs/splash053full set
content-farm/splash05partial — missing @lib, @styles
astro-knots/splash236full set
lfm/splash1233full set
astro-knots/sites/mpstaton-site4252@brand only (rest via Vite)
astro-knots/sites/fullstack-vc1010none

fullstack-vc is the outlier worth fixing: 101 deep-relative imports and no aliases declared at all.

How to apply

  • Scaffolding a site → paste the canonical block into tsconfig.json before writing the first component.

  • Adding a new top-level src/ directory → add its alias in the same commit.

  • Touching a file with ../../ in an import → convert that import while you're there. Do not do a tree-wide rewrite as a side quest.

  • Keep alias names matching directory names (@layoutssrc/layouts). A cleverly-named alias is a second thing to learn.

Origin

This reminder began as a pasted diff of the change that introduced the pattern to splash/tsconfig.json — the original six aliases, before @lib and @styles were added:

DIFF
-  "exclude": ["dist"]
+  "exclude": ["dist"],
+  "compilerOptions": {
+    "baseUrl": ".",
+    "paths": {
+      "@/*": ["src/*"],
+      "@components/*": ["src/components/*"],
+      "@layouts/*": ["src/layouts/*"],
+      "@loaders/*": ["src/loaders/*"],
+      "@content/*": ["src/content/*"],
+      "@pages/*": ["src/pages/*"]
+    }
+  }
  • Astro Knots is not a True Monorepo — why standalone-deployable sites are a constraint, and therefore why the Vite-alias variant exists at all

  • Preferred Stack