Module Dependency Resolution
How to declare dependencies between modules, how the resolver works, and how to fix common errors.
Overview
The module system automatically discovers which modules depend on others, then loads them in the correct order. Dependencies are declared in the module manifest.
export default {
id: “my-feature”,
dependencies: [“core”, “@stnd/account”],
routes: […],
};
Dependency resolution rules
When you declare dependencies: [“some-id”], the resolver tries multiple strategies to find the module.
| Dependency ID | Resolution strategy | What it resolves to |
|---|---|---|
“core” |
1. Check loaded modules with id=“core” 2. Try @stnd/core package3. ERROR if not found |
Local app module or package |
“@stnd/account” |
1. Full package resolution | @stnd/account package |
“@modules/core” |
1. Exact package resolution | @modules/core alias or package |
Types of dependencies
Local app modules
If your app has a module with id: “core”, just use that ID in dependencies:
// apps/myapp/modules/core/index.module.js
export default {
id: “core”,
name: “Core”,
routes: […],
};
// apps/myapp/modules/note/index.module.js
export default {
id: “note”,
dependencies: [“core”], // ← References the local core module
routes: […],
};
@stnd/* packages
Use the full package name for Standard packages:
export default {
id: “my-feature”,
dependencies: [
“@stnd/account”,
“@stnd/icon”,
“@stnd/modules/launcher”,
],
routes: […],
};
Bare IDs (advanced)
Bare IDs (no @, no slashes) are treated as potential @stnd/ packages:
dependencies: [“account”] // Resolves to @stnd/account
dependencies: [“toast”] // Resolves to @stnd/modules/toast
This can cause errors if you meant a local module. Prefer explicit names.
Common errors and fixes
Error: “Invalid module manifest (missing ID)”
::stnd:: [Modules] ✖ Invalid module manifest (missing ID): /path/to/standard.js
Required by: “note” module
See: https://stnd.build/manual/core-is-not-a-module
What happened: A module declared a dependency that resolved to a non-module file (like the Astro integration).
Most common cause: Using a bare ID like “core” when you meant a local app module:
// ✗ WRONG — “core” resolves to @stnd/core package, which is the Astro integration
export default {
id: “note”,
dependencies: [“core”],
routes: […],
};
// ✓ RIGHT — Remove the dependency if core is always loaded
export default {
id: “note”,
dependencies: [], // Core is loaded automatically
routes: […],
};
Error: “Circular dependency detected”
::stnd:: [Modules] ✖ Circular dependency detected: note → editor → note
What happened: Two modules depend on each other, creating a cycle.
Fix: Reorganize to remove the cycle:
// BEFORE (circular): editor depends on note, note depends on editor
// AFTER (linear)
export default {
id: “shared”, // ← New shared module
routes: […],
};
export default {
id: “editor”,
dependencies: [“shared”],
routes: […],
};
export default {
id: “note”,
dependencies: [“shared”],
routes: […],
};
Best practices
1. Use full package names for external dependencies
// ✓ Clear and explicit
dependencies: [“@stnd/account”, “@stnd/icon”]
// ✗ Ambiguous — is this a local module or a package?
dependencies: [“account”, “icon”]
2. Declare only what you need
// ✓ Only what the module directly uses
export default {
id: “articles”,
dependencies: [“@stnd/press”],
routes: […],
};
3. Keep dependency graphs shallow
If you have many modules depending on many others, consider a shared foundation module:
// BEFORE (complex)
articles → core, press, utils
comments → core, press, api
timeline → core, press, utils, api
// AFTER (clear layers)
foundation → core, press, utils, api
articles → foundation
comments → foundation
timeline → foundation
4. Document why dependencies exist
export default {
id: “rich-editor”,
// Uses Press for rendering, Prism for syntax highlighting
dependencies: [“@stnd/press”, “@stnd/modules/prism”],
routes: […],
};
How load order is determined
Dependencies are resolved into a topological sort. Modules with no dependencies load first, then modules that depend only on already-loaded modules, and so on.
Declaration order: Load order:
───────────────────── ───────────
articles core
comments → core @stnd/press
timeline → core articles
comments
timeline
If you declare an unsatisfiable dependency (circular or non-existent), the loader throws immediately.
Dependencies vs imports
Don’t confuse module dependencies (load order) with code imports (what your code actually uses).
| When to use | How to declare | Effect |
|---|---|---|
| Module A needs Module B loaded before it | dependencies: [“module-b”] in Module A’s manifest |
B loads first, then A |
| Module A’s code uses exports from B | import Thing from “@modules/b” in Module A’s code |
B’s exports available at runtime |
// modules/articles/index.module.js
export default {
id: “articles”,
dependencies: [“@stnd/press”], // ← Press must load first
routes: [“./routes/[slug].astro”],
};
// modules/articles/routes/[slug].astro
—-
import Press from “@stnd/press/Press.astro”; // ← Import what you use
—-
Troubleshooting checklist
- Does the error say which module required the bad dependency? (Look for “Required by:“)
- Is the dependency a bare ID that might resolve to the wrong package? (Use full names:
@stnd/foonotfoo) - Is the dependency a module in your app that hasn’t been declared yet?
- Could there be a circular dependency? (Module A → B → A)
- Did you recently rename or delete a module? (Update any declarations of it as a dependency)