startsWith()
ES6+Determines whether a string begins with the characters of a given search string, returning true or false. An optional position argument treats the check as if the string started at that index, letting you test for a prefix at any known offset. The comparison is case-sensitive and literal, added in ES2015.
Syntax
string.startsWith(searchString, position)Parameters
searchString string The characters to search for at the start
position number optionalPosition to begin searching
Return Value
true if the string starts with the given characters
Examples
const str = 'Hello World';
console.log(str.startsWith('Hello'));
console.log(str.startsWith('World')); 📌 When to Use
Use startsWith() whenever position matters and that position is the beginning: checking URL protocols before rewriting them, routing requests by path prefix, classifying MIME types by their major type, detecting command prefixes in chat bots, filtering environment variables by namespace, or deciding whether a phone number carries a country code. It states the anchored intent that includes() cannot: path.includes('/api/') is true for '/blog/api-tips/', while path.startsWith('/api/') is true only for genuine API paths. It also replaces two older, buggier idioms: indexOf(prefix) === 0, which scans the whole string on a miss instead of failing fast, and slice(0, prefix.length) === prefix, which is correct but noisy. The optional position parameter checks for a prefix at an arbitrary offset, handy when parsing a known-format string segment by segment without slicing. The comparison is case-sensitive and works in UTF-16 code units, so normalize case first when checking things like MIME types, which are case-insensitive by specification. For prefix patterns rather than literal prefixes, such as 'two or three uppercase letters then a digit', use an anchored regular expression instead; startsWith() throws a TypeError if handed a regex, precisely to keep those two worlds distinct.
⚠️ Common Mistakes
Using includes() where the prefix position is the point: substring presence anywhere is a much weaker condition than presence at the start, and route guards or protocol checks built on includes() accept malicious or malformed values that merely contain the expected text somewhere.
Ignoring case sensitivity: 'HTTP://EXAMPLE.COM'.startsWith('http://') is false. Scheme names, MIME types, and header names are case-insensitive by their specifications, so lowercase the subject before the check or you will reject valid input.
Passing a regular expression: startsWith() throws a TypeError for regex arguments rather than converting them. Anchor a pattern with the caret in a regex and use test() when you need pattern-shaped prefixes.
Misunderstanding the second parameter: it is the position at which the pretend start of the string lies, not a length limit. str.startsWith('foo', 4) asks whether 'foo' appears at index 4, which is useful but entirely different from limiting how much of the string is examined.
Trusting prefix checks for security decisions: url.startsWith('https://trusted.com') passes for 'https://trusted.com.evil.io'. Prefix matching on raw URLs is not origin validation; parse the URL and compare the hostname exactly.
Forgetting that every string starts with the empty string: startsWith('') is always true, so a prefix taken from empty user input silently matches everything.
✅ Best Practices
Choose the anchored method on purpose: startsWith() for prefixes, endsWith() for suffixes, includes() only for genuine anywhere-matches, so each check documents where the text must appear.
Pair startsWith() with slice(prefix.length) as the standard strip-a-prefix idiom: test first, then cut exactly the length of the prefix you just verified.
Normalize case explicitly before checking case-insensitive vocabularies such as schemes and MIME types, keeping the normalization adjacent to the check so the pairing is visible.
When routing on several prefixes, order the tests from most specific to least specific, as with '/api/v2/' before '/api/', or the general case will shadow the specific one.
Use the position parameter for cursor-style parsing of structured strings instead of allocating a slice for every segment test.
For URL trust decisions, parse with the URL constructor and compare origin or hostname; keep startsWith() for format checks, not authorization.
⚡ Performance Notes
startsWith() is one of the cheapest string operations: it compares at most prefix-length code units at a fixed offset and stops at the first mismatch, so its cost is bounded by the prefix, not the subject string. That makes it strictly better than the old indexOf(prefix) === 0 idiom, which on a miss keeps scanning the entire string for a match that can only count if it lands at zero. It allocates nothing, unlike the slice-and-compare idiom, which builds a throwaway substring per check, and it avoids all regex machinery, so in a hot router matching thousands of paths it is the fastest correct primitive available. Chains of several startsWith() tests are fine; if a service genuinely routes among dozens of prefixes on a hot path, a first-character switch or a trie eventually wins, but that threshold is far beyond typical application code.
🌍 Real World Example
URL and Protocol Validation
Three anchored-prefix patterns. ensureHttps() upgrades or adds a protocol: an https URL passes through, an http URL is rewritten by slicing off exactly the prefix that startsWith() just confirmed, and a bare domain gets the scheme prepended, the verify-then-slice idiom in its natural habitat. routeRequest() dispatches paths by testing the most specific prefix first, so '/api/v2/' wins over '/api/', and each branch strips the matched prefix for the downstream handler. isImageMimeType() lowercases the MIME type before checking the 'image/' prefix, respecting the case-insensitivity of MIME types while keeping the check itself a single fast comparison.
// Validate and normalize URL protocol
function ensureHttps(url) {
if (url.startsWith('https://')) {
return url;
}
if (url.startsWith('http://')) {
return 'https://' + url.slice(7);
}
return 'https://' + url;
}
console.log(ensureHttps('http://example.com')); // 'https://example.com'
console.log(ensureHttps('example.com')); // 'https://example.com'
// Route handler based on path prefix
function routeRequest(path) {
if (path.startsWith('/api/v2/')) {
return { handler: 'apiV2', path: path.slice(8) };
}
if (path.startsWith('/api/')) {
return { handler: 'apiV1', path: path.slice(5) };
}
if (path.startsWith('/static/')) {
return { handler: 'static', path: path.slice(8) };
}
return { handler: 'page', path };
}
console.log(routeRequest('/api/v2/users'));
// { handler: 'apiV2', path: 'users' }
// Check MIME type category
function isImageMimeType(mimeType) {
return mimeType.toLowerCase().startsWith('image/');
}
console.log(isImageMimeType('image/png')); // true
console.log(isImageMimeType('application/json')); // false