Learn/Advanced Topics

JSON in JavaScript — fetch, localStorage, import & Beyond

JavaScript and JSON are inseparable. This guide covers every pattern you'll encounter in production — from basic parsing to advanced topics like streaming, Web Workers, and deep cloning.

Intermediate~16 min read

JSON.parse() and JSON.stringify()

Basic Parsing

Parse JSON safelyjavascript
1const jsonString = '{"name":"Alice","age":28}';
2
3try {
4 const user = JSON.parse(jsonString);
5 console.log(user.name); // "Alice"
6} catch (err) {
7 console.error('Invalid JSON:', err.message);
8}

Stringify with Formatting

Pretty-print JSONjavascript
1const data = { name: "Alice", scores: [98, 87, 95] };
2
3// Compact (for APIs, storage)
4JSON.stringify(data);
5// '{"name":"Alice","scores":[98,87,95]}'
6
7// Pretty (for debugging, logs)
8JSON.stringify(data, null, 2);
9// '{
10// "name": "Alice",
11// "scores": [98, 87, 95]
12// }'

Replacer and Reviver

Transform during parse/stringifyjavascript
1// Reviver: transform values during parse
2const json = '{"created":"2026-03-28T10:00:00Z","name":"Alice"}';
3const data = JSON.parse(json, (key, value) => {
4 if (key === 'created') return new Date(value);
5 return value;
6});
7// data.created is now a Date object
8
9// Replacer: filter/transform during stringify
10const user = { name: "Alice", password: "secret123", age: 28 };
11JSON.stringify(user, (key, value) => {
12 if (key === 'password') return undefined; // strip sensitive fields
13 return value;
14}, 2);
15// { "name": "Alice", "age": 28 }

Fetching JSON from APIs

Fetch → Parse → Render Flow
Production-ready fetch patternjavascript
1async function fetchUsers() {
2 const response = await fetch('https://api.example.com/users', {
3 headers: { 'Accept': 'application/json' },
4 signal: AbortSignal.timeout(10_000),
5 });
6
7 if (!response.ok) {
8 throw new Error(`HTTP ${response.status}: ${response.statusText}`);
9 }
10
11 const contentType = response.headers.get('content-type');
12 if (!contentType?.includes('application/json')) {
13 throw new Error(`Expected JSON, got ${contentType}`);
14 }
15
16 return response.json(); // returns parsed object
17}
18
19// Usage with error boundary
20try {
21 const users = await fetchUsers();
22 console.log(`Loaded ${users.length} users`);
23} catch (err) {
24 if (err.name === 'TimeoutError') {
25 console.error('Request timed out');
26 } else {
27 console.error('Fetch failed:', err.message);
28 }
29}

Always Check response.ok

fetch() does not throw on HTTP 4xx/5xx errors. A 404 Not Found is a successful fetch that returns a response with ok: false. Always check before calling .json().

localStorage and sessionStorage

Safe localStorage wrapperjavascript
1function saveJson(key, data) {
2 try {
3 localStorage.setItem(key, JSON.stringify(data));
4 return true;
5 } catch (err) {
6 // QuotaExceededError if storage is full (~5-10 MB limit)
7 console.warn('Failed to save:', err.message);
8 return false;
9 }
10}
11
12function loadJson(key, fallback = null) {
13 try {
14 const raw = localStorage.getItem(key);
15 return raw ? JSON.parse(raw) : fallback;
16 } catch {
17 return fallback; // corrupted data returns fallback
18 }
19}
20
21// Usage
22saveJson('preferences', { theme: 'dark', fontSize: 16 });
23const prefs = loadJson('preferences', { theme: 'light', fontSize: 14 });

Storage Limits

localStorage has a ~5-10 MB limit per origin. For larger data, use IndexedDB. Never store sensitive data (tokens, passwords) in localStorage — it's accessible to any JavaScript on the same origin.

Importing JSON Files

Static Import (Build Time)

