How to Use Subpath Imports in package.json
This article provides an overview of the imports field
in package.json, explaining how it enables native subpath
imports in modern JavaScript environments. You will learn the syntax,
use cases for internal module aliasing, conditional resolution patterns,
and how to configure your project to eliminate deep relative paths
without relying on third-party build tools.
What is the imports
Field?
The imports field in package.json is a
native standard supported by Node.js, modern runtimes (like Deno and
Bun), and bundlers (such as Vite, Webpack, and esbuild). It defines
internal module aliases that apply exclusively within the package
itself.
Unlike the exports field, which controls what external
consumers can access when importing your package, the
imports field is strictly internal. All import specifiers
defined in the imports map must begin with a hash
(#) to distinguish them from standard external package
names.
Setting Up Basic Subpath Imports
To configure internal path aliases, add the imports
property to your package.json file.
{
"name": "my-app",
"type": "module",
"imports": {
"#utils": "./src/utils/index.js",
"#config": "./src/config.js"
}
}With this configuration, any file inside your project can use
#utils or #config directly, avoiding complex
relative paths:
// Instead of: import { formatDate } from '../../utils/index.js';
import { formatDate } from '#utils';Pattern Matching with Wildcards
You can use wildcard asterisks (*) to alias entire
directories dynamically rather than mapping files individually.
{
"imports": {
"#components/*": "./src/components/*",
"#services/*": "./src/services/*.js"
}
}When importing:
import Button from '#components/Button.jsx';
import { AuthService } from '#services/auth';Every instance of * in the import specifier replaces the
* in the target path.
Conditional Imports
The imports field allows conditional mapping based on
runtime environments, module types, or custom flags.
{
"imports": {
"#database": {
"node": "./src/db/node.js",
"browser": "./src/db/browser.js",
"default": "./src/db/default.js"
}
}
}When code executes import db from '#database', the
runtime resolves the file matching its current environment
condition.
Key Rules and Best Practices
- Prefix with
#: Every key in theimportsobject must start with#. Leading hashes prevent collisions with external dependencies published on npm. - Relative Targets: The target paths must be
explicitly relative (starting with
./). - Internal Only: External packages cannot access your
#aliases; they are private to your package root. - TypeScript Compatibility: TypeScript supports
subpath imports automatically when the
moduleResolutionoption intsconfig.jsonis set tonode16,nodenext, orbundler.