How do I format lists like A, B, and C in JavaScript?
Use Intl.ListFormat to format arrays with locale-specific conjunctions and separators.
Introduction
When you display a list of items to users, you need to join them with commas and a conjunction like "and". Different languages have different conventions for list formatting. English uses commas and "and", Spanish uses "y", French uses "et", and Chinese uses different punctuation entirely.
The Intl.ListFormat API formats arrays into locale-appropriate strings with the correct separators and conjunctions. This handles the cultural differences in list formatting automatically.
The problem with manual list formatting
You can join array elements with commas using the join() method.
const fruits = ["apples", "oranges", "bananas"];
const list = fruits.join(", ");
console.log(list);
// "apples, oranges, bananas"
This approach has two problems. First, it does not add a conjunction before the last item. Second, it uses English punctuation that does not work for other languages.
You could manually add "and" before the last item.
const fruits = ["apples", "oranges", "bananas"];
const lastFruit = fruits[fruits.length - 1];
const otherFruits = fruits.slice(0, -1);
const list = otherFruits.join(", ") + ", and " + lastFruit;
console.log(list);
// "apples, oranges, and bananas"
This code only works for English. Spanish users would see "apples, oranges, and bananas" instead of "apples, oranges y bananas". French users would see "and" instead of "et". The punctuation and conjunction rules vary by language.
Using Intl.ListFormat to format lists
The Intl.ListFormat constructor creates a formatter that converts arrays into locale-appropriate list strings.
const formatter = new Intl.ListFormat("en");
const fruits = ["apples", "oranges", "bananas"];
console.log(formatter.format(fruits));
// "apples, oranges, and bananas"
The formatter uses the correct separators and conjunction for the specified locale. You pass the locale as the first argument to the constructor.
const enFormatter = new Intl.ListFormat("en");
const esFormatter = new Intl.ListFormat("es");
const frFormatter = new Intl.ListFormat("fr");
const fruits = ["apples", "oranges", "bananas"];
console.log(enFormatter.format(fruits));
// "apples, oranges, and bananas"
console.log(esFormatter.format(fruits));
// "apples, oranges y bananas"
console.log(frFormatter.format(fruits));
// "apples, oranges et bananas"
The formatter automatically applies the punctuation and conjunction rules for each locale.
Formatting lists with "and"
The default behavior of Intl.ListFormat uses the conjunction "and" or its equivalent in other languages. This is called conjunction formatting.
const formatter = new Intl.ListFormat("en", { type: "conjunction" });
const items = ["bread", "milk", "eggs"];
console.log(formatter.format(items));
// "bread, milk, and eggs"
The type option controls what connector appears between items. The value "conjunction" produces lists that use "and". This is the default value, so you can omit it.
Understanding list type options
The type option accepts three values that control how items connect.
The "conjunction" type uses "and" or its equivalent.
const formatter = new Intl.ListFormat("en", { type: "conjunction" });
console.log(formatter.format(["red", "green", "blue"]));
// "red, green, and blue"
The "disjunction" type uses "or" or its equivalent.
const formatter = new Intl.ListFormat("en", { type: "disjunction" });
console.log(formatter.format(["red", "green", "blue"]));
// "red, green, or blue"
The "unit" type formats lists of measurements or quantities without a conjunction.
const formatter = new Intl.ListFormat("en", { type: "unit" });
console.log(formatter.format(["5 pounds", "12 ounces"]));
// "5 pounds, 12 ounces"
The unit type uses minimal punctuation appropriate for technical or measurement data.
Understanding style options
The style option controls the length and formality of the formatted output. It accepts three values.
The "long" style uses full words and standard punctuation. This is the default.
const formatter = new Intl.ListFormat("en", { style: "long" });
console.log(formatter.format(["Alice", "Bob", "Carol"]));
// "Alice, Bob, and Carol"
The "short" style uses abbreviated forms when available.
const formatter = new Intl.ListFormat("en", { style: "short" });
console.log(formatter.format(["Alice", "Bob", "Carol"]));
// "Alice, Bob, & Carol"
The "narrow" style uses the most compact form possible.
const formatter = new Intl.ListFormat("en", { style: "narrow" });
console.log(formatter.format(["Alice", "Bob", "Carol"]));
// "Alice, Bob, Carol"
The narrow style often omits the conjunction entirely. The exact output depends on the locale.
How different locales format lists
Each locale has its own rules for list formatting. The formatter applies these rules automatically.
English uses commas and includes the Oxford comma before "and".
const formatter = new Intl.ListFormat("en");
console.log(formatter.format(["coffee", "tea", "juice"]));
// "coffee, tea, and juice"
Spanish uses commas and the conjunction "y".
const formatter = new Intl.ListFormat("es");
console.log(formatter.format(["café", "té", "jugo"]));
// "café, té y jugo"
French uses commas and the conjunction "et".
const formatter = new Intl.ListFormat("fr");
console.log(formatter.format(["café", "thé", "jus"]));
// "café, thé et jus"
Chinese uses the character 和 for "and" and the enumeration comma 、 as a separator.
const formatter = new Intl.ListFormat("zh");
console.log(formatter.format(["咖啡", "茶", "可乐"]));
// "咖啡、茶和可乐"
German uses commas and the conjunction "und".
const formatter = new Intl.ListFormat("de");
console.log(formatter.format(["Kaffee", "Tee", "Saft"]));
// "Kaffee, Tee und Saft"
The formatter handles these differences without requiring you to know the rules for each language.
Getting individual parts with formatToParts
The formatToParts() method returns an array of objects representing each part of the formatted list. This is useful when you need to style different parts separately.
const formatter = new Intl.ListFormat("en");
const parts = formatter.formatToParts(["red", "green", "blue"]);
console.log(parts);
The output is an array of objects with type and value properties.
[
{ type: "element", value: "red" },
{ type: "literal", value: ", " },
{ type: "element", value: "green" },
{ type: "literal", value: ", and " },
{ type: "element", value: "blue" }
]
Each list item has type "element". The separators and conjunctions have type "literal". You can use this to apply custom styling.
const formatter = new Intl.ListFormat("en");
const parts = formatter.formatToParts(["red", "green", "blue"]);
const html = parts
.map((part) => {
if (part.type === "element") {
return `<strong>${part.value}</strong>`;
}
return part.value;
})
.join("");
console.log(html);
// "<strong>red</strong>, <strong>green</strong>, and <strong>blue</strong>"
This approach gives you precise control over the formatting while maintaining locale-appropriate separators and conjunctions.
Browser support
The Intl.ListFormat API is available in all modern browsers. It has been supported since April 2021 across major browsers including Chrome, Firefox, Safari, and Edge.
You can check if the API is available before using it.
if (typeof Intl.ListFormat !== "undefined") {
const formatter = new Intl.ListFormat("en");
console.log(formatter.format(["a", "b", "c"]));
} else {
console.log("Intl.ListFormat is not supported");
}
For older browsers, you need to provide a fallback or use a polyfill. The fallback can use the simple join() method.
function formatList(items, locale) {
if (typeof Intl.ListFormat !== "undefined") {
const formatter = new Intl.ListFormat(locale);
return formatter.format(items);
}
return items.join(", ");
}
console.log(formatList(["red", "green", "blue"], "en"));
// "red, green, and blue" (or "red, green, blue" in older browsers)
This ensures your code works even in browsers without Intl.ListFormat support.