Articles

What Is BigInt in JavaScript? Big Integers Explained

BigInt is JavaScript's built-in type for whole numbers beyond Number's safe limit. How the syntax works, what it can't do, and when you actually need it.

Takina Takina · · 4 min read
Code on a laptop screen in a dark room

BigInt is a built-in JavaScript primitive type for representing whole numbers of arbitrary size — integers that can exceed the range a regular Number can safely hold. Every other numeric value in JavaScript, from 1 to 1.5 to Infinity, is a 64-bit floating-point Number. BigInt is a second, separate numeric type that trades floating-point convenience for exact, unbounded integer precision.

Why Number isn’t enough

JavaScript’s Number type stores every value as an IEEE 754 double-precision float, which gives it exactly 53 bits of usable integer precision. That means Number can represent whole numbers exactly only up to Number.MAX_SAFE_INTEGER (2^53 − 1, or 9,007,199,254,740,991). Past that point, some integers simply can’t be represented — the float format has to round to the nearest representable value, and adjacent integers start colliding.

This is easy to demonstrate:

console.log(9007199254740992 === 9007199254740993); // true

Two clearly different integers compare as equal because both round to the same underlying float. For everyday arithmetic this rarely matters. For cryptographic key material, 64-bit database IDs, high-precision timestamps, or any math where losing a single integer’s worth of precision is a correctness bug, it matters a great deal.

Creating and using BigInt values

A BigInt literal is a normal integer literal with an n suffix:

const big = 9007199254740993n;
const alsoOK = BigInt(9007199254740993); // constructor form, though this loses precision if the source is already an imprecise Number
const fromString = BigInt("9007199254740993"); // safest way to construct one

Arithmetic operators (+, -, *, /, %, **) all work on BigInt values, and division truncates toward zero rather than producing a fraction — 7n / 2n is 3n, not 3.5n, because BigInt has no concept of a decimal point.

What you can and can’t do with BigInt

BigInt deliberately does not mix with Number in arithmetic. This throws a TypeError:

1n + 1; // TypeError: Cannot mix BigInt and other types

You have to convert explicitly in one direction — 1n + BigInt(1) or Number(1n) + 1 — which is a deliberate design choice, not an oversight. Silently coercing between the two would reintroduce exactly the precision loss BigInt exists to prevent. Comparison operators (<, >, ==) are the one exception: 1n == 1 is true, because loose equality across types was already part of the language before BigInt arrived.

A few other constraints worth knowing:

  • BigInt values can’t be used with Math object methods (Math.sqrt(4n) throws).
  • They can’t be serialized by JSON.stringify() by default — you’ll need a custom replacer or toJSON method.
  • Comparing a BigInt to NaN or using it where null/undefined coercion happens can produce surprising results, since BigInt has its own rules for typeof ("bigint") and coercion.

BigInt vs Number: a quick comparison

NumberBigInt
Underlying formatIEEE 754 doubleArbitrary-precision integer
Safe integer range±(2^53 − 1)Unbounded
Decimals supportedYesNo — integers only
Typical useGeneral arithmetic, measurementsLarge IDs, crypto, exact integer math
Mixing in expressionsNot allowed without explicit conversion
JSON.stringify supportNativeRequires custom handling

Where BigInt actually matters

The clearest use cases are places where an integer naturally exceeds 2^53: snowflake-style distributed IDs, 64-bit database primary keys pulled in from a backend, cryptographic operations that need exact modular arithmetic, and any protocol (like some binary formats read through typed arrays and ArrayBuffer) that specifies 64-bit integer fields. If you’re hashing or signing data, exact integer math is often a hard requirement rather than a nice-to-have — see how digital signatures work for the kind of arithmetic where a rounding error would silently break a signature.

BigInt also shows up when JavaScript is used as a host for lower-level numeric work — reading binary buffers in Node.js, or interoperating with WebAssembly modules that pass 64-bit integers across the boundary. Before BigInt existed, developers reached for string-based big-number libraries to fake this; now the capability is native, at the cost of not composing with regular numeric code for free.

Gotchas to watch for

The biggest practical trap is JSON. APIs that return large integer IDs as raw JSON numbers (not strings) have often already lost precision by the time JSON.parse() hands them to you — BigInt can’t retroactively fix a value that was already rounded during parsing. The safe pattern is for the API to send those IDs as strings and for the client to convert with BigInt(idString) explicitly.

The second trap is performance: BigInt arithmetic is meaningfully slower than Number arithmetic for small values, because the engine can’t use the fast native integer paths it uses for regular numbers. Reach for BigInt when correctness genuinely requires it, not as a default replacement for Number in ordinary loops or counters.

Type-checking code should also account for the new primitive. typeof 10n returns "bigint", distinct from "number", so any code that branches on typeof x === "number" needs an explicit BigInt branch if it’s meant to handle both.

The takeaway

BigInt gives JavaScript exact, arbitrary-precision integers where Number’s 53-bit limit would otherwise silently round large values. Reach for it when you’re handling IDs, cryptographic values, or binary data that specifies 64-bit integers — and reach for plain Number everywhere else, since BigInt doesn’t mix with it automatically and carries a real performance cost. The two types are meant to coexist deliberately, not to replace one another.

Takina Takina · · 4 min read

Svelte 5 Runes Explained

Svelte 5 runes like $state and $derived replace the old reactive-assignment magic with explicit function calls that work anywhere in a file.

#JavaScript #Frameworks #Web Development
Takina Takina · · 4 min read

Shallow Copy vs Deep Copy in JavaScript

A shallow copy duplicates an object's top-level properties but shares nested references; a deep copy duplicates everything recursively. How to do each.

#JavaScript #Web Development #Frontend