Almost everything a program shows a person is text. A welcome message, a product name, an error notice, a price label, the URL in the address bar — all of it is text, and in JavaScript text lives in a type called the string. You already met strings in passing when we covered variables and data types. Now we’ll dig in properly, because once you can confidently create, join, search, and reshape text, a huge slice of everyday JavaScript stops feeling mysterious.
The good news: strings come loaded with built-in tools, and you don’t need to memorize all of them. You need a solid handful, plus a feel for how they behave. That’s exactly what this article gives you.
What a string is
A string is a sequence of characters — letters, digits, spaces, punctuation, emoji, anything. You create one by wrapping text in quotes. JavaScript accepts three kinds of quote marks, and the first two are interchangeable:
const single = 'Hello';
const double = "Hello";
const backtick = `Hello`;
Single and double quotes do the exact same thing. People usually pick one style and stick with it across a project — many teams lean toward single quotes, but it genuinely doesn’t matter as long as you’re consistent. The backtick (`) is special, and we’ll get to its superpowers shortly.
Whichever quote you open with, you have to close with the same one. The handy part of having two interchangeable options is that you can put one kind inside the other without any fuss:
const sentence = "It's a sunny day";
const quote = 'She said "hello" to me';
Here the double quotes wrap a string that contains an apostrophe, and the single quotes wrap a string that contains literal double quotes. No special tricks needed.
Escaping special characters
But what if your text contains the very same quote you used to wrap it? That’s where the backslash (\) comes in. It’s called an escape character, and it tells JavaScript “treat the next character as plain text, not as code”:
const tricky = 'It\'s a beautiful day';
const path = "C:\\Users\\Documents";
The \' is a literal apostrophe that won’t end the string early, and \\ produces a single literal backslash. A couple of other escape sequences show up often: \n inserts a new line, and \t inserts a tab.
console.log("Line one\nLine two");
// Line one
// Line two
Pick the quote that needs the least escaping
A small habit that keeps code clean: choose the quote style that lets you avoid backslashes. If your text has apostrophes, wrap it in double quotes ("It's fine"). If it has double quotes, wrap it in single quotes ('She said "hi"'). Reach for escaping only when the text genuinely contains both. Less escaping means fewer typos and far more readable strings.
String length and individual characters
Every string carries a .length property that tells you how many characters it contains:
const name = "JavaScript";
console.log(name.length); // 10
To reach one specific character, you can index into the string almost like an array — and like arrays, string positions start at 0:
const word = "Hello";
console.log(word[0]); // "H" (first character)
console.log(word[4]); // "o" (fifth character)
So the last character always sits at length - 1. If you’ve worked through arrays and objects, this zero-based counting will already feel familiar — it’s the same rule.
Strings can't be changed in place
Strings are immutable — once created, you can’t alter a character inside one. Writing word[0] = "J" does nothing; it won’t throw an error, it just silently fails, and word stays exactly as it was. Every method you’re about to meet follows from this: none of them edit the original string. They each return a brand-new string and leave the original untouched. So text.toUpperCase() on its own changes nothing — you have to capture the result: const loud = text.toUpperCase(). Forgetting to store the return value is one of the most common string bugs beginners hit.
Joining strings together
Sticking strings together is called concatenation. The simplest way is the + operator, the same plus sign you use for math — except with strings, it glues them end to end:
const firstName = "John";
const lastName = "Doe";
const fullName = firstName + " " + lastName;
console.log(fullName); // "John Doe"
Notice the " " in the middle — that’s a string containing a single space. Without it you’d get "JohnDoe" jammed together, because + adds exactly what you give it and nothing more. This kind of manual joining works, but once you’re stitching together more than two or three pieces, it gets noisy and easy to mess up. There’s a much nicer way.
Template literals: the modern way to build strings
This is where the backtick earns its keep. A string written with backticks is called a template literal, and it lets you drop variables straight into the text using ${...}:
const firstName = "John";
const lastName = "Doe";
const greeting = `Hello, ${firstName} ${lastName}!`;
console.log(greeting); // "Hello, John Doe!"
Compare that to the + version above — it reads far more like the sentence you’re actually trying to build. Anything inside ${...} gets evaluated and dropped into place, so you can put full expressions in there, not just variable names:
const price = 50000;
const qty = 3;
console.log(`Total: ${price * qty} rupiah`); // "Total: 150000 rupiah"
Template literals have a second gift: they can span multiple lines exactly as written, no \n needed:
const message = `Dear customer,
Thank you for your order.
ACY Partner Indonesia`;
What you see is what you get — the line breaks in the source become line breaks in the string.
Default to template literals
Once you’re comfortable, reach for template literals (backticks) as your everyday string tool, especially anytime you’re mixing text with variables. They’re easier to read, easier to change, and they sidestep the missing-space mistakes that plague + concatenation. Plain quotes are still perfectly fine for simple fixed text like "Save" or "Cancel" — but the moment a variable enters the picture, backticks usually win.
Searching inside a string
Often you need to know whether a string contains something, or where. JavaScript gives you a few readable methods for this.
includes() answers a yes/no question — does this text appear anywhere? — and returns true or false:
const email = "john.doe@example.com";
console.log(email.includes("@")); // true
console.log(email.includes("gmail")); // false
startsWith() and endsWith() check the two ends specifically:
const file = "report.pdf";
console.log(file.endsWith(".pdf")); // true
console.log(file.startsWith("report")); // true
When you need the exact position of something, indexOf() returns the index where it first appears — or -1 if it’s not found at all:
const sentence = "the quick brown fox";
console.log(sentence.indexOf("quick")); // 4
console.log(sentence.indexOf("cat")); // -1
indexOf returns -1, not false, when nothing matches
A classic trap: indexOf() reports “not found” as -1, not as false. Since -1 is a truthy value, a check like if (text.indexOf("x")) { ... } runs even when “x” is absent — and breaks when “x” sits at index 0, because 0 is falsy. So the older, correct pattern is if (text.indexOf("x") !== -1). The honest advice for modern code: when you only care whether something appears, use includes() instead — it returns a clean true/false and removes the whole footgun. Save indexOf() for when you actually need the position.
Extracting part of a string
To pull a piece out of a larger string, slice() is the method to know. You give it a start index and (optionally) an end index, and it returns the characters in between — including the start, excluding the end:
const text = "JavaScript";
console.log(text.slice(0, 4)); // "Java" (indexes 0,1,2,3)
console.log(text.slice(4)); // "Script" (from index 4 to the end)
When you leave off the second number, slice() runs to the end of the string. It also accepts negative indexes, which count from the back — handy for grabbing the tail end of something:
const file = "vacation-photo.jpg";
console.log(file.slice(-3)); // "jpg" (last 3 characters)
Remember that none of this touches the original text or file — slice() hands you a new string and leaves the source alone.
Transforming strings
A grab-bag of methods reshape text, and these come up constantly in real work. Each one returns a new string:
const text = " Hello World ";
console.log(text.toUpperCase()); // " HELLO WORLD "
console.log(text.toLowerCase()); // " hello world "
console.log(text.trim()); // "Hello World" (outer spaces removed)
trim() deserves a special mention — it strips whitespace off both ends of a string, which is exactly what you want for anything a user typed into a form. People accidentally add stray spaces all the time, and trim() quietly cleans them up.
replace() swaps one piece of text for another:
const greeting = "Hello John";
console.log(greeting.replace("John", "Jane")); // "Hello Jane"
One thing worth knowing: with a plain string as the target, replace() only changes the first match. If you need to replace every occurrence, replaceAll() is the clear, modern choice:
const path = "a/b/c/d";
console.log(path.replaceAll("/", "-")); // "a-b-c-d"
Splitting and joining
split() chops a string into an array, breaking it apart wherever it finds the separator you name. It pairs beautifully with the arrays you already know:
const csv = "apple,banana,cherry";
const fruits = csv.split(",");
console.log(fruits); // ["apple", "banana", "cherry"]
console.log(fruits.length); // 3
This is the everyday way to turn a comma-separated line, a sentence of words, or a path into something you can loop over. And it has a perfect counterpart on the array side — join() — which glues an array back into a single string:
const parts = ["2026", "07", "29"];
console.log(parts.join("-")); // "2026-07-29"
split() and join() are two halves of the same idea: one takes text apart on a delimiter, the other puts it back together with a delimiter of your choosing.
A realistic example
Let’s tie the pieces together into something you might genuinely write — formatting a username and building a friendly greeting from raw, messy input:
const rawInput = " John.Doe@Example.COM ";
// Clean it up
const email = rawInput.trim().toLowerCase();
console.log(email); // "john.doe@example.com"
// Pull the username out (everything before the @)
const username = email.slice(0, email.indexOf("@"));
console.log(username); // "john.doe"
// Turn "john.doe" into "John Doe" for display
const displayName = username
.split(".")
.map(part => part[0].toUpperCase() + part.slice(1))
.join(" ");
console.log(`Welcome back, ${displayName}!`); // "Welcome back, John Doe!"
Look at how the methods chain together: trim() then toLowerCase() clean the input, slice() and indexOf() carve out the username, and split() / map() / join() rebuild it into a nicely capitalized name. Each step takes a string and returns a new one, so they flow neatly from one into the next. This is real string work — nothing exotic, just the methods from this article doing their jobs in sequence.
Wrapping up
You can now create and reshape text with confidence:
- A string is a sequence of characters, written with
'single',"double", or`backtick`quotes. Single and double are interchangeable; pick the one that needs the least escaping. - Use
\to escape special characters (\',\\,\n,\t) when needed. .lengthgives the character count, and you can index characters with[ ], starting at 0. Strings are immutable — methods return a new string and never change the original, so always capture the result.- Template literals (backticks with
${...}) are the modern way to build strings with variables, and they support multi-line text. Default to them whenever variables are involved. - Search with
includes(),startsWith(),endsWith(), andindexOf()(which returns-1when nothing matches). - Extract with
slice(), and transform withtoUpperCase(),toLowerCase(),trim(),replace()/replaceAll(), andsplit()(which pairs with arrayjoin()).
Text is everywhere in programming, so these methods will follow you through everything you build from here. Next up, we’ll turn from text to numbers and math — how JavaScript handles arithmetic, rounding, random values, and the quirks worth knowing before they surprise you.