String.prototype.substring()
Extracts the characters between two indices and returns them as a new string.
substring(indexStart)
substring(indexStart, indexEnd)- Added in JavaScript ES1
Parameters
| Parameter | Type | Description |
|---|---|---|
$indexStart | number | The index of the first character to include in the returned substring. |
$indexEndoptional | number | The index of the first character to exclude from the returned substring. If omitted or undefined, extraction runs to the end of the string. |
Return value
stringA new string containing the specified part of the given string.
substring() copies the characters from indexStart up to but not including indexEnd. The original string is untouched — strings are immutable, so you always get a new one back.
How it sanitises its arguments
This is the method's defining trait, and the reason people either love it or avoid it. It never lets an index escape the string:
- Any argument below
0becomes0; anything abovestr.lengthbecomesstr.length. NaNis treated as0.- If
indexStartis greater thanindexEnd, the two arguments are swapped, sos.substring(4, 0)ands.substring(0, 4)return the same thing.
substring vs slice
Both take start and end indices, but slice() accepts negative indices as offsets from the end and returns an empty string when start > end. Reach for substring() when the indices come from a source you do not fully trust — the results of indexOf(), a user-supplied offset, arithmetic that might go negative — and you want clamping rather than a surprise. Reach for slice() when you actually want -3 to mean "three from the end".
Example
When to use it
- Pull a fixed-width field out of a flat text record
- Truncate a label to a maximum length before display
- Split a key at a delimiter found with indexOf()
- Read the year out of an ISO date string
Frequently asked questions
What is the difference between substring and slice in JavaScript?
slice() accepts negative indices as offsets from the end of the string and returns an empty string when start is greater than end, while substring() clamps negative values to 0 and swaps the arguments when start is greater than end. Use slice() when you want to count backwards; use substring() when you want out-of-range indices quietly corrected.
Why does substring return characters when start is bigger than end?
Because substring() swaps its two arguments whenever indexStart exceeds indexEnd, by specification. s.substring(4, 0) is evaluated as s.substring(0, 4). If you need a reversed range to yield an empty string, use slice() instead.
Does substring modify the original string?
No. JavaScript strings are immutable, so substring() always returns a brand-new string and leaves the original variable pointing at the untouched value. Even substring(0) returns a copy rather than the same reference in spirit.
More examples
Cutting a key at a delimiter
The output shows the header split into name and value. The last line demonstrates the clamping: a missing delimiter makes indexOf return -1, and substring turns that into 0 rather than throwing or counting from the end.
const entry = "content-type: application/json";
const colon = entry.indexOf(":");
const name = entry.substring(0, colon);
const value = entry.substring(colon + 1).trim();
console.log(name); // "content-type"
console.log(value); // "application/json"
// When there is no delimiter, indexOf returns -1,
// which substring clamps to 0.
console.log(JSON.stringify("novalue".substring(0, -1))); // ""substring vs slice on negative indices
The two methods disagree on every line. slice() honours negative indices and returns an empty string for a reversed range; substring() clamps and swaps. Picking the wrong one is the most common source of off-by-everything string bugs.
const s = "abcdef";
console.log(s.substring(-2)); // "abcdef" — -2 clamped to 0
console.log(s.slice(-2)); // "ef" — counts from the end
console.log(s.substring(4, 1)); // "bcd" — arguments swapped
console.log(s.slice(4, 1)); // "" — empty when start > end
console.log(s.substring(NaN, 3)); // "abc" — NaN treated as 0Truncating with an ellipsis
Short input is returned unchanged; the long headline is cut to 19 characters plus an ellipsis, 20 in total. Because substring clamps, the function is safe even if max is larger than the string.
function truncate(text, max) {
return text.length <= max ? text : text.substring(0, max - 1) + "…";
}
console.log(truncate("Short", 20));
console.log(truncate("A rather long headline about strings", 20));
console.log(truncate("", 20).length);
Discussion