Skip to content

SuperJSON

Serialization & FormatsSerializationTypeScript

What it is

SuperJSON serialises JavaScript values that JSON cannot represent — Date, Map, Set, BigInt, undefined and RegExp — and restores them on the other side.

Use stringify and parse as drop-in replacements for JSON's. SuperJSON records type metadata alongside the data and uses it to rebuild the original values.

Installation

npm install superjson

Getting started

The smallest useful thing you can do with it, and what each part means.

What plain JSON loses
import superjson from 'superjson';

const payload = {
  createdAt: new Date('2026-01-01'),
  tags: new Set(['a', 'b']),
  scores: new Map([['x', 1]]),
  missing: undefined,
  big: 10n,
};

JSON.parse(JSON.stringify(payload));
// createdAt is a string, tags is {}, scores is {}, missing is gone,
// and BigInt throws outright.

const restored = superjson.parse(superjson.stringify(payload));
restored.createdAt instanceof Date; // true
restored.tags.has('a');             // true
The Set and Map becoming empty objects is the dangerous one — it fails silently rather than throwing, so the bug surfaces far from its cause.

Advanced usage

Where the library earns its place over a simpler alternative.

Registering custom classes
class Money {
  constructor(public cents: number, public currency: string) {}
}

superjson.registerCustom<Money, string>(
  {
    isApplicable: (v): v is Money => v instanceof Money,
    serialize: (v) => `${v.cents}:${v.currency}`,
    deserialize: (s) => {
      const [cents, currency] = s.split(':');
      return new Money(Number(cents), currency);
    },
  },
  'Money',
);
Both ends must register the same transformer with the same name, or deserialisation produces a plain string rather than a Money instance.

Errors and fixes

The failures you are most likely to hit, and what actually resolves them.

Values come back as plain objects
The consumer used JSON.parse rather than superjson.parse, so the metadata was ignored.
Custom class deserialises as a string
The transformer is registered on only one side, or under a different name.

Best practices

  • Use it where rich values genuinely cross a boundary — tRPC and Next.js server props are the common cases.
  • Do not use it for public APIs; the output is non-standard JSON that other clients will not understand.
  • Register custom transformers identically on both sides.
  • Prefer plain ISO strings for simple date-only payloads — it is one less dependency.

Background

Why it exists, and what it was reacting to.

Created by the Blitz.js team, SuperJSON exists because JSON silently degrades rich values: a Date becomes a string, a Map becomes an empty object, and undefined disappears entirely.