Skip to main content

The toJSON() method in JavaScript

toJSON() is a special method that JavaScript calls automatically when an object is serialized through JSON.stringify(). It lets you control exactly what ends up in the JSON string: if an object has toJSON(), what gets serialized is the result of calling that method, not the object itself.

Theory

TL;DR

  • JSON.stringify(obj) first checks whether the object has a toJSON() method.
  • If it does, the result of that call is serialized instead of the object.
  • It works recursively: a nested object's toJSON() is called too.
  • You can declare it in your own classes, handy for a clean data shape.
  • It may return any value: an object, a string, a number, null.
  • Date has its own toJSON(), which is why it automatically becomes an ISO string.
  • It has no effect on JSON.parse() at all.

Quick example

javascript
const user = { name: 'Alice', age: 25, password: 'secret', toJSON() { // pick what ends up in the JSON return { name: this.name, age: this.age }; } }; console.log(JSON.stringify(user)); // '{"name":"Alice","age":25}'

password did not make it into the JSON, because toJSON() returns only name and age.

How it works under the hood

When you call:

javascript
JSON.stringify(obj)

JavaScript does the following:

  1. It checks whether obj has a toJSON() method. If it does, it calls it:

    javascript
    const value = obj.toJSON();
  2. It serializes the result of that call, not the object itself.

In other words, toJSON() is an extension point of the format itself: a type decides on its own what it looks like in JSON.

Recursion in nested objects

toJSON() fires for every nested object, not only for the root one.

javascript
const user = { name: 'Alice', stats: { score: 42, toJSON() { return 'Top Secret'; // replace the object with a string } } }; console.log(JSON.stringify(user)); // '{"name":"Alice","stats":"Top Secret"}'

The toJSON() method inside stats was called automatically and replaced the nested object.

toJSON() in classes and built-in types

The method is convenient in your own classes, so instances serialize nicely:

javascript
class User { constructor(name, age) { this.name = name; this.age = age; } toJSON() { return { user: this.name, age: this.age }; } } const alice = new User('Alice', 25); console.log(JSON.stringify(alice)); // '{"user":"Alice","age":25}'

This is how you define a clean representation of the data, for example for sending it to a server.

Built-in types behave differently:

TypeWhat toJSON() doesExample
DateReturns an ISO stringnew Date().toJSON() gives "2025-10-14T17:00:00.000Z"
Map / SetNot serialized (by default)JSON.stringify(new Map()) gives {}
BigIntNot supportedError: TypeError: Do not know how to serialize a BigInt

What it may return and how it combines with replacer

You may return any value: a string, a number, even null.

javascript
const product = { name: 'T-shirt', price: 1500, toJSON() { return `${this.name}: ${this.price} UAH`; } }; console.log(JSON.stringify(product)); // '"T-shirt: 1500 UAH"'

The returned string becomes the entire serialization result.

If an object has both toJSON() and a replacer passed in, toJSON() runs first and its result is what reaches the replacer:

javascript
const obj = { name: 'Alice', toJSON() { return { custom: true }; } }; console.log(JSON.stringify(obj, (key, value) => { if (key === 'custom') return 'ok'; return value; })); // '{"custom":"ok"}'

Summary

QuestionAnswer
What it doesDefines what will be serialized by JSON.stringify()
Where it is calledAutomatically during JSON.stringify()
What it returnsAny value (object, string, number and so on)
Does it work recursivelyYes, for nested objects
Can it be used in classesYes
Does it affect JSON.parse()No, it only affects serialization

Common mistakes

  • Expecting JSON.parse() to give back a class instance. toJSON() is one way: restoring the type needs your own constructor, a factory, or a reviver in JSON.parse().
  • Declaring toJSON as an arrow function class field. Then this does not point where you expect; use a regular method.
  • Returning the object itself (return this) from toJSON(). For the root call that is either infinite recursion or the same result with no benefit.
  • Thinking toJSON() encrypts or protects data. It only shapes the representation; a hidden field is still readable straight off the object.
  • Forgetting that the method also fires for nested values. A single toJSON() deep in the tree can silently change the whole payload.
  • Counting on toJSON() for Map, Set or BigInt. They do not have one (and BigInt throws a TypeError), so you have to write the conversion yourself.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.