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:

  1. Resolution and Fetching: The module loader resolves the file path and fetches the resource over the network or from the local file system.
  2. MIME-Type and Attribute Verification: The engine verifies that the server-provided MIME type (typically application/json) matches the type: "json" declaration. If there is a mismatch or the attribute is missing, the engine throws an error before executing any code.
  3. Parsing: The engine passes the file content through a parser identical to JSON.parse(). If the file contains invalid JSON syntax, a SyntaxError is raised during the module graph initialization phase.
  4. Module Record Creation: A synthetic module record is created. The parsed JavaScript object is assigned to the default export.
  5. 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