The idea
So much of a web app is text: product names, prices with thousands separators, messages built from data, search terms typed with stray spaces and capital letters. JavaScript strings come with a toolbox of methods for exactly these jobs, and template literals make building text from data readable.
Template literals
The cart page needs a line like "Total for 2 × Arabica Gayo: Rp 190.000", built from a quantity, a name and a calculated total. The old way glues it together with +, which gets messy fast. Compare it with a template literal, which lets you write the sentence and drop the values straight into it:
const name = "Arabica Gayo";
const price = 95000;
const qty = 2;
console.log("Total for " + qty + " × " + name + ": Rp " + (price * qty).toLocaleString("id-ID"));
console.log(`Total for ${qty} × ${name}: Rp ${(price * qty).toLocaleString("id-ID")}`);
console.log(`${qty > 1 ? "items" : "item"} in your cart`);
Total for 2 × Arabica Gayo: Rp 190.000
Total for 2 × Arabica Gayo: Rp 190.000
items in your cart
- Template literals use backticks (
`), not quotes. ${...}inserts the result of any expression: a variable, a calculation, a function call, a ternary.- They can span several lines, which makes small HTML snippets readable (as in the DOM modules).
Same output, but the template literal reads like the sentence it produces. Use them whenever you combine text with values. (When inserting user data into HTML, remember module 5: build elements with textContent instead.)
Cleaning and comparing
When someone types " Arabica GAYO " into the search box, they mean the same thing as "arabica gayo". But to JavaScript those are completely different strings, and a search that compares them directly finds nothing. Cleaning the input before you compare fixes most of these problems:
const typed = " Arabica GAYO ";
console.log(`[${typed.trim()}]`);
console.log(typed.trim().toLowerCase());
console.log(typed.toLowerCase().includes("gayo"));
console.log("NG-0018".startsWith("NG-"), "photo.JPG".toLowerCase().endsWith(".jpg"));
console.log("kopi" === "Kopi", "kopi" === "Kopi".toLowerCase());
[Arabica GAYO]
arabica gayo
true
true true
false true
Strings are compared exactly, character by character, so normalise user input first: trim() removes surrounding spaces, toLowerCase() makes comparisons case-insensitive. These two calls prevent most "the search finds nothing" bugs.
Strings are immutable: every method returns a new string and leaves the original unchanged. typed.trim() doesn't change typed; you have to use or store the result.
Taking strings apart
A product code like NG-0018-BLK packs several facts into one string: the brand, the number and the colour. And a tag list typed by a person, like "arabica, aceh , single origin", needs to become separate tags. These methods cut strings into pieces and join pieces back together:
const sku = "NG-0018-BLK";
console.log(sku.length, sku[0], sku.at(-1));
console.log(sku.slice(3, 7), sku.slice(-3));
console.log(sku.indexOf("-"), sku.split("-"));
const tags = "arabica, aceh , single origin";
console.log(tags.split(",").map((t) => t.trim()));
console.log(["Gayo", "Toraja", "Kintamani"].join(" · "));
11 N K
0018 BLK
2 ["NG", "0018", "BLK"]
["arabica", "aceh", "single origin"]
Gayo · Toraja · Kintamani
slice(start, end)takes a piece (end not included); negative numbers count from the end.split(separator)turns a string into an array;join(separator)turns an array back into a string.
Replacing and padding
Prices often arrive as text like "Rp 1.250.000", which you can't do maths with until the "Rp" and the dots are gone. And order numbers often need a fixed width, like 0007 instead of 7. These methods handle both jobs:
console.log("Rp 1.250.000".replaceAll(".", "").replace("Rp ", ""));
console.log(Number("Rp 1.250.000".replace(/[^\d]/g, "")));
console.log(String(7).padStart(4, "0"), "Total".padEnd(10, ".") + "Rp 95.000");
console.log("ha".repeat(3));
1250000
1250000
0007 Total.....Rp 95.000
hahaha
replacechanges the first match;replaceAllchanges every match.- Both accept a regular expression:
/[^\d]/gmeans "any character that isn't a digit, everywhere" (thegflag). Removing non-digits is the reliable way to parse Indonesian prices, where.is the thousands separator. padStart/padEndpad to a length: handy for order numbers and aligned text.
Formatting numbers for people
The opposite problem: you have a number like 1250000.5 and need to show it to an Indonesian customer as "1.250.000,5" or "Rp 95.000". JavaScript has built-in formatting that knows each country's conventions, so you don't have to add the dots yourself:
const amount = 1250000.5;
console.log(amount.toLocaleString("id-ID"));
console.log(amount.toLocaleString("en-US"));
console.log(Math.round(amount).toLocaleString("id-ID"));
console.log((0.1234).toFixed(2), (12.5).toFixed(0));
const rupiah = new Intl.NumberFormat("id-ID", { style: "currency", currency: "IDR", maximumFractionDigits: 0 });
console.log(rupiah.format(95000).replace(/\s/, " "));
1.250.000,5
1,250,000.5
1.250.001
0.12 13
Rp 95.000
toLocaleString(locale)formats with the right separators: Indonesia uses.for thousands and,for decimals.toFixed(n)gives a string with exactly n decimals (for display only).Intl.NumberFormathandles currencies properly. Create the formatter once and reuse it. (It puts a non-breaking space after "Rp", replaced here with a normal space to keep the output plain.)
Build text with template literals ( `${value}` ); normalise input with trim() and toLowerCase() before comparing; use includes, startsWith, slice, split/join and replaceAll to inspect and reshape; format numbers with toLocaleString("id-ID") or Intl.NumberFormat.
Why this matters for clean code
Put formatting in one place: a formatRupiah(amount) helper used everywhere, rather than "Rp " + x.toLocaleString(...) scattered across the codebase. When the format changes (adding decimals, a different locale for an English page), you change one function. The same goes for normalising search terms: one normalise(text) function, used by every comparison.
Recap
- Template literals: backticks and
${expression}; multi-line allowed. trim,toLowerCase/toUpperCase,includes,startsWith,endsWith; strings are immutable.length,at(-1),slice,indexOf,split,join.replace(first) vsreplaceAll; regex/[^\d]/gto keep digits;padStart/padEnd,repeat.toLocaleString("id-ID"),toFixed(n)(returns a string),Intl.NumberFormatfor currency.