Strings and Template Literals in JavaScript
A practical guide to JavaScript strings — template literals, indexing, the essential methods, immutability, and the modern ways to slice, search, and transform text.
What you'll learn
- ✓The three string syntaxes and when to use each
- ✓Template literals for interpolation and multi-line text
- ✓Indexing, slicing, and length
- ✓The string methods you will use constantly
- ✓Why strings are immutable and what that means in practice
Prerequisites
- •JavaScript data types — see Data Types
A string is a sequence of text characters, and there is almost no program that doesn’t touch one. JavaScript’s string API is small enough to learn in an afternoon and rich enough to cover most everyday text work.
Creating strings
Three quote styles, all producing equivalent string values:
const a = "double quotes";
const b = 'single quotes';
const c = `backticks`;
Single and double quotes are interchangeable — pick one style and stick with it. Backticks (also called template literals) are different and worth learning well.
Escape sequences
If your text contains the same quote you used to wrap it, escape it with a backslash:
const a = "She said \"hello\".";
const b = 'It\'s a trap.';
Or use the other quote style:
const a = 'She said "hello".';
const b = "It's a trap.";
Common escapes:
"line one\nline two" // newline
"col1\tcol2" // tab
"a backslash: \\" // literal backslash
Template literals
Backtick strings unlock two superpowers that double and single quotes lack.
1. Interpolation
The ${...} syntax embeds any JavaScript expression directly inside the string:
const name = "Ada";
const age = 30;
console.log(`${name} is ${age} years old.`);
// 'Ada is 30 years old.'
const a = 7, b = 5;
console.log(`The sum of ${a} and ${b} is ${a + b}.`);
// 'The sum of 7 and 5 is 12.'
This replaces string concatenation. In older code you would have written:
console.log(name + " is " + age + " years old.");
That still works but is harder to read. Use template literals.
2. Multi-line strings
Backtick strings can span multiple lines without escape sequences:
const message = `Hello,
This is a multi-line string.
Whitespace and newlines are preserved exactly.
— The Codeloom Team`;
console.log(message);
For both reasons, modern style uses backticks freely.
Length and indexing
.length gives you the number of characters:
const s = "hello";
console.log(s.length); // 5
Index characters with square brackets, zero-based:
console.log(s[0]); // 'h'
console.log(s[4]); // 'o'
console.log(s[s.length - 1]); // 'o' — last character
console.log(s[99]); // undefined
There is no Python-style s[-1] for “last character”. Use s.at(-1) instead, which is the modern equivalent:
console.log(s.at(-1)); // 'o'
console.log(s.at(-2)); // 'l'
Strings are immutable
You cannot change a character inside an existing string:
let s = "hello";
s[0] = "H"; // silently does nothing
console.log(s); // 'hello'
To “change” a string, you build a new one and reassign:
let s = "hello";
s = "H" + s.slice(1);
console.log(s); // 'Hello'
This may feel restrictive coming from languages with mutable strings, but it is the norm — Python, Java, and C# all do the same. Strings are values, not containers.
Slicing with slice
slice(start, end) returns a portion of a string. end is exclusive, and both arguments can be negative to count from the end:
const s = "JavaScript";
console.log(s.slice(0, 4)); // 'Java'
console.log(s.slice(4)); // 'Script'
console.log(s.slice(-6)); // 'Script'
console.log(s.slice(-6, -3)); // 'Scr'
If you’ve used Python, this is the equivalent of s[start:end].
Common string methods
A small set of methods covers most everyday text work.
Searching
const s = "the quick brown fox";
s.includes("quick"); // true
s.startsWith("the"); // true
s.endsWith("fox"); // true
s.indexOf("brown"); // 10
s.indexOf("cat"); // -1 (not found)
Case
"hello".toUpperCase(); // 'HELLO'
"HELLO".toLowerCase(); // 'hello'
Trimming whitespace
" hello ".trim(); // 'hello'
" hello ".trimStart(); // 'hello '
" hello ".trimEnd(); // ' hello'
.trim() is what you want when reading user input — it removes accidental spaces and newlines at both ends.
Replacing
const s = "I like cats. Cats are great.";
s.replace("cats", "dogs"); // 'I like dogs. Cats are great.' — first only
s.replaceAll("cats", "dogs"); // 'I like dogs. Cats are great.'
s.replaceAll(/cats/gi, "dogs"); // 'I like dogs. dogs are great.' — case-insensitive regex
replaceAll requires a regex with the global flag, or a plain string. replace only swaps the first occurrence unless you pass a global regex.
Splitting and joining
const csv = "Alice,Bob,Carol";
const names = csv.split(",");
console.log(names); // ['Alice', 'Bob', 'Carol']
console.log(names.join(" and ")); // 'Alice and Bob and Carol'
"hello".split(""); // ['h','e','l','l','o']
split and join are the workhorses for converting between strings and arrays.
Padding and repeating
"7".padStart(3, "0"); // '007'
"7".padEnd(3, "-"); // '7--'
"abc".repeat(3); // 'abcabcabc'
padStart is great for things like zero-padded numbers, time displays, and tabular output.
Try it yourself. Write a function initials(fullName) that returns the uppercase initials of a name. For example, initials("ada lovelace") should return "AL". Use split, map (we’ll cover it formally with arrays), and join.
function initials(fullName) {
return fullName
.split(" ")
.map((word) => word[0].toUpperCase())
.join("");
}
console.log(initials("ada lovelace")); // 'AL'Comparing strings
Use === for equality:
"hello" === "hello"; // true
"hello" === "Hello"; // false — case matters
The comparison operators <, >, <=, >= compare strings lexicographically, character by character using Unicode code point order:
"apple" < "banana"; // true
"Banana" < "apple"; // true — uppercase letters come before lowercase
For case-insensitive comparison, lowercase both sides first:
"Apple".toLowerCase() === "apple".toLowerCase(); // true
For locale-aware sorting (à before z in French, for instance), use localeCompare:
["zebra", "apple", "élan"].sort((a, b) => a.localeCompare(b));
Converting between strings and other types
We mentioned this in Data Types, but it bears repeating:
String(42); // '42'
String(true); // 'true'
String(null); // 'null'
(123).toString(); // '123'
(123).toString(2); // '1111011' — binary
(255).toString(16); // 'ff' — hex
Number("42"); // 42
Number.parseInt("42px"); // 42
For display, toLocaleString (covered in Numbers and Math) handles locale-aware formatting.
Try it yourself. Write a function slugify(title) that turns a string like " My First Blog Post! " into "my-first-blog-post". Lowercase it, trim, split on spaces, filter out empty pieces, and join with -.
A small worked example
A function that takes a list of guests and returns a polite sentence:
function guestList(names) {
if (names.length === 0) return "Nobody is coming.";
if (names.length === 1) return `${names[0]} is coming.`;
if (names.length === 2) return `${names[0]} and ${names[1]} are coming.`;
const allButLast = names.slice(0, -1).join(", ");
const last = names.at(-1);
return `${allButLast}, and ${last} are coming.`;
}
console.log(guestList([]));
console.log(guestList(["Ada"]));
console.log(guestList(["Ada", "Linus"]));
console.log(guestList(["Ada", "Linus", "Grace", "Edsger"]));
Output:
Nobody is coming.
Ada is coming.
Ada and Linus are coming.
Ada, Linus, Grace, and Edsger are coming.
Every concept in this post — template literals, slice, join, at(-1) — earns its keep here.
Recap
You now know:
- Three quote styles; template literals (backticks) for interpolation and multi-line text
- Strings are immutable — methods return new strings rather than modifying in place
- Index with
[i]; for negative indices uses.at(-1) slicefor substrings,split/joinfor converting to and from arraysincludes,startsWith,endsWith,indexOffor searchingreplace(first) vs.replaceAll- Lexicographic comparison and the
localeCompareescape hatch for sorting
Next steps
Strings are sequences of characters. Next, we look at the most-used general-purpose sequence type in JavaScript: arrays.
→ Next: JavaScript Arrays: Create, Iterate, Transform
Questions or feedback? Email codeloomdevv@gmail.com.