javascriptes2025import-attributesjson-modulesesmconcepts

JSON Import Attributes: import data from './x.json' with { type: 'json' }

·8 min read·JSON Concepts

Importing JSON is finally standard

For years, loading a JSON file into JavaScript meant fs.readFile + JSON.parse, a bundler plugin, or a require('./data.json') that only worked in CommonJS. ES modules had no standard way to import JSON — until import attributes, standardized in ECMAScript 2025. Now you can import a JSON file directly, with an explicit attribute telling the runtime it's JSON:

js
import config from "./config.json" with { type: "json" };

console.log(config.version);   // config is the parsed object, not a string

The imported value is the already-parsed object — no JSON.parse step. As of 2026 this works in Node.js 22+, Deno, and current Chrome, Firefox, Safari, and Edge, with no bundler configuration. It builds on JSON in JavaScript and JSON in Node.js by making the import itself a language feature.

The syntax: the `with` keyword

An import attribute comes after the module specifier, introduced by with, as an object of key–value pairs:

js
// static import
import pkg from "./package.json" with { type: "json" };

// dynamic import — note the attributes go under a "with" key
const data = await import("./data.json", { with: { type: "json" } });
console.log(data.default);   // the JSON object is the default export

The ECMAScript standard defines the type attribute with values "json" and "text"; browsers additionally support "css" for CSS module imports. For JSON, type: "json" is the one you need.

Why is the `type` attribute required?

It looks redundant — the file already ends in .json, so why must you *also* say type: "json"? The answer is security, specifically preventing a MIME-confusion / privilege-escalation attack.

A module's file extension or URL doesn't reliably tell the runtime what the server actually returned. Without the attribute, a server could serve a URL that *looks* like JSON but returns executable JavaScript, and the importing code might run it as a script. The type: "json" attribute makes the developer's intent explicit and binding: the runtime must treat the resource as JSON data, and if what comes back isn't valid JSON (or the server's MIME type contradicts it), the import fails instead of silently executing code. In short, the attribute guarantees "this is data, never code."

It replaced import assertions (`assert`)

If you've seen the older assert { type: "json" } syntax, that was an earlier proposal (import *assertions*) that has been deprecated in favor of import *attributes* with with. The change was deliberate: assert implied the attribute only *checked* the type after loading, but the committee realized the attribute can actually *influence how the module is fetched and interpreted* — so with (which reads as "load this with these attributes") replaced assert. Migrate any assert { type: "json" } to with { type: "json" }; the assertion form is being removed from engines.

js
// ❌ deprecated
import data from "./data.json" assert { type: "json" };
// ✅ current (ES2025)
import data from "./data.json" with { type: "json" };

When to use it — and when not to

Use JSON imports for static, bundled-with-your-code data: configuration, package.json or tsconfig.json values, i18n message catalogs, fixture data, feature manifests. It's cleaner than reading and parsing a file, and the value is typed as an object.

Don't use it for data that changes at runtime or comes from a network endpoint — imports are resolved once and cached for the life of the module, so an imported JSON file is effectively a constant. For dynamic data, keep using fetch + response.json(). And remember imported JSON is read-only in spirit: mutating the object mutates the single shared module instance everyone else imported.

For related fundamentals see JSON in JavaScript, the JSON.stringify guide, and JSONC & JSON5 for comment-friendly config formats.

Frequently asked questions

Use import attributes: import data from "./data.json" with { type: "json" }. Standardized in ECMAScript 2025, this imports the already-parsed JSON object with no JSON.parse call, and works in Node.js 22+, Deno, and current browsers.

For security. The file extension doesn't guarantee what the server actually returns, so the type: "json" attribute makes your intent explicit and binding — the runtime treats the resource as JSON data and fails the import if it isn't, rather than risking executing returned JavaScript.

assert { type: "json" } was the older import-assertions syntax and is deprecated. It was replaced by import attributes using with { type: "json" }, because the attribute can affect how a module is fetched and parsed, not just validate it afterward. Use with.

Pass the attributes as a second argument under a with key: const mod = await import("./data.json", { with: { type: "json" } }), then read mod.default for the parsed object.

Use a JSON import for static data that ships with your code (config, fixtures, message catalogs) — it's resolved once and cached. Use fetch and response.json() for data that changes at runtime or comes from a server.

Try JSONKit — JSON Developer Tools

Format, validate, convert and diff JSON. Instant, accurate and 100% private.