SuperJSON
SuperJSON
SuperJSON
Overview
SuperJSON is a library that safely serializes JavaScript expressions to a superset of JSON, supporting complex types that standard JSON doesn't handle, such as Date, BigInt, RegExp, Map, Set, and Error. It functions as a thin wrapper over JSON.stringify and JSON.parse, using metadata to preserve type information. It's particularly useful for Next.js data fetching functions like getServerSideProps and getStaticProps when dealing with non-standard types like Date objects.
Details
SuperJSON is designed to overcome the limitations of standard JSON serialization. It has the following features:
Supported Types
Standard JSON Types:
- string, number, boolean, null, Array, Object
Extended Types (Supported by Default):
- undefined: Not omitted from objects, not converted to null in arrays
- BigInt: Saved as string with type information in metadata
- Date: Saved as ISO string, restored as Date object on deserialization
- RegExp: Saved as pattern string
- Set: Saved as array with Set type identified in metadata
- Map: Saved as array of key-value pairs
- Error: Saved as object containing name and message
- URL: Saved as string with URL type identified in metadata
- TypedArray: Saved as array with type information in metadata
- Special Numbers: NaN, Infinity, -Infinity, -0
How It Works
SuperJSON separates data into two parts: json (JSON-compatible data) and meta (metadata such as type information). This allows standard JSON parsers to read the basic data, while SuperJSON can restore complete type information when used.
TypeScript Integration
Written in TypeScript, it provides strong type safety, enabling autocompletion and type checking for deserialized values.
Advantages and Disadvantages
Advantages
- Type Preservation: Accurately saves and restores complex JavaScript types like Date, Map, Set
- Referential Equality: Correctly restores multiple references to the same object
- TypeScript Support: Full type safety and autocompletion
- Lightweight: Maintains minimal bundle size (only supports built-in types by default)
- Next.js Integration: Easy integration with SWC or Babel plugins
- Custom Type Support: Can register custom types with registerCustom
- Backward Compatibility: Readable as standard JSON (basic data accessible without metadata)
- Error Handling: Supports Error object serialization
Disadvantages
- Additional Dependency: Requires adding an external library to the project
- Performance: Slightly slower than standard JSON.stringify/parse
- File Size: Output size larger than standard JSON due to metadata
- Learning Curve: Basic usage is simple, but advanced features require understanding
- Compatibility: Requires metadata handling when interoperating with systems not using SuperJSON
- Maintenance: Concerns about maintenance status of Next.js plugins (as of 2025)
Reference Pages
- SuperJSON GitHub Repository
- SuperJSON npm Package
- next-superjson-plugin
- SuperJSON Documentation
- Blitz.js SuperJSON Integration
Code Examples
Hello World - Basic Usage
import superjson from 'superjson';
// Serialization
const data = {
message: "Hello World",
createdAt: new Date(2025, 0, 1),
pattern: /hello/gi,
tags: new Set(['greeting', 'example'])
};
const jsonString = superjson.stringify(data);
console.log(jsonString);
// Output: {"json":{"message":"Hello World","createdAt":"2025-01-01T00:00:00.000Z","pattern":"/hello/gi","tags":["greeting","example"]},"meta":{"values":{"createdAt":["Date"],"pattern":["regexp"],"tags":["set"]}}}
// Deserialization
const parsed = superjson.parse(jsonString);
console.log(parsed.createdAt instanceof Date); // true
console.log(parsed.tags instanceof Set); // true
Next.js Integration
// pages/api/user.ts
import superjson from 'superjson';
export default function handler(req, res) {
const userData = {
id: 1,
name: "John Doe",
registeredAt: new Date(),
tags: new Set(['admin', 'verified']),
metadata: new Map([
['lastLogin', new Date()],
['loginCount', BigInt(100)]
])
};
// Serialize with SuperJSON and send response
res.status(200).json(superjson.serialize(userData));
}
// pages/index.tsx
import { GetServerSideProps } from 'next';
import superjson from 'superjson';
// When using next-superjson-plugin
export const getServerSideProps: GetServerSideProps = async () => {
const user = {
name: "Jane Smith",
createdAt: new Date(),
preferences: new Map([
['theme', 'dark'],
['language', 'en']
])
};
return {
props: {
user // Plugin automatically serializes with SuperJSON
}
};
};
// Component receives it as a regular object
export default function HomePage({ user }) {
console.log(user.createdAt instanceof Date); // true
console.log(user.preferences instanceof Map); // true
return (
<div>
<h1>Welcome, {user.name}!</h1>
<p>Member since: {user.createdAt.toLocaleDateString()}</p>
</div>
);
}
Registering Custom Types
import superjson from 'superjson';
import { Decimal } from 'decimal.js';
// Register custom serializer for Decimal.js type
superjson.registerCustom<Decimal, string>(
{
isApplicable: (v): v is Decimal => Decimal.isDecimal(v),
serialize: (v) => v.toJSON(),
deserialize: (v) => new Decimal(v),
},
'decimal.js'
);
// Custom class example
class Point {
constructor(public x: number, public y: number) {}
distance() {
return Math.sqrt(this.x ** 2 + this.y ** 2);
}
}
// Register custom class
superjson.registerClass(Point);
// Usage example
const data = {
price: new Decimal('99.99'),
location: new Point(3, 4)
};
const serialized = superjson.stringify(data);
const deserialized = superjson.parse<typeof data>(serialized);
console.log(deserialized.price.toString()); // "99.99"
console.log(deserialized.location.distance()); // 5
Error Handling and Security
import superjson from 'superjson';
// Error object serialization
class CustomError extends Error {
constructor(message: string, public code: string) {
super(message);
this.name = 'CustomError';
}
}
const error = new CustomError('Something went wrong', 'ERR_001');
const serializedError = superjson.stringify({ error });
const { error: deserializedError } = superjson.parse(serializedError);
console.log(deserializedError instanceof Error); // true
console.log(deserializedError.message); // "Something went wrong"
// Safe deserialization
function safeParse<T>(jsonString: string): T | null {
try {
return superjson.parse<T>(jsonString);
} catch (error) {
console.error('Failed to parse SuperJSON:', error);
return null;
}
}
Advanced Usage - serialize and deserialize
import superjson from 'superjson';
// Detailed control with serialize/deserialize
const complexData = {
user: {
id: 1,
name: "Alice Johnson",
birthday: new Date(1990, 5, 15),
permissions: new Set(['read', 'write', 'admin'])
},
session: {
token: 'abc123',
expiresAt: new Date(Date.now() + 3600000),
metadata: new Map([
['ip', '192.168.1.1'],
['userAgent', 'Mozilla/5.0...']
])
}
};
// Separate json and meta with serialize
const { json, meta } = superjson.serialize(complexData);
console.log('JSON part:', JSON.stringify(json, null, 2));
console.log('Metadata:', JSON.stringify(meta, null, 2));
// Can save/send json and meta separately
// Example: json in API response, meta in headers, etc.
// Restoration
const restored = superjson.deserialize({ json, meta });
console.log(restored.user.birthday instanceof Date); // true
console.log(restored.session.metadata instanceof Map); // true
State Management Library Integration
import { create } from 'zustand';
import { persist } from 'zustand/middleware';
import superjson from 'superjson';
// Zustand store example
interface StoreState {
user: {
name: string;
lastSeen: Date;
tags: Set<string>;
} | null;
preferences: Map<string, any>;
setUser: (user: StoreState['user']) => void;
updatePreference: (key: string, value: any) => void;
}
const useStore = create<StoreState>()(
persist(
(set) => ({
user: null,
preferences: new Map(),
setUser: (user) => set({ user }),
updatePreference: (key, value) =>
set((state) => {
const newPrefs = new Map(state.preferences);
newPrefs.set(key, value);
return { preferences: newPrefs };
}),
}),
{
name: 'app-storage',
storage: {
getItem: (name) => {
const str = localStorage.getItem(name);
if (!str) return null;
return superjson.parse(str);
},
setItem: (name, value) => {
localStorage.setItem(name, superjson.stringify(value));
},
removeItem: (name) => localStorage.removeItem(name),
},
}
)
);
// Usage example
const Component = () => {
const { user, setUser } = useStore();
const handleLogin = () => {
setUser({
name: "New User",
lastSeen: new Date(),
tags: new Set(['new', 'unverified'])
});
};
return (
<div>
{user && (
<p>Last seen: {user.lastSeen.toLocaleString()}</p>
)}
</div>
);
};