JSON (JavaScript Object Notation) is a text format for data. Servers send it, configuration files use it, and browsers store it. It looks almost like JavaScript object literals, but it is a string, so you must convert it before you can use it. This lesson covers both directions and introduces the data file for the course dashboard you will build from here on.
JSON is text
This is JSON:
{
"title": "JavaScript Complete Course",
"lessons": 54,
"free": true,
"tags": ["javascript", "web"],
"author": null
}
It is stricter than a JavaScript object literal:
- Keys must be in double quotes.
- Strings must use double quotes. Single quotes are not allowed.
- No trailing commas, no comments.
- Values can only be strings, numbers,
true,false,null, arrays and objects.
JSON.parse: text to values
JSON.parse takes a JSON string and returns the JavaScript value it describes:
const text = '{"title":"JavaScript Complete Course","lessons":54,"tags":["javascript","web"]}';
const course = JSON.parse(text);
console.log(course.lessons); // → 54
console.log(course.tags[1]); // → web
console.log(typeof text, typeof course); // → string object
The top-level value does not have to be an object. JSON.parse('[1, 2]'), JSON.parse('"hi"') and JSON.parse('42') are all valid.
Parsing safely
Invalid JSON throws a SyntaxError. Text from outside your program (a server, a file, localStorage, a user) can be broken or empty, so wrap the parse in try...catch:
function parseJson(text, fallback) {
try {
return JSON.parse(text);
} catch (error) {
console.warn('Could not parse JSON:', error.message);
return fallback;
}
}
console.log(parseJson('{"ok":true}', {})); // → { ok: true }
console.log(parseJson("{'ok':true}", {})); // logs a warning, then → {}
console.log(parseJson('', [])); // logs a warning, then → []
The exact warning text differs between engines; the error type is always SyntaxError. Lesson 35 covers error handling in depth.
Parsing successfully does not mean the data has the shape you expect. A server might send {"courses": null}. Check the parts you rely on, for example with Array.isArray(data) or the optional chaining from lesson 25.
Never use eval to read JSON. JSON.parse only ever builds data. eval runs whatever code is in the string.
JSON.stringify: values to text
JSON.stringify does the reverse:
const settings = { theme: 'dark', fontSize: 18, motion: false };
const json = JSON.stringify(settings);
console.log(json); // → {"theme":"dark","fontSize":18,"motion":false}
The third argument adds indentation, which makes output readable for logs and files:
console.log(JSON.stringify(settings, null, 2));
// → {
// "theme": "dark",
// "fontSize": 18,
// "motion": false
// }
The second argument is a replacer. Pass an array of keys to include only those properties:
const user = { id: 3, name: 'Tom', password: 'secret' };
console.log(JSON.stringify(user, ['id', 'name'])); // → {"id":3,"name":"Tom"}
A common use of the pair is saving data in the browser, where localStorage only stores strings:
localStorage.setItem('settings', JSON.stringify(settings));
const restored = parseJson(localStorage.getItem('settings'), {});
What JSON cannot represent
JSON has fewer types than JavaScript. stringify does not throw for most unsupported values. It quietly changes or drops them:
| JavaScript value | In an object | In an array | On its own |
|---|---|---|---|
undefined | Property dropped | Becomes null | Returns undefined |
| Function | Property dropped | Becomes null | Returns undefined |
NaN, Infinity | Becomes null | Becomes null | 'null' |
Date | ISO string | ISO string | ISO string |
Map, Set | {} | {} | '{}' |
BigInt | Throws TypeError | Throws TypeError | Throws TypeError |
| Circular reference | Throws TypeError | Throws TypeError | Throws TypeError |
const data = {
name: 'Quiz',
score: NaN,
helper() {},
missing: undefined,
created: new Date('2026-03-01T09:00:00Z'),
};
console.log(JSON.stringify(data));
// → {"name":"Quiz","score":null,"created":"2026-03-01T09:00:00.000Z"}
Round-tripping through JSON therefore loses information. A Date comes back as a string, and a Map as an empty object. Convert a Map with Object.fromEntries(map) before saving, and rebuild it afterwards.
Restoring dates with a reviver
JSON.parse takes an optional second argument, a reviver function. It is called for every key and value, and whatever it returns is used instead:
const saved = '{"title":"Closures","updated":"2026-03-01T09:00:00.000Z"}';
const lesson = JSON.parse(saved, (key, value) =>
key === 'updated' ? new Date(value) : value
);
console.log(lesson.updated instanceof Date); // → true
console.log(lesson.updated.getUTCFullYear()); // → 2026
Always return value for keys you do not change, or they disappear.
Custom output with toJSON
If an object has a toJSON method, stringify serialises whatever that method returns. That is how Date produces its ISO string, and you can use it in your own classes.
The course dashboard data
From this lesson on, the course's larger project is a course dashboard: a page that loads a list of courses, then filters, sorts, searches and caches it. Create a dashboard folder with this structure. You will fill in index.html and app.js over the next few lessons:
dashboard/
index.html
app.js
data/
courses.json
The data lives in data/courses.json. Each entry has the same six fields: id, title, topic, level, lessons and updated. Save this as the file's contents:
[
{ "id": 1, "title": "JavaScript Basics", "topic": "JavaScript", "level": "Beginner", "lessons": 12, "updated": "2026-09-14" },
{ "id": 2, "title": "CSS Grid in Practice", "topic": "CSS", "level": "Intermediate", "lessons": 8, "updated": "2026-08-02" },
{ "id": 3, "title": "Accessible Forms", "topic": "Accessibility", "level": "Intermediate", "lessons": 6, "updated": "2026-10-01" },
{ "id": 4, "title": "Responsive Layouts", "topic": "CSS", "level": "Beginner", "lessons": 10, "updated": "2026-07-21" },
{ "id": 5, "title": "Async JavaScript", "topic": "JavaScript", "level": "Advanced", "lessons": 9, "updated": "2026-09-30" },
{ "id": 6, "title": "HTML Forms from Scratch", "topic": "HTML", "level": "Beginner", "lessons": 7, "updated": "2026-06-11" }
]
A few choices are worth noticing. id and lessons are numbers, not strings, so you can compare and sort them directly. updated is a date string in YYYY-MM-DD form, because JSON has no date type; lesson 27 turns it into a readable date. level uses three fixed values, Beginner, Intermediate and Advanced, which later lessons filter on.
Until lesson 33 shows how to load the file with fetch(), you can practise on the same data by pasting part of it into the console as a string:
const coursesJson = '[{"id":1,"title":"JavaScript Basics","topic":"JavaScript","level":"Beginner","lessons":12,"updated":"2026-09-14"},{"id":2,"title":"CSS Grid in Practice","topic":"CSS","level":"Intermediate","lessons":8,"updated":"2026-08-02"}]';
const courses = parseJson(coursesJson, []);
const totalLessons = courses.reduce((sum, course) => sum + course.lessons, 0);
console.log(`${courses.length} courses, ${totalLessons} lessons`);
// → 2 courses, 20 lessons
Summary
- JSON is text with strict rules: double quotes, no comments, no trailing commas.
JSON.parseturns text into values; wrap it intry...catchfor any outside input.JSON.stringifyturns values into text; use the third argument for indentation.undefined, functions,NaN,MapandSetare dropped or changed;BigIntand circular references throw.- Restore special types with a reviver, and keep dashboard data in
data/courses.jsonwith six fixed fields.