Skip to content

Fix Import a JSON file to JS code compatible with old and current browsers Step-by-Step Guide

Importing JSON files directly into JavaScript has historically been restricted due to security concerns (specifically the Same-Origin Policy) and the lack of a native module specification for non-JavaScript assets.

In modern environments, you might encounter Uncaught SyntaxError: Cannot use import statement outside a module or TypeError: Failed to fetch. In older browsers, the keyword import is simply not recognized, or the browser may block the request because the server did not return a valid application/json MIME type. As the ECMAScript specification evolved, the syntax shifted from Import Assertions to Import Attributes, creating a fragmentation gap between browser versions.

Cause Impact Technical Detail
MIME Type Mismatch All Browsers Server sends text/plain or text/html instead of application/json.
CORS Policy Modern Browsers Accessing a JSON file from a different domain without appropriate headers.
Syntax Evolution Chromium 91-122 Usage of assert { type: 'json' } which is now deprecated in favor of with.
Legacy Engines IE11 / Old Safari No support for fetch or ES6 Modules; requires XMLHttpRequest.
SOP Restrictions Local Files Browsers block file:// protocol access to JSON for security.

1. The Modern Standard (Chrome 123+, Safari 17.2+, Firefox 126+)

Section titled โ€œ1. The Modern Standard (Chrome 123+, Safari 17.2+, Firefox 126+)โ€

Use Import Attributes. This is the current TC39 proposal stage 3+ standard.

// Modern syntax using 'with'
import configData from './data.json' with { type: 'json' };
console.log(configData.name);

If you need to support browsers from the last 5-7 years without specific experimental flags, fetch is the most reliable asynchronous method.

async function loadConfig() {
try {
const response = await fetch('./data.json');
if (!response.ok) {
throw new Error('Network response was not ok');
}
const data = await response.json();
console.log(data);
} catch (error) {
console.error('Failed to load JSON:', error);
}
}
loadConfig();

For ancient environments, you must fallback to XMLHttpRequest (XHR). This does not support promises, so a callback pattern is required.

function loadJSONLegacy(url, callback) {
var xhr = new XMLHttpRequest();
xhr.overrideMimeType("application/json");
xhr.open('GET', url, true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
callback(JSON.parse(xhr.responseText));
}
};
xhr.send(null);
}
loadJSONLegacy('./data.json', function(data) {
console.log(data);
});

In a professional production environment, you should let your build tool handle the import. This ensures compatibility across all browsers by inlining or transforming the JSON during the build process.

// This works in Vite/Webpack out of the box
import data from './data.json';
export const getAppConfig = () => {
return data.version;
};
  1. Strict MIME Types: Ensure your web server (Nginx, Apache, or Express) sends the header Content-Type: application/json. Without this, modern browsers will block the import for security reasons.
  2. Use Build Tools: Use a transpiler like Babel or a bundler like Vite. They convert modern import statements into compatible code for older browsers automatically.
  3. Graceful Degradation: When using the fetch API, always implement a .catch() block or try/catch to handle network failures or malformed JSON.
  4. Local Testing: Browsers block JSON imports via the file:// protocol. Always use a local development server.
    • Quick Tip: Run npx serve or use the VS Code extension Live Server (Alt + L Alt + O).
  5. Prefer Attributes over Assertions: If targeting very recent browsers, move away from assert { type: 'json' } and use with { type: 'json' } to align with the latest V8 and SpiderMonkey implementations.