JavaScript example

How to convert a string to a number in JavaScript

7 min read Updated Sep 2026 Runs in an isolated runtime
Quick answer

Use Number(text): Number("42") is 42, and anything that isn't a number as a whole, like "42px", gives NaN. Check the result with Number.isFinite(), and reject blank strings first, because Number("") is 0. Use parseInt(text, 10) only when you want the number at the start of a longer string.

JavaScript has one ordinary number type, so "string to integer" and "string to float" are the same conversion. There are still several ways to write it, and they disagree about the edge cases: Number() and unary + convert the whole string or fail, while parseInt and parseFloat read as far as they can and ignore the rest. None of them throw on bad input. They return NaN, which is why validating the result matters as much as converting it. Each example runs on this page: hit Run, then edit the code and run it again.

1Number() and unary +Recommended

Number(text) converts the entire string. Surrounding whitespace is ignored, a leading sign, decimals and exponents are fine, and anything else makes the result NaN. The unary plus operator, +text, runs exactly the same conversion and is common in compact code. Call Number without new: new Number() creates a wrapper object, not a number.

number.js

Output

Prints 42 number, then 50, then 428. That third line is the bug this conversion exists to prevent: + with a string on either side concatenates, so form values and query parameters that were never converted turn sums into glued-together text. The next lines print 3.14 -7 1000 12 and NaN NaN, and unary plus gives the same answers, 42 3.14 NaN. Pick whichever reads better to your team; Number(text) is easier to spot in a review.

2parseInt() and parseFloat(): read the number at the start

parseInt and parseFloat are the older functions, and they work differently. They skip leading whitespace, read as many characters as make a number, and stop at the first one that doesn't fit, returning what they have. They only return NaN when there is nothing to read at all. That makes them right for text like "42px" and wrong for validation. Always pass parseInt its second argument, the radix: without it, a 0x prefix silently switches to hex.

parse.js

Output

The partial reads print 42, 3.99, 3 and NaN: parseInt("3.99", 10) doesn't round, it just stops at the dot. Without a radix "0x1F" is 31, with radix 10 it is 0, and radix 16 and 2 give 255 10. parseInt(0.0000005) is 5 because the number becomes the string 5e-7 before parsing, so never use parseInt to drop the decimals of a number; use Math.trunc. Finally, .map(parseInt) prints [ 10, NaN, 2 ]: map calls it with (value, index), so the index lands in the radix slot. .map(Number) gives [ 10, 10, 10 ].

3The traps: empty strings, whitespace and partial text

The biggest surprise in Number() is that an empty or all-whitespace string converts to 0, not NaN. An empty form field is therefore a valid zero unless you check for it. The grid below runs the same inputs through all three functions so you can see exactly where they disagree.

traps.js

Output

Read the rows for "", " " and "\n": Number says 0 while both parse functions say NaN. The other rows show each function's own rules. Number accepts "0x1F" as 31 and "1e3" as 1000, where parseInt reads 0 and 1. Thousands separators and numeric-literal underscores are not part of the string format, so "1,234" and "1_000" are NaN for Number and a misleading 1 for the parse functions. The text "Infinity" converts to Infinity. The last line, 0 NaN 1, is null, undefined and true: a missing value can become a zero too.

4Check that a string is a valid number

Since nothing throws, validation means checking the result. Use the Number. versions: Number.isNaN(x) and Number.isFinite(x) only return true for actual numbers. The older global isNaN() and isFinite() convert their argument with Number() first, so they inherit every trap from section 3. A reliable check is three steps: make sure it's a non-blank string, convert with Number(), and accept only a finite result.

validate.js

Output

The first three lines are true false, false false and true false. So the global isNaN calls a blank string a number, and Number.isFinite("42") is false because a string is not a number yet: convert first, then check. toNumber returns 42, -3.5, 7 and 1000 for the good inputs and null for the blank ones, "12px" and "Infinity". The classic isNumeric agrees on every row, which is why it earned its votes. Both accept "0x1F" (31), since Number reads hex; add a regex such as /^[+-]?(\d+\.?\d*|\.\d+)(e[+-]?\d+)?$/i if you need plain decimal only. For whole numbers add Number.isInteger, which prints 2.5 false here.

5Huge integers: BigInt

A JavaScript number is a 64-bit float, so it holds integers exactly only up to Number.MAX_SAFE_INTEGER, 253 − 1. Past that, Number() and parseInt still return a value, just not the one in the string, and no error tells you. Database IDs, snowflake IDs and 64-bit counters hit this all the time. BigInt(text) parses a whole-number string into an arbitrary-precision integer instead.

bigint.js

Output

Number(id) prints 9007199254740992, one less than the string, and Number.isSafeInteger flags it with false. The BigInt is exact: 9007199254740993n bigint, and big + 1n is 9007199254740994n. Unlike Number(), BigInt() throws on bad text (SyntaxError: Cannot convert 3.5 to a BigInt) but shares the blank-string trap: 0n 12n. Mixing it with a regular number throws TypeError: Cannot mix BigInt and other types, use explicit conversions. The JSON lines print 9007199254740992, then { id: 9007199254740993n }: the reviver's third argument exposes the original source text, which is new enough that older runtimes don't pass it. The safest API design is still to send big IDs as strings.

6Which should you use?

MethodOn "42px"On "" (blank)Best for
Number(s)NaN (rejects)0 (trap)Converting a whole string: the default
+sNaN (rejects)0 (trap)Same as Number(), shorter
parseInt(s, 10)42 (partial read)NaNAn integer at the start of text; other radixes
parseFloat(s)42 (partial read)NaNA decimal at the start of text, like "3.99 USD"
BigInt(s)Throws SyntaxError0n (trap)Integers past Number.MAX_SAFE_INTEGER
isNaN(s)truefalse (calls it a number)Nothing new; use Number.isNaN / Number.isFinite

Frequently asked questions

What is the difference between Number() and parseInt()?

Number(s) converts the whole string and returns NaN if any part of it is not numeric, so Number("42px") is NaN. parseInt(s, 10) reads digits from the start and stops at the first other character, so parseInt("42px", 10) is 42 and parseInt("3.99", 10) is 3. They also disagree on blank strings: Number("") is 0, parseInt("", 10) is NaN. Use Number to convert and parseInt to extract a leading integer.

How do I check if a string is a valid number in JavaScript?

Reject blank strings, convert, then test the result: s.trim() !== "" && Number.isFinite(Number(s)). The blank check matters because Number("") and Number(" ") are 0. Number.isFinite also rejects NaN and Infinity. Add Number.isInteger if only whole numbers are allowed.

What is the difference between isNaN() and Number.isNaN()?

The global isNaN(x) converts x with Number() first and then checks, so isNaN("abc") is true but isNaN("") is false because the empty string converts to 0. Number.isNaN(x) does no conversion and is only true for the actual NaN value, so Number.isNaN("abc") is false. The same split applies to isFinite and Number.isFinite. Prefer the Number. versions on a value you have already converted.

Why does ["1", "2", "3"].map(parseInt) return [1, NaN, NaN]?

map calls its callback with three arguments: the value, the index and the array. parseInt takes the index as its radix, so the calls are parseInt("1", 0), parseInt("2", 1) and parseInt("3", 2). Radix 0 means "guess" and gives 1, radix 1 is invalid, and "3" is not a binary digit. Use .map(Number) or .map((s) => parseInt(s, 10)).

How do I convert a decimal string to an integer?

Convert with Number, then pick the rounding you want: Math.trunc(Number("-3.99")) is -3 (toward zero), and Math.round, Math.floor and Math.ceil round the other ways. parseInt("-3.99", 10) also gives -3 because it stops at the dot, but do not use parseInt on a number: parseInt(1e21) is 1, because the number is turned into the string "1e+21" first.

How do I parse a number with commas, like "1,234,567"?

Remove the separators before converting: Number("1,234,567".replaceAll(",", "")) is 1234567. Number("1,234") is NaN and parseInt("1,234", 10) is a misleading 1. JavaScript has no built-in parser for locale-formatted numbers, so if input may use other conventions such as "1.234,5", normalize it for the locale you expect first.

Run it yourself

Open any of these in the full JavaScript editor to tweak, run and share.

JavaScript playground