JavaScript Bigint: Complete Guide
BigInt is JavaScript's primitive for arbitrarily large integers. Learn to create BigInts, perform operations, handle mixed-type rules, and use BigInt for precise math beyond Number's limits.
A BigInt is a numeric primitive in JavaScript that can represent integers of any size. The standard Number type maxes out at 9,007,199,254,740,991 (Number.MAX_SAFE_INTEGER), and beyond that, integer precision breaks down.
BigInt has no such limit.
const maxSafe = Number.MAX_SAFE_INTEGER;
console.log(maxSafe); // 9007199254740991
console.log(maxSafe + 1); // 9007199254740992 (OK)
console.log(maxSafe + 2); // 9007199254740992 (WRONG -- same as +1!)
// BigInt handles this correctly
const bigSafe = 9007199254740991n;
console.log(bigSafe + 1n); // 9007199254740992n
console.log(bigSafe + 2n); // 9007199254740993n (correct!)The n suffix marks a BigInt literal. Without it, the value is a regular Number and subject to the same precision limits.
Creating BigInts
JavaScript gives you two ways to create a BigInt, and which one you reach for depends on whether the value is known while you are writing the code or only becomes available at runtime:
// Literal with n suffix -- for values known at authoring time
const a = 42n;
const b = -10n;
// Constructor -- for values computed or parsed at runtime
const c = BigInt(42);
const d = BigInt("9007199254740993"); // From a string
const e = BigInt(true); // 1n
const f = BigInt(false); // 0n
console.log(a, b, c, d, e, f);
// 42n, -10n, 42n, 9007199254740993n, 1n, 0nUse the n suffix for literals. Use BigInt() when converting from a Number, a string, or a boolean. Passing a decimal string or a non-integer Number throws a RangeError:
BigInt(3.14); // RangeError
BigInt("3.14"); // SyntaxError
BigInt("not a number"); // SyntaxErrorOnly integers can become BigInts. No fractions, no decimals.
Arithmetic with BigInts
BigInt supports the standard arithmetic operators, and they behave the same way you would expect from regular integer math, with one exception around division covered right after this example:
const x = 100n;
const y = 3n;
console.log(x + y); // 103n -- addition
console.log(x - y); // 97n -- subtraction
console.log(x * y); // 300n -- multiplication
console.log(x / y); // 33n -- division (truncates toward zero!)
console.log(x % y); // 1n -- remainder
console.log(x ** y); // 1000000n -- exponentiationDivision truncates toward zero. 100n divided by 3n is 33n, not 33.333, because there are no fractional BigInts at all. If you need the quotient and remainder together, a small helper function can compute both at once:
function divmod(a, b) {
return { quotient: a / b, remainder: a % b };
}
console.log(divmod(100n, 3n)); // { quotient: 33n, remainder: 1n }The Hard Rule: No Mixing with Number
You cannot mix BigInt and Number in the same operation. This is one of the strictest rules in the language, and JavaScript enforces it at runtime rather than silently converting one side for you:
const big = 10n;
const num = 5;
big + num; // TypeError: Cannot mix BigInt and other typesThis is by design. Adding a BigInt and a Number creates an ambiguity: should the result be a BigInt, losing the fractional part of the Number, or a Number, losing the precision of the BigInt?
The language refuses to guess. You must convert explicitly:
const big = 10n;
const num = 5;
// Explicit conversion -- you choose which type
console.log(big + BigInt(num)); // 15n
console.log(Number(big) + num); // 15 (loses precision if BigInt is huge)Convert to BigInt when you need exact integer results. Convert to Number only when you are certain the BigInt fits safely within Number's safe integer range.
Comparisons
Comparison operators work across BigInt and Number, but strict equality does not:
console.log(1n == 1); // true -- loose equality coerces
console.log(1n === 1); // false -- different types
console.log(1n < 2); // true
console.log(2n > 1); // true
console.log(1n < 1); // false
console.log(1n <= 1); // trueUse loose equality for cross-type checks when you only care about the mathematical value. Use strict equality when you need to distinguish BigInt from Number.
| Comparison | Between 1n and 1 | Why |
|---|---|---|
| Loose equality | true | Loose equality coerces both sides to the same numeric value |
| Strict equality | false | Strict equality also checks the type, and BigInt and Number are different types |
| Relational (less than, greater than, etc.) | Work normally | These operators compare mathematical value, not type |
BigInt and Math Methods
The Math object does not accept BigInts. Calling Math.abs on a BigInt throws a TypeError. This is because Math methods operate on Numbers and return Numbers, and silent conversion would lose precision:
// Math.abs(-5n); // TypeError
// Instead, write your own for BigInts when needed
function bigAbs(n) {
return n < 0n ? -n : n;
}
console.log(bigAbs(-5n)); // 5nJSON and BigInt
JSON.stringify throws on BigInt because the JSON format has no BigInt type of its own. This is a common surprise the first time an ID field happens to hold a BigInt value:
const data = { id: 9007199254740993n, name: "Alex" };
// JSON.stringify(data); // TypeError: Do not know how to serialize a BigIntThe fix is a custom toJSON() method on the object, or a replacer function passed to JSON.stringify that handles every BigInt regardless of where it appears:
// Option 1: toJSON on the object
const data = {
id: 9007199254740993n,
name: "Alex",
toJSON() {
return { id: this.id.toString(), name: this.name };
}
};
console.log(JSON.stringify(data)); // {"id":"9007199254740993","name":"Alex"}
// Option 2: replacer function (works for any BigInt anywhere)
function bigintReplacer(key, value) {
if (typeof value === "bigint") return value.toString();
return value;
}
console.log(JSON.stringify({ id: 1n }, bigintReplacer)); // {"id":"1"}To parse those strings back into real BigInt values on the way in, pass a reviver function as the second argument to JSON.parse:
const json = '{"id":"9007199254740993","name":"Alex"}';
const parsed = JSON.parse(json, (key, value) => {
if (key === "id") return BigInt(value);
return value;
});
console.log(parsed.id); // 9007199254740993nPractical Use Case: Unique IDs
Many systems generate IDs larger than MAX_SAFE_INTEGER. Twitter Snowflake IDs and some database auto-increment values routinely exceed 9 quadrillion:
// A Snowflake-style ID from an API response
const rawId = "18285738472829337600";
const id = BigInt(rawId);
console.log(id); // 18285738472829337600n
console.log(Number(rawId)); // 18285738472829338000 (wrong last 3 digits!)
// BigInt preserves every digit
console.log(id.toString()); // "18285738472829337600"If you store this ID as a Number, the last few digits are lost. If you later query by that truncated ID, the query fails silently, since the database has no row matching the rounded value. BigInt avoids this entire class of bug by preserving the exact digits from the moment the value enters your code.
Practical Use Case: Timestamp Arithmetic at Nanosecond Precision
When working with high-resolution timestamps, summing many values can overflow Number precision long before it overflows the value range, since Number silently loses integer accuracy well before it runs out of digits:
// High-resolution timestamps (performance.now() returns fractional ms)
// A BigInt-based accumulator avoids precision drift
let totalNs = 0n;
function addMeasurement(startNs, endNs) {
totalNs += (endNs - startNs);
}
addMeasurement(1000000001n, 1000000050n);
addMeasurement(2000000001n, 2000000100n);
console.log(totalNs); // 148nBigInt Limits and Gotchas
| Gotcha | Explanation |
|---|---|
| No fractional results | 5n / 2n is 2n, not 2.5n. Use a library if you need arbitrary-precision decimals. |
| No unary plus | +5n throws a TypeError. Use BigInt() or stick with the literal. |
| No unsigned right shift | BigInts are signed and arbitrarily large, so unsigned right shift has no defined meaning. |
| No Math methods | Math.max(1n, 2n) throws. Write your own helper. |
| parseInt and parseFloat return Number | Use BigInt() or a string approach for large values. |
BigInt is a specialized tool. For everyday math, Number is faster and more convenient, and /javascript/javascript-number-type-complete-guide covers the standard numeric type in full.
Reach for BigInt when you see values larger than 9 quadrillion or when you need a guarantee of exact integer results. For serializing BigInt values, review the JSON handling patterns in /javascript/json-parse-and-json-stringify-in-javascript-guide.
Rune AI
Key Insights
- BigInt is a primitive type for integers of any size, created with an n suffix (42n) or BigInt(value).
- BigInt supports +, -, *, /, **, and % but cannot be mixed with Number in the same operation.
- Division with BigInt truncates toward zero (no fractional results).
- Use BigInt for values beyond Number.MAX_SAFE_INTEGER (9,007,199,254,740,991).
- JSON.stringify does not support BigInt. Use toJSON() or a replacer to serialize as strings.
Frequently Asked Questions
When should I use BigInt instead of Number?
Why can't I mix BigInt and Number in operations?
Conclusion
BigInt fills the gap that Number left open for integer precision. When you need to work with IDs larger than 9 quadrillion, perform exact financial calculations without floating-point rounding, or handle cryptographic values, BigInt gives you arbitrary-precision integers as a first-class primitive. The mixed-type restrictions are deliberate and protect you from silent precision bugs. Convert explicitly, and BigInt delivers exactly the value you expect.
More in this topic
Using Decorators for Logging in JS Architecture
Learn how JavaScript decorators wrap class methods to add logging without touching the original code, including setup with Babel and how execution order works.
JavaScript While Loop Explained: A Complete Guide
A while loop repeats code as long as a condition stays true. Learn the syntax, see practical examples, and understand when a while loop is the right choice over a for loop.
JavaScript Loops Tutorial: for, while, do while
Loops let JavaScript repeat code without writing it twice. Learn the three core loop types, what each one does, and when to choose one over another.