How to Import JSON Modules in JavaScript
JavaScript JSON modules provide a standardized, native mechanism to load JSON data directly into codebases using ES module syntax. This article explains how JSON modules work under the hood, the syntax required for static and dynamic imports, the role of import attributes, and the benefits this feature brings to modern JavaScript development across browsers and runtime environments.
Understanding JSON Modules
Historically, importing JSON files into JavaScript required
platform-specific workarounds. Developers relied on tools like CommonJS
require('./data.json') in Node.js, asynchronous
fetch() API calls in browsers, or build-step bundlers like
Webpack.
JSON modules standardize this behavior as part of the official
ECMAScript specification. They allow the JavaScript engine to treat a
.json file as a first-class module, automatically parsing
its contents into a JavaScript object and exposing it as the module’s
default export.
Import Syntax and Import Attributes
To import a JSON file directly, JavaScript requires the use of import
attributes via the with keyword (formerly specified as
assert). This attribute explicitly tells the runtime to
treat the imported resource as JSON data rather than executable
JavaScript code.
Static Import
For static declarations at the top of a file, use the standard
import statement followed by
with { type: "json" }:
import configData from './config.json' with { type: 'json' };
console.log(configData.appName);
console.log(configData.version);Dynamic Import
For conditional or lazy-loading scenarios, JSON modules can be loaded
using dynamic import():
async function loadUserData() {
const { default: userData } = await import('./user.json', {
with: { type: 'json' }
});
console.log(userData.name);
}How the Import Process Works
When an engine encounters a JSON module import, it executes the following steps:
- Resolution and Fetching: The module loader resolves the file path and fetches the resource over the network or from the local file system.
- MIME-Type and Attribute Verification: The engine
verifies that the server-provided MIME type (typically
application/json) matches thetype: "json"declaration. If there is a mismatch or the attribute is missing, the engine throws an error before executing any code. - Parsing: The engine passes the file content through
a parser identical to
JSON.parse(). If the file contains invalid JSON syntax, aSyntaxErroris raised during the module graph initialization phase. - Module Record Creation: A synthetic module record
is created. The parsed JavaScript object is assigned to the
defaultexport. - Caching: Like standard JavaScript modules, imported JSON modules are cached in the module map. Subsequent imports of the same path share the same parsed object instance.
Why the
type: "json" Attribute Is Required
The explicit with { type: "json" } attribute is a
security measure designed to prevent cross-site script inclusion (XSSI)
and privilege escalation attacks.
Without the explicit attribute, an attacker could potentially trick a web application into requesting a resource with executable JavaScript headers while serving malicious code. By requiring the developer to explicitly declare the expected type as JSON, the browser guarantees that the received content will never be executed as script, even if the server returns a JavaScript MIME type.
Key Benefits of JSON Modules
- Eliminates Boilerplate: Removes the need for
manually reading files with
fs.readFile()or managing asynchronousfetch().then(res => res.json())chains for local data. - Module Graph Integration: JSON files participate directly in the dependency graph, allowing static analyzers and bundlers to optimize, tree-shake, and validate dependencies ahead of time.
- Performance: Native JSON parsing directly in the engine’s module pipeline is often faster than reading raw strings and parsing them manually in userland JavaScript code.