mathjs
mathjs is a broad mathematics toolkit for JavaScript and Node.js. It combines familiar numeric functions with arbitrary-precision decimals, fractions, complex numbers, physical units, dense and sparse matrices, symbolic derivatives, and a parser for calculator-style expressions. It can solve the awkward mixed-type jobs that built-in `Math` cannot, but its full feature set and expression runtime are much larger than a focused arithmetic package.
mathjs earns its weight when a product genuinely needs several of its numeric types, units, matrices, and expression tools together. For one focused task, install the focused package; for untrusted expressions, current patching and process isolation are requirements, not optional hardening.
Use it if
- You need one API that can calculate with numbers, BigNumbers, fractions, complex values, units, arrays, and matrices
- Your product needs a calculator expression language with variables, compiled expressions, or persistent parser scope
- You need symbolic derivatives or expression-tree output alongside numeric evaluation
- You are prepared to build a custom or number-only bundle when browser payload matters
- You only need decimal money arithmetic: `decimal.js` provides the underlying arbitrary-precision decimal model without mathjs's parser, matrices, units, and 192.1 KB gzipped full bundle
- You will evaluate untrusted formulas inside a request handler: the security guide warns about unknown parser vulnerabilities, CPU and memory exhaustion, and recommends a killable worker or child process
- You require Node 16 or older: version 15.2.0 declares Node 18 as the minimum runtime and targets ES2020-capable JavaScript engines
- You expect every operation to preserve one simple JavaScript type: outputs depend on inputs and configuration, mixed arrays and Matrix values can produce a Matrix, `sqrt(-4)` produces a Complex value, and BigNumber conversion can lose precision
- Your legal review requires every included component to use only Apache terms: mathjs is Apache-2.0, but its README states that the bundled CSparse port is LGPL-2.1-or-later
Setup reality
`npm install mathjs` has no peer dependencies or native compilation, and the package exposes both ESM imports and a CommonJS `require` entry. Node 18 or newer is required. The easy full import hides the main production cost: the measured browser bundle is 192.1 KB gzipped and pulls nine runtime dependencies. Import named functions so a modern bundler can tree-shake, use `mathjs/number` when JavaScript numbers are enough, or create a custom instance from dependency collections such as `addDependencies`. Configuration belongs on an instance created with `create`; this also prevents one feature's precision or matrix choice from surprising unrelated code. BigNumber calculations are slower, are not supported by every function, and should begin from decimal strings when the source value must not first pass through binary floating point. If you use `evaluate`, remember that assignments mutate the supplied scope. Prefer a `Map`, compile repeated expressions once, and clear or discard parser instances between users because a parser retains variables and functions. Treat user formulas as hostile work: the official security page recommends disabling dangerous parser-visible functions and running evaluation in a worker or child process that can be terminated for time or memory abuse. Version 15.2.0 fixed two expression-parser vulnerabilities that allowed arbitrary JavaScript execution, so pinning an older major is not a harmless compatibility choice. Browser users should also avoid the complete prebuilt global bundle unless the payload is acceptable. There are no credentials or config files, but numeric type, precision, tolerances, matrix output, import policy, execution isolation, and bundle composition are application decisions you must make before shipping.
Patterns
Import only the functions you callcalculate-basic-values
import { round, sqrt, log } from 'mathjs';
round(Math.E, 3); // 2.718
log(10000, 10); // 4
sqrt(-4).toString(); // '2i'Named imports give bundlers a chance to remove unused code. `sqrt(-4)` returns a Complex object, not a number.
Evaluate a formula against a scopeevaluate-with-scope
import { evaluate } from 'mathjs';
const scope = new Map([['price', 12.5], ['quantity', 4]]);
const total = evaluate('price * quantity', scope); // 50
evaluate('tax = total * 0.2', new Map([...scope, ['total', total]]));A `Map` is the documented safer scope shape. Assignments write into the supplied scope, so do not reuse one across tenants or requests.
Compile once and evaluate many timescompile-repeated-expression
import { compile } from 'mathjs';
const formula = compile('a * x^2 + b');
for (const x of [1, 2, 3]) {
console.log(formula.evaluate({ a: 2, b: 1, x }));
}Compilation avoids reparsing the same expression. It does not make hostile formulas safe from expensive computation.
Use a parser with isolated statekeep-parser-state
import { parser } from 'mathjs';
const session = parser();
session.evaluate('rate = 1.08');
session.evaluate('price = 25');
session.evaluate('price * rate'); // 27
session.remove('price');
session.clear();A parser retains assignments and function definitions. Create one per trusted session and clear or discard it when that session ends.
Create an isolated BigNumber instanceconfigure-big-numbers
import { all, create } from 'mathjs';
const math = create(all, {
number: 'BigNumber',
precision: 64,
relTol: 1e-60,
absTol: 1e-63,
});
math.evaluate('0.1 + 0.2').toString(); // '0.3'Tune relative and absolute tolerances with precision. The docs warn that leaving number-oriented defaults can make high-precision comparisons misleading.
Keep rational arithmetic exactcalculate-fractions
import { add, divide, format, fraction } from 'mathjs';
const a = fraction(1, 3);
const b = fraction(3, 7);
format(add(a, b)); // '16/21'
format(divide(a, b)); // '7/9'Version 14 moved Fraction internals to bigint. Convert to a JavaScript number only when losing exactness is acceptable.
Parse and convert physical unitsconvert-physical-units
import { unit } from 'mathjs';
const distance = unit('12.7 cm');
distance.to('inch').toString(); // '5 inch'
unit(20, 'degC').to('degF').toString(); // '68 degF'Units carry dimensions and conversion rules. Keep them as Unit values until the boundary instead of stripping values early.
Multiply matrices and inspect the resultwork-with-matrices
import { det, matrix, multiply, size } from 'mathjs';
const a = matrix([[2, 0], [-1, 3]]);
const b = [[7, 1], [-2, 3]];
const product = multiply(a, b);
product.toArray(); // [[14, 2], [-13, 8]]
size(product); // [2, 2]
det(b); // 23Mixed Array and Matrix input returns a Matrix. In version 15, `size` always returns a plain array.
Differentiate and simplify symbolicallydifferentiate-expression
import { derivative, simplify } from 'mathjs';
const d = derivative('x^3 + 2*x', 'x');
d.toString(); // '3 * x ^ 2 + 2'
simplify('(x + x) * 2').toString(); // '4 * x'These functions produce expression nodes. Exact formatting can change across versions, so test mathematical meaning instead of snapshotting whitespace.
Parse an expression tree and emit LaTeXrender-expression-latex
import { parse } from 'mathjs';
const node = parse('sqrt(x / x + 1)');
console.log(node.toString());
console.log(node.toTex());`toTex()` returns a string for a renderer such as MathJax. It does not render or sanitize HTML for you.
Remove risky functions from user expressionslimit-expression-api
import { all, create } from 'mathjs';
const math = create(all);
const limitedEvaluate = math.evaluate;
const blocked = () => { throw new Error('disabled in formulas'); };
math.import({
import: blocked,
createUnit: blocked,
reviver: blocked,
evaluate: blocked,
parse: blocked,
simplify: blocked,
derivative: blocked,
resolve: blocked,
}, { override: true });
limitedEvaluate('sqrt(16)'); // 4This follows the official hardening pattern but is not a complete sandbox. Run untrusted work in a killable worker or child process with resource limits.
Create a tree-shakeable custom buildbuild-small-instance
import {
create,
addDependencies,
divideDependencies,
formatDependencies,
fractionDependencies,
} from 'mathjs';
const math = create({
addDependencies,
divideDependencies,
formatDependencies,
fractionDependencies,
});
math.format(math.add(math.fraction(1, 3), math.fraction(1, 6))); // '1/2'A function's dependency collection can still pull several data types. Use imports from `mathjs/number` when number-only behavior is enough.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| decimal.js | npm | You need dependable arbitrary-precision decimal arithmetic without a general math environment |
| ml-matrix | npm | Linear algebra is the main job and you do not need units, symbolic expressions, or a calculator parser |
| expr-eval | npm | You need a smaller expression evaluator with a narrower feature surface |
| nerdamer | npm | Symbolic algebra, equations, and manipulation matter more than mixed numeric data types |