Serialize Temporal Values to JSON in JavaScript

To round-trip Temporal values through JSON, write with toString()/toJSON and read with a reviver that calls Temporal.*.from(). Part of Serializing and Storing Temporal Values.

Why this scenario is tricky

JSON.stringify has built-in special handling for Date (it calls toISOString), but no knowledge of Temporal types. Left alone, a ZonedDateTime serializes via its toJSON to a string on the way out — good — but JSON.parse has no idea it should become a Temporal value again, so it silently stays a string and the next .add() call throws.

Stringify is half the round-tripparse leaves Temporal values as stringsStringify is half the round-tripJSON.parse(json)zdt is now a stringparse with a reviverfrom() restores the typeYou must supply a reviver; parse will not rebuild Temporal types for you.

Minimal working solution

import { Temporal } from '@js-temporal/polyfill';
const json = JSON.stringify({ at: zdt });         // zdt.toJSON() → ISO string
const obj = JSON.parse(json, (k, v) =>
  k === 'at' ? Temporal.ZonedDateTime.from(v) : v); // reviver rehydrates

Reviver on parseMap known keys back to Temporal typesReviver on parseJSON stringparse + revivertyped value

Full production version

Tag values with their type so one reviver handles a whole payload.

import { Temporal } from '@js-temporal/polyfill';
const REVIVE: Record<string, (s: string) => unknown> = {
  Instant: (s) => Temporal.Instant.from(s),
  ZonedDateTime: (s) => Temporal.ZonedDateTime.from(s),
  PlainDate: (s) => Temporal.PlainDate.from(s),
};
function reviver(_k: string, v: any) {
  return v && typeof v === 'object' && v.__t in REVIVE ? REVIVE[v.__t](v.value) : v;
}

Type-tagged revivalCarry a __t tag so any type rehydratesType-tagged revival{__t,value}reviverlookup __tfrom()

Verification snippet

JSON round-trip assertionsValues survive stringify then parseAssertions that prove the edge casestringify(zdt)ISO stringparse w/ reviverZonedDateTimeno reviverstays string.add works aftertrue

Common pitfalls

JSON pitfallsForgetting the reviver or mixing DateWrongRightJSON.parse alonestring, not Temporalparse with a revivertyped valuesmix Date and Temporal keysinconsistentone tagged codecuniform

Frequently Asked Questions

Does JSON.stringify work on Temporal values?

Yes on the way out — most Temporal types implement toJSON, so they serialize to their canonical ISO string. The gap is on the way in: JSON.parse returns those as plain strings, so you must pass a reviver that calls Temporal.*.from() to rebuild the typed values.

How do I revive many different Temporal types from one payload?

Tag each serialized value with its type name and value, then use a single reviver that looks up the tag and calls the matching from(). This handles Instant, ZonedDateTime, and PlainDate in the same object without per-key special cases.