Import assertion (ES2025+)javascript
1// Works in Node.js 20+, modern bundlers
2import config from './config.json' with { type: 'json' };
3console.log(config.database.host);

Dynamic Import (Runtime)

Lazy loading JSONjavascript
1async function loadTranslations(lang) {
2 const module = await import(`./locales/${lang}.json`, {
3 with: { type: 'json' }
4 });
5 return module.default;
6}
7
8const en = await loadTranslations('en');

Fetch from Public Directory

Load JSON file at runtime (browser)javascript
1const response = await fetch('/data/countries.json');
2const countries = await response.json();

Deep Cloning with JSON

JSON clone vs structuredClonejavascript
1const original = { name: "Alice", scores: [98, 87] };
2
3// JSON method (old pattern — has limitations)
4const clone1 = JSON.parse(JSON.stringify(original));
5
6// structuredClone (modern — handles more types)
7const clone2 = structuredClone(original);
8
9// JSON clone fails with: Date, Map, Set, RegExp, undefined, Infinity, circular refs
10// structuredClone handles all of these correctly
FeatureJSON ClonestructuredClone
Date objects✗ (becomes string)
Map / Set✗ (becomes {})
undefined✗ (dropped)
Circular references✗ (throws)
Functions
PerformanceSlowerFaster
Browser supportAllAll modern (2022+)

Web Workers and JSON

Offload heavy JSON parsing to a Workerjavascript
1// main.js
2const worker = new Worker('/json-worker.js');
3worker.postMessage({ action: 'parse', data: hugeJsonString });
4
5worker.onmessage = (event) => {
6 const { result, error } = event.data;
7 if (error) console.error(error);
8 else console.log('Parsed:', result);
9};
10
11// json-worker.js
12self.onmessage = (event) => {
13 const { action, data } = event.data;
14 if (action === 'parse') {
15 try {
16 const result = JSON.parse(data);
17 self.postMessage({ result });
18 } catch (err) {
19 self.postMessage({ error: err.message });
20 }
21 }
22};

Tip

Parsing JSON over 1 MB blocks the main thread. Use a Web Worker to keep the UI responsive. Our JSON Viewer uses this pattern for large files.

Common Pitfalls

PitfallExampleFix
Double parsingJSON.parse(JSON.parse(str))Parse once — check if input is string or object
Stringify drops undefined{a: undefined} → "{}"Use replacer to convert undefined to null
Date serializationDate → ISO string after stringifyUse reviver to restore Date objects
BigInt not supportedJSON.stringify({n: 1n}) throwsUse replacer to convert BigInt to string
Circular referencesobj.self = obj → throwsUse a custom replacer or structuredClone
NaN / InfinityBecome null in JSONValidate numbers before serialization

Try It Yourself

Try modifying this JSON and validating it — this is exactly what JSON.parse() does under the hood.

Try It Yourself

This is the JSON your fetch() call would return

Frequently Asked Questions

What is the difference between JSON and a JavaScript object?
A JavaScript object is a runtime data structure. JSON is a text format. JSON requires double-quoted keys and strings, does not allow functions, undefined, or trailing commas. JSON.parse() converts JSON text into a JS object; JSON.stringify() does the reverse.
Can I import a JSON file in JavaScript?
Yes. Use import data from "./data.json" with { type: "json" } (import assertions) in modern environments, or use fetch() to load JSON files dynamically.
How do I store JSON in localStorage?
localStorage only stores strings. Use JSON.stringify(data) when saving and JSON.parse(localStorage.getItem(key)) when reading. Always wrap parse in try/catch for safety.
Why does JSON.stringify skip undefined values?
By design, JSON has no "undefined" type. JSON.stringify() silently drops keys with undefined values in objects and converts undefined array elements to null. Use a replacer function to handle this if needed.
Is JSON.parse safe from injection attacks?
JSON.parse() itself is safe — it only parses data, not executable code. However, if you insert parsed data into HTML without escaping, you are vulnerable to XSS. Always sanitize before rendering in the DOM.