Devpuff
Pricing
Log InStart Free
  1. Home
  2. Blog
  3. How to capitalize the first letter of a string in JavaScript
Learn

How to capitalize the first letter of a string in JavaScript

To capitalize the first letter in JavaScript the one-liner works until that character is an emoji, whose length is 2 so slice(1) cuts it in half.

By Max Arthur
Co-Founder & Content Marketer·August 26, 2026·8 Min read
A string capitalized by charAt and slice, with an emoji example showing the character split across two code units

JavaScript has no capitalize method, so the standard answer is two operations joined:

const capitalize = (s) => s.charAt(0).toUpperCase() + s.slice(1);

capitalize("hello world");   // "Hello world"

Take the first character, uppercase it, and glue the rest back on unchanged. slice(1) starts at index 1, so nothing is lost.

Measured in Node v24.14.1, that handles the ordinary cases correctly, including one people worry about unnecessarily:

capitalize("");        // ""   no error
capitalize("Hello");   // "Hello"
capitalize(" hello");  // " hello"

The empty string is safe because charAt(0) returns "" rather than throwing, unlike s[0] which returns undefined and produces "undefinedhello" when concatenated.

The leading-space case is worth noticing: it capitalises the space, which does nothing, and the h stays lowercase. Trim first if that matters.

Where it breaks

"😀".length;          // 2
[..."😀"].length;     // 1

An emoji is a single character that occupies two UTF-16 code units. charAt(0) returns half of it, and slice(1) starts in the middle of it.

Measured, capitalize("😀abc") returned "😀abc", apparently unharmed. That is luck rather than correctness: uppercasing half a surrogate pair returns it unchanged, and concatenating the two halves back together reassembles the emoji. Change the operation slightly and it breaks.

The robust version iterates by character rather than by code unit:

const capitalize = (s) => {
  const [first, ...rest] = [...s];
  return first ? first.toUpperCase() + rest.join("") : s;
};

Spreading a string iterates it by code point, so first is the whole emoji. Measured, this handles "😀abc" correctly and behaves identically on ordinary text.

Whether you need it depends on your input. User-supplied names, chat messages and anything from the open web can contain emoji, accented characters outside the basic plane, and scripts that do not fit in one code unit. Fixed internal strings cannot.

The regex alternative

"hello world".replace(/^./, (c) => c.toUpperCase());

Measured, this gives "Hello world". It reads well and has the same surrogate-pair caveat, since . matches one code unit by default. Adding the u flag fixes that:

s.replace(/^./u, (c) => c.toUpperCase());

Title case

Capitalising every word is the same function applied per word:

const titleCase = (s) => s.split(" ").map(capitalize).join(" ");

titleCase("the quick brown fox");   // "The Quick Brown Fox"

Measured, that gives exactly the result above. Two limitations to be aware of.

It splits on spaces only, so "mother-in-law" and "o'brien" keep their lowercase letters after the punctuation. A regex on word boundaries handles those:

s.replace(/\b\w/g, (c) => c.toUpperCase());

Real title case has rules about small words. English convention leaves articles and short prepositions lowercase unless they start the title, so "the lord of the rings" should be "The Lord of the Rings", not "The Lord Of The Rings". No general-purpose function knows that, and if you need it, a small list of exception words is the usual approach.

Sometimes you want CSS instead

If the string is being displayed and not stored, CSS does this without touching the data:

.title { text-transform: capitalize; }

That capitalises the first letter of every word. text-transform: uppercase and lowercase are the other two.

The advantage is that the underlying value is unchanged, so search, sorting and copying all still work on the original text. The disadvantage is that it capitalises every word with no way to make exceptions, and it does not apply anywhere outside the rendered page.

Rule of thumb: transform in CSS when it is presentation, in JavaScript when the value itself should change. A name stored as "ada lovelace" and displayed as "Ada Lovelace" is presentation; a name being normalised before saving is data.

The locale cases

Two results worth knowing, both measured.

"ßabc".charAt(0).toUpperCase();   // "SS"

The German sharp s uppercases to two characters. So toUpperCase can change a string's length, which breaks any code assuming the first character maps to exactly one character.

"istanbul".charAt(0).toUpperCase();                // "I"
"istanbul".charAt(0).toLocaleUpperCase("tr-TR");   // "İ"

In Turkish, the uppercase of a dotted i is a dotted İ, not I. toUpperCase uses language-neutral rules; toLocaleUpperCase respects the locale you pass.

For an interface displaying user content in many languages, toLocaleUpperCase is more correct. For a machine-readable transformation such as building a key or a slug, you want the language-neutral version specifically, because a Turkish user's browser should not change what the key is.

Making it safe for any input

function capitalize(value) {
  if (typeof value !== "string" || value.length === 0) return "";
  const [first, ...rest] = [...value];
  return first.toUpperCase() + rest.join("");
}

The type check matters because undefined.charAt throws, and a value arriving from an API is often not the type you expected. Returning "" for bad input is one choice; returning the input unchanged is another. Either is better than a crash inside a render.

Normalising rather than capitalising

Capitalising for display is one job. Normalising a value so two versions of it compare equal is a different one, and the tool is the opposite:

const key = name.trim().toLowerCase();

Lowercasing, not uppercasing, and trimming first. This is what makes "Ada", "ada " and "ADA" collapse to one value for deduplication, lookup or comparison.

For anything involving accents there is a further step, because the same visible character can be encoded two ways:

"é" === "é";   // can be false
"é".normalize("NFC") === "é".normalize("NFC");   // true

One is a single code point, the other is e plus a combining accent. They look identical and are different strings. normalize("NFC") collapses them to one form, and any string used as a key should go through it.

Slugs need a third step, stripping the accents entirely:

const slug = (s) =>
  s.normalize("NFD")
    .replace(/[̀-ͯ]/g, "")   // drop combining marks
    .toLowerCase()
    .trim()
    .replace(/[^a-z0-9]+/g, "-")
    .replace(/^-|-$/g, "");

slug("Café Münster");   // "cafe-munster"

NFD splits characters into a base letter plus its marks, so the regex can remove the marks and leave the letter. That is the standard approach and it is worth keeping rather than rediscovering.

Sentence case for a whole paragraph

Capitalising after every full stop is a different problem again:

const sentenceCase = (s) =>
  s.toLowerCase().replace(/(^\s*\w|[.!?]\s+\w)/g, (c) => c.toUpperCase());

The regex matches the first word character of the string and the first after any sentence-ending punctuation.

It gets abbreviations wrong, because a full stop in "e.g." or "Dr." looks like a sentence end. There is no general fix short of a language model, and the usual answer is to accept the imperfection or avoid transforming prose at all.

That points at the honest conclusion for this whole topic: capitalisation is presentation, and transforming stored text loses information. Where you can store what the user typed and style it on the way out, do that.

Uppercasing the whole string

Sometimes the actual requirement is not the first letter:

s.toUpperCase();   // everything
s.toLowerCase();   // everything

Both return a new string and leave the original alone, like every string method in JavaScript. Strings are immutable, so s.toUpperCase() on its own line does nothing at all, which is a common early mistake:

name.toUpperCase();          // result discarded
name = name.toUpperCase();   // reassigned

The same applies to trim, replace, slice and the rest. If a string operation appears to have no effect, check whether the result was used.

For a heading that should read as uppercase, the CSS route is better again, since text-transform: uppercase keeps the underlying text intact for copying, searching and screen readers, which announce the original casing rather than spelling out what looks like an acronym.

Three common mistakes

Using s[0] instead of charAt(0). On an empty string, s[0] is undefined and the concatenation produces "undefined". charAt(0) returns an empty string.

Assuming one character is one code unit. Emoji and some scripts occupy two, so slice(1) can cut a character in half. Spread the string if the input is user-supplied.

Using toUpperCase() on the whole string when you meant the first letter. It happens more than it should, and the result is shouting rather than a capital.

Names are not a good place to do this

Worth saying plainly, because "capitalize the user's name" is the most common reason people arrive at this question.

Automatically capitalising a person's name gets it wrong for real people. "de Beauvoir", "van Gogh", "O'Brien", "McDonald", "bell hooks" and "danah boyd" all break under a rule that uppercases the first letter of every word, and two of those are people who capitalise their names that way deliberately.

The correct handling for a name field is to store exactly what the user typed and display exactly that. If the display looks inconsistent, that inconsistency is the data, and the person it belongs to is the authority on it.

Where transformation genuinely belongs:

  • Machine-generated identifiers: turning user_name into User name for a form label.
  • Enum values: in_progress becoming In progress.
  • Sentence starts in text you generated yourself.

The pattern across those: capitalise things your code produced, not things a person typed.

A helper worth keeping

export const capitalize = (value) => {
  if (typeof value !== "string") return "";
  const [first, ...rest] = [...value];
  return first ? first.toUpperCase() + rest.join("") : "";
};

export const titleCase = (value) =>
  String(value).split(" ").map(capitalize).join(" ");

export const humanize = (key) =>
  capitalize(String(key).replace(/[_-]+/g, " ").trim());

humanize is the one that earns its place most often: it turns first_name or first-name into First name, which is exactly the label case above and the reason most codebases end up needing this at all.

Quick reference

s.charAt(0).toUpperCase() + s.slice(1)        // the standard one-liner
const [f, ...r] = [...s]; f.toUpperCase() + r.join("")   // emoji-safe
s.replace(/^./u, (c) => c.toUpperCase())      // regex form
s.split(" ").map(capitalize).join(" ")        // title case
s.toLocaleUpperCase("tr-TR")                  // locale-aware
text-transform: capitalize;   /* when it is only presentation */

Want to try this on strings that fight back? Start with the JavaScript track.

Keep reading

More from the blog

Two arrays combined by spread into a new array, beside push spreading into an existing one and overflowing the stack
September 1, 2026·9 min readLearn

How to merge two arrays in JavaScript

To merge arrays in JavaScript, spread and concat both work. push(...arr) throws a RangeError at 200,000 items, which is why the choice matters.

Read more
The same object iterated with Object.keys and with for-in, where for-in returns an extra inherited key
August 31, 2026·9 min readLearn

How to loop through an object in JavaScript

To loop through an object in JavaScript, for...in walks inherited properties too. And integer-like keys come out first, whatever order you wrote.

Read more
{ }
✦

Ready to write some code?

Put this into practice - start your first free lesson. No setup, no credit card.

Start learning free
Devpuff

Learn to code by doing. One tiny, playful lesson at a time.

Learn
ProgramsCoursesPricing
Company
AboutBlogResourcesAffiliates
Support
Help CenterContactStatus
Programs
Frontend DevelopmentFull-Stack DevelopmentPython Developer
Courses
Advanced ReactAsync JavaScriptAsync PythonCSS BasicsCSS LayoutDSA Basics
Learn to Code
Learn JavaScriptLearn PythonLearn SQLLearn HTMLLearn CSSLearn ReactBrowse All Topics
Platform Comparisons
Devpuff vs CodecademyDevpuff vs MimoDevpuff vs Sololearn
© 2026 Devpuff. All rights reserved.Privacy PolicyTerms and ConditionsCookies PolicyRefund Policy
Devpuff vs freeCodeCamp
Read All Comparisons