Buffer.byteLength()
Returns how many bytes a string occupies in a given encoding, which is not its character count.
Buffer.byteLength(string[, encoding])- Added in Node.js v0.1.90
Parameters
| Parameter | Type | Description |
|---|---|---|
$string | string | Buffer | TypedArray | DataView | ArrayBuffer | SharedArrayBuffer | A value to calculate the length of. |
$encodingoptional | string= 'utf8' | If string is a string, this is its encoding. |
Return value
integerThe number of bytes contained within string.
Errors
Throws ERR_INVALID_ARG_TYPE for input that is neither a string nor an ArrayBuffer-backed value, and ERR_UNKNOWN_ENCODING for an unrecognised encoding name.
Changelog
- PHP v7.0.0Passing invalid input now throws an error instead of coercing it to a string.
- PHP v5.10.0The string parameter can now be any TypedArray, DataView or ArrayBuffer.
JavaScript's String.prototype.length counts UTF-16 code units, not bytes. 'café'.length is 4, but the string takes 5 bytes in UTF-8 because é encodes as two. Buffer.byteLength() gives you the byte count, and getting this wrong is what produces truncated responses and off-by-one framing bugs.
Where it matters
Anything that declares a size in bytes: the Content-Length header, a length prefix in a binary protocol, a database column limit expressed in bytes, or a quota you enforce on user input. Emoji make the gap dramatic — '👍'.length is 2 because it is a surrogate pair, while its UTF-8 length is 4.
The encoding argument is not optional in practice
The default is 'utf8'. Under 'utf16le' the same string is 8 bytes; under 'latin1' or 'ascii' it is always one byte per code unit; under 'base64' the argument is treated as encoded input and you get the decoded size. Passing the wrong one gives a plausible-looking number that is silently wrong.
Non-string input is measured directly: a Buffer, TypedArray, DataView or ArrayBuffer returns its own byte length, so a helper can accept either without branching.
Example
When to use it
- Set an accurate Content-Length header before writing a response body.
- Enforce a byte-based size limit on user-supplied text.
- Write the length prefix of a binary frame before the payload.
- Check that a value fits a VARCHAR column measured in bytes.
Frequently asked questions
Why is string.length different from Buffer.byteLength?
String.length counts UTF-16 code units while Buffer.byteLength counts bytes in the target encoding, so 'café'.length is 4 but its UTF-8 byte length is 5. Any non-ASCII character makes the two diverge.
How do I calculate Content-Length in Node.js?
Use Buffer.byteLength(body, 'utf8'), because the header is measured in bytes. Using body.length sends a value that is too small whenever the body contains non-ASCII text, and the response is truncated.
How many bytes does an emoji take in UTF-8?
Most emoji take 4 bytes in UTF-8 while their JavaScript .length is 2, because they are stored as a surrogate pair. Emoji built from several code points joined with zero-width joiners take considerably more.
More examples
A correct Content-Length header
Using body.length here would understate the size by one byte per non-ASCII character, and the client would either hang waiting for bytes that never come or read a body cut short. The header is defined in bytes, so the measurement must be too.
const http = require('node:http');
http.createServer((req, res) => {
const body = JSON.stringify({ greeting: 'Grüße', from: 'Köln' });
res.writeHead(200, {
'Content-Type': 'application/json; charset=utf-8',
'Content-Length': Buffer.byteLength(body, 'utf8') // 41, not body.length (38)
});
res.end(body);
}).listen(3000);Enforcing a byte limit without cutting a character in half
The check and the truncation both work in bytes, because that is the unit the limit is expressed in. The while loop walks backwards off any UTF-8 continuation byte, so the result is 9 bytes rather than 10 but decodes cleanly instead of ending in a replacement character.
const LIMIT = 10;
function fits(text) {
return Buffer.byteLength(text, 'utf8') <= LIMIT;
}
function truncateToBytes(text, limit) {
const buf = Buffer.from(text, 'utf8');
if (buf.length <= limit) return text;
// Decoding a cut buffer can end in U+FFFD, so trim back to a boundary.
let end = limit;
while (end > 0 && (buf[end] & 0xc0) === 0x80) end--;
return buf.subarray(0, end).toString('utf8');
}
console.log(fits('hello'), fits('Grüße dich')); // true false
console.log(truncateToBytes('Grüße dich', 10)); // Grüße di
console.log(Buffer.byteLength(truncateToBytes('Grüße dich', 10))); // 9Measuring binary values too
Since v5.10.0 any ArrayBuffer-backed value is measured directly, so one helper covers strings and binary alike; note that a Uint16Array of 4 elements reports 8 bytes, not 4. Numbers and other types throw rather than being coerced, a change made in v7.
console.log(Buffer.byteLength(Buffer.alloc(3))); // 3
console.log(Buffer.byteLength(new ArrayBuffer(9))); // 9
console.log(Buffer.byteLength(new Uint16Array(4))); // 8
console.log(Buffer.byteLength(new DataView(new ArrayBuffer(5)))); // 5
// Handy in a helper that accepts either text or bytes:
const size = (v) => Buffer.byteLength(v);
console.log(size('café'), size(Buffer.from('café'))); // 5 5
try { Buffer.byteLength(42); } catch (err) { console.log(err.code); }
// ERR_INVALID_ARG_TYPE
Discussion