Strings are the most frequently used data type in JavaScript — every user input, URL, API response, and UI label is a string. JavaScript provides a comprehensive set of string methods for searching, transforming, slicing, and formatting text. In this lesson you will master the most important string methods, template literals, and the techniques for working with strings in real-world applications like data validation, URL construction, and text processing.
String Creation and Properties
| Syntax | Name | Notes |
|---|---|---|
'single quotes' |
Single-quoted string | Standard — no interpolation |
"double quotes" |
Double-quoted string | Same as single quotes — pick one and be consistent |
`backtick` |
Template literal | Supports ${expression} interpolation and multiline |
str.length |
Length property | Number of UTF-16 code units — not a method, no () |
str[0] |
Index access | Returns character at index — strings are zero-indexed |
str.at(-1) |
Negative index (ES2022) | Last character — cleaner than str[str.length-1] |
Essential String Methods
| Method | What It Does | Example |
|---|---|---|
toUpperCase() / toLowerCase() |
Case conversion | 'Hello'.toLowerCase() → 'hello' |
trim() / trimStart() / trimEnd() |
Remove whitespace | ' hi '.trim() → 'hi' |
includes(str) |
Check if substring exists | 'hello'.includes('ell') → true |
startsWith(str) / endsWith(str) |
Check prefix / suffix | 'file.js'.endsWith('.js') → true |
indexOf(str) |
First occurrence index or -1 | 'hello'.indexOf('l') → 2 |
slice(start, end) |
Extract substring | 'hello'.slice(1, 3) → 'el' |
replace(old, new) |
Replace first match | 'aabbcc'.replace('b', 'x') → 'axbcc' |
replaceAll(old, new) |
Replace all matches | 'aabbcc'.replaceAll('b', 'x') → 'aaxxcc' |
split(delimiter) |
Split into array | 'a,b,c'.split(',') → ['a','b','c'] |
padStart(n, char) / padEnd(n, char) |
Pad to length | '5'.padStart(3, '0') → '005' |
repeat(n) |
Repeat string n times | 'ab'.repeat(3) → 'ababab' |
Template Literals
| Feature | Syntax | Example |
|---|---|---|
| Variable interpolation | `Hello ${name}` |
`Total: $${price.toFixed(2)}` |
| Expression evaluation | `${2 + 2}` |
`${isAdmin ? 'Admin' : 'User'}` |
| Multiline strings | Newlines inside backticks | No needed — just press Enter |
| Nested templates | `${`inner ${val}`}` |
Fully nestable — use sparingly |
str.toUpperCase() does not change str in place; it returns a new uppercase string. You must capture the result: const upper = str.toUpperCase(). Forgetting this is one of the most common beginner mistakes.email.toLowerCase().includes('@gmail.com') handles 'USER@GMAIL.COM' and 'user@gmail.com' identically — a vital step for user-facing string comparisons. Always lowercase (or uppercase) both sides before comparing.String.replace(str, replacement) only replaces the first occurrence. To replace all occurrences, either use replaceAll(str, replacement) (ES2021+) or use a regex with the global flag: str.replace(/pattern/g, replacement). Forgetting this causes partial replacements that are hard to debug.Basic Example
// ── Basic string operations ───────────────────────────────────────────────
const raw = ' Hello, World! ';
const clean = raw.trim(); // 'Hello, World!'
const lower = clean.toLowerCase(); // 'hello, world!'
const upper = clean.toUpperCase(); // 'HELLO, WORLD!'
console.log(clean.length); // 13
console.log(clean[0]); // 'H'
console.log(clean.at(-1)); // '!' (last char)
// ── Searching ────────────────────────────────────────────────────────────
const sentence = 'The quick brown fox jumps over the lazy dog';
console.log(sentence.includes('fox')); // true
console.log(sentence.startsWith('The')); // true
console.log(sentence.endsWith('dog')); // true
console.log(sentence.indexOf('fox')); // 16
console.log(sentence.indexOf('cat')); // -1
// ── Slicing ──────────────────────────────────────────────────────────────
const filename = 'report-2025-final.pdf';
const ext = filename.slice(filename.lastIndexOf('.')); // '.pdf'
const base = filename.slice(0, filename.lastIndexOf('.')); // 'report-2025-final'
console.log(ext, base);
// ── Replace ──────────────────────────────────────────────────────────────
const template = 'Hello, {name}! You have {count} messages.';
const filled = template
.replace('{name}', 'Alice')
.replace('{count}', '5');
console.log(filled); // 'Hello, Alice! You have 5 messages.'
// ── Split and join ───────────────────────────────────────────────────────
const csv = 'apple,banana,cherry,date';
const fruits = csv.split(','); // ['apple', 'banana', 'cherry', 'date']
const rejoined = fruits.join(' | '); // 'apple | banana | cherry | date'
console.log(fruits, rejoined);
// ── Template literals ────────────────────────────────────────────────────
const user = { name: 'Bob', age: 32, plan: 'Pro' };
const card = `
Name: ${user.name}
Age: ${user.age}
Plan: ${user.plan.toUpperCase()}
Senior: ${user.age >= 65 ? 'Yes' : 'No'}
`.trim();
console.log(card);
// ── Padding for formatting ────────────────────────────────────────────────
const items = [
{ name: 'Widget', qty: 3, price: 9.99 },
{ name: 'Gadget', qty: 12, price: 49.00 },
{ name: 'Doohickey', qty: 1, price: 129.50 },
];
items.forEach(({ name, qty, price }) => {
const total = (qty * price).toFixed(2);
console.log(
name.padEnd(12) +
String(qty).padStart(5) +
('$' + total).padStart(12)
);
});
// Widget 3 $29.97
// Gadget 12 $588.00
// Doohickey 1 $129.50
How It Works
Step 1 — Strings Are Zero-Indexed Like Arrays
str[0] is the first character, str[str.length - 1] is the last. str.at(-1) is a modern shorthand for the last character — negative indices count from the end. Strings are immutable so str[0] = 'X' silently does nothing (no error, but no change either).
Step 2 — slice() Works with Negative Indices
str.slice(start, end) extracts from start up to (but not including) end. Both can be negative — str.slice(-4) extracts the last 4 characters. str.slice(0, -1) returns everything except the last character. This is more flexible than substring().
Step 3 — split() and join() Are Complementary
'a,b,c'.split(',') converts a delimited string into an array. ['a','b','c'].join(' | ') converts an array back to a string with a new delimiter. These two methods are the core of CSV parsing, tag processing, and URL construction.
Step 4 — Template Literals Evaluate Any Expression
Inside ${ } you can put any valid JavaScript expression — not just variable names. Method calls, arithmetic, ternaries, function calls — all are evaluated and converted to strings. This makes template literals far more powerful than simple variable substitution.
Step 5 — String Methods Are Chainable
Because every string method returns a new string, you can chain them: str.trim().toLowerCase().replace(' ', '-'). Each method receives the output of the previous one. This produces clean, readable transformation pipelines.
Real-World Example: URL Slug Generator
// slug.js — convert article titles to URL-friendly slugs
function slugify(title) {
return title
.toLowerCase() // 'My Article Title!'
.trim() // remove leading/trailing spaces
.replace(/[^\w\s-]/g, '') // remove special chars (keep words, spaces, hyphens)
.replace(/\s+/g, '-') // replace spaces with hyphens
.replace(/-+/g, '-') // collapse multiple hyphens
.replace(/^-|-$/g, ''); // remove leading/trailing hyphens
}
function buildPostUrl(baseUrl, category, title, id) {
const slug = slugify(title);
return `${baseUrl}/${category}/${slug}-${id}`;
}
function truncate(str, maxLength, suffix = '...') {
if (str.length <= maxLength) return str;
return str.slice(0, maxLength - suffix.length).trimEnd() + suffix;
}
function formatTag(tag) {
return tag.trim().toLowerCase().replace(/\s+/g, '-');
}
// Tests
const titles = [
'My First JavaScript Tutorial!',
'ES2024 -- What's New in JavaScript?',
'10 Tips for Writing Clean Code',
' Hello, World! ',
];
console.log('=== Slugs ===');
titles.forEach(t => console.log(slugify(t)));
console.log('
=== Full URLs ===');
const url = buildPostUrl('https://stacklesson.com', 'javascript', titles[0], 42);
console.log(url); // https://stacklesson.com/javascript/my-first-javascript-tutorial-42
console.log('
=== Truncation ===');
const excerpt = 'JavaScript is the programming language of the web. It runs in every browser.';
console.log(truncate(excerpt, 50)); // 'JavaScript is the programming language of the...'
console.log(truncate(excerpt, 100)); // full string — under limit
console.log('
=== Tags ===');
const tags = [' JavaScript ', 'Web Dev', 'ES2024'];
console.log(tags.map(formatTag)); // ['javascript', 'web-dev', 'es2024']
Common Mistakes
Mistake 1 — Forgetting string methods return new strings
❌ Wrong — result of toUpperCase() is discarded:
let name = 'alice';
name.toUpperCase(); // Does nothing useful — result not saved
console.log(name); // still 'alice'
✅ Correct — capture the returned value:
let name = 'alice';
const upper = name.toUpperCase();
console.log(upper); // 'ALICE'
Mistake 2 — replace() only replaces the first occurrence
❌ Wrong — expecting all spaces to become hyphens:
'hello world foo'.replace(' ', '-'); // 'hello-world foo' — only first!
✅ Correct — use replaceAll() or a global regex:
'hello world foo'.replaceAll(' ', '-'); // 'hello-world-foo'
'hello world foo'.replace(/ /g, '-'); // 'hello-world-foo' (regex global flag)
Mistake 3 — Comparing strings without normalising case
❌ Wrong — ‘admin’ !== ‘Admin’ so this fails for mixed-case input:
if (userRole === 'admin') { /* fails for 'Admin', 'ADMIN' */ }
✅ Correct — normalise before comparing:
if (userRole.toLowerCase() === 'admin') { /* matches any case */ }
Quick Reference
| Method | Returns | Example |
|---|---|---|
trim() |
String — no whitespace | ' hi '.trim() → 'hi' |
includes(sub) |
Boolean | 'hello'.includes('ell') → true |
slice(s, e) |
String — substring | 'hello'.slice(1,-1) → 'ell' |
split(sep) |
Array | 'a,b'.split(',') → ['a','b'] |
replaceAll(a, b) |
String — all replaced | 'aabb'.replaceAll('a','x') → 'xxbb' |
padStart(n, ch) |
String — left-padded | '5'.padStart(3,'0') → '005' |
at(-1) |
Character | Last char — cleaner than [length-1] |