Converting Between Camel, Snake, and Kebab Case in JavaScript

Abstract image used to represent Convert Between Camel, Snake, and Kebab Case in JS
Image by Daniela Castro.

In Brief

Case conversion works by tokenising the supported input before joining the tokens in the target format. This article's utility recognises spaces, hyphens, underscores, lowertoupper transitions, and acronymtoword transitions, keeps digits in the tokens, and collapses repeated separators. The final step joins those tokens as camel case, Pascal case, snake case, or kebab case.

When working in web development, text formatting can play a crucial (and sometimes underappreciated) role, particularly when handling variable or file names, API responses, and configuration settings. Different naming conventions (i.e., camel case, snake case, and kebab case) are widely used in JavaScript and software development in general, depending on the context.

Today, I intend to explore how we can go about converting between these different word cases using JavaScript, and discuss some of the most common use cases and best practices. By the end, you should have a decent understanding of how to manipulate text formatting in JavaScript, depending on your specific needs.


Understanding Naming Conventions

Before diving into the conversions themselves, let's take a moment to clarify the three most common naming styles:

  • camelCase is often used in JavaScript variable names, where each word after the first starts with an uppercase letter (e.g., theVariableName).
  • snake_case separates lowercase words with underscores, commonly used in database fields and Python variables (e.g., the_variable_name).
  • kebab-case separates lowercase words with hyphens instead of underscores and is frequently used in URLs and CSS class names (e.g., random-variable-name).

Converting a String of Text into Different Cases

It is very common in frontend development to find that you need to process text by converting a spaceseparated phrase into one of the specific naming conventions I've described above. For example let's look at, converting "random variable name" into camel case, snake case, and kebab case.

Sanitising the String

Before we apply any conversion to a string like this, we need to prepare it by cleaning up the input. We need to:

  • Trim spaces:

    Remove leading and trailing spaces.
  • Handle multiple spaces

    : Convert multiple spaces into a single space.
  • Remove special characters

    : Strip out any nonalphanumeric characters, keeping only letters, numbers, and spaces.

So, here's a utility function that does exactly that:

const sanitiseString = (str: string): string =>
  str.trim().replace(/[^a-zA-Z0-9\s]/g, "").replace(/\s+/g, " ");

Converting a String to camelCase

So, with sanitisation handled, we can convert our strings into camelCase by:

  • Converting the cleaned string to lowercase.
  • Use a regular expression to find each word after a space and capitalise its first letter.
  • Remove spaces from the final output.

This looks something like this:

const toCamelCase = (str: string): string =>
  sanitiseString(str).toLowerCase().replace(/ (\w)/g, (_, char) => char.toUpperCase());

console.log(toCamelCase("random variable name!"));  // Output: randomVariableName

A sidenote about PascalCase

PascalCase is common for React component names, classes, and TypeScript types. It is closely related to camelCase: each word begins with a capital, including the first. So instead of randomVariableName (as you would get with camelCase), PascalCase produces RandomVariableName.

As far as implementation is concerned, it's almost an exact copy of the above but with a very slightly different regex so that we capture the first letter as well as the first letter of each following word:

const toPascalCase = (str: string): string =>
  sanitiseString(str).replace(/(?:^| )(\w)/g, (_, char) => char.toUpperCase());

console.log(toPascalCase("random variable name!"));  // Output: RandomVariableName

Converting a String to snake_case

Unlike camelCase and PascalCase, snake_case retains a physical representation of where the spaces in our string were, by replacing them with underscores (_).

To do this, we:

  • Convert the cleaned string to lowercase.
  • Replace spaces with underscores.

Something like this:

const toSnakeCase = (str: string): string =>
  sanitiseString(str).toLowerCase().replace(/\s+/g, "_");

console.log(toSnakeCase("random variable name!"));  // Output: random_variable_name

Converting a String to kebab-case

Much like the relationship between camelCase and PascalCase, kebab-case is a very close relative to snake_case, with hyphens(-) instead of underscores.

It will come as no surprise then that implementation is very similar too. To achieve kebab-case, we:

  • Convert the cleaned string to lowercase.
  • Replace spaces with hyphens.
const toKebabCase = (str: string): string =>
  sanitiseString(str).toLowerCase().replace(/\s+/g, "-");

Putting It All Together: A Single Utility to Convert to Different Cases

In the real world, we wouldn't necessarily want to maintain separate functions for each case; we would create a utility function that handles whichever case you choose to ask of it.

Something like this:

const convertCase = (
  str: string,
  format: 'camel' | 'pascal' | 'snake' | 'kebab'
): string => {
  const sanitiseString = (str: string): string =>
    str
      .trim()
      .replace(/[^a-zA-Z0-9\s]/g, '')
      .replace(/\s+/g, ' ');

  const formatted = sanitiseString(str);

  switch (format) {
    case 'camel':
      return formatted
        .toLowerCase()
        .replace(/ (\w)/g, (_, char) => char.toUpperCase());
    case 'pascal':
      return formatted.replace(/(?:^| )(\w)/g, (_, char) => char.toUpperCase());
    case 'snake':
      return formatted.toLowerCase().replace(/\s+/g, '_');
    case 'kebab':
      return formatted.toLowerCase().replace(/\s+/g, '-');
    default:
      throw new Error('Unsupported format type');
  }
};

In this way, we can bundle all the different cases together into one nice and precise utility function.


Why use a regex pattern in replace()?

These examples already use replace(). The choice is whether its first argument is a literal string or a regular expression. A string pattern replaces the first matching occurrence; a regex with the g flag replaces every match. Regex also lets us capture a character and change it in the replacement callback.

  • Multiple matches: A regex with the g flag can replace all matching spaces or separators. For repeated literal strings, replaceAll() is another option.
  • Captured characters: The pattern can capture the character after a space so the replacement callback can capitalise it.
  • Pattern matching: A regex can describe a group of possible characters, such as whitespace, instead of requiring a separate replacement for each one.
  • Related conversions: The camelCase and PascalCase examples use similar patterns, with the latter also matching the first character.

For example, in the toCamelCase() function:

sanitiseString(str).toLowerCase().replace(/ (\w)/g, (_, char) => char.toUpperCase());
  • / (\w)/g looks for a space followed by a word character. In the sanitised ASCII input here, that character is a letter or digit.
  • The function capitalises the letter and removes the space.

Converting Between Different Cases

Converting between different cases is more complicated than simply taking a string and converting it to a predefined case pattern. After all, that's what this article is really all about.

So, here's one I prepared earlier:

type CaseFormat = "camel" | "pascal" | "snake" | "kebab";

const tokeniseCase = (value: string): string[] => {
  return value
    .trim()
    .replace(/([a-z0-9])([A-Z])/g, "$1 $2")
    .replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2")
    .replace(/[^a-zA-Z0-9]+/g, " ")
    .trim()
    .split(/\s+/)
    .filter(Boolean)
    .map((word) => word.toLowerCase());
};

const capitalise = (word: string): string => {
  return word.charAt(0).toUpperCase() + word.slice(1);
};

const convertBetweenCases = (value: string, targetFormat: CaseFormat): string => {
  const words = tokeniseCase(value);

  if (words.length === 0) {
    return "";
  }

  switch (targetFormat) {
    case "camel":
      return words[0] + words.slice(1).map(capitalise).join("");
    case "pascal":
      return words.map(capitalise).join("");
    case "snake":
      return words.join("_");
    case "kebab":
      return words.join("-");
    default:
      throw new Error("Unsupported format type");
  }
};

console.log(convertBetweenCases("random_variable_name", "camel"));  // Output: randomVariableName
console.log(convertBetweenCases("randomVariableName", "snake"));  // Output: random_variable_name
console.log(convertBetweenCases("random-variable-name", "pascal"));  // Output: RandomVariableName
console.log(convertBetweenCases("RandomVariableName", "kebab"));  // Output: random-variable-name
console.log(convertBetweenCases("HTTPResponseCode", "camel"));  // Output: httpResponseCode
console.log(convertBetweenCases("version__2--API", "kebab"));  // Output: version-2-api

Here, we're putting everything together. Before conversion, the utility tokenises ASCII letters and digits using separators, lowertoupper transitions, and acronymtoword transitions as boundaries. It then joins those tokens in the requested format; it does not need to detect and label the original case first.

This works like this:

  1. Tokenises Input

    : We use tokeniseCase() to preserve the supported word boundaries before conversion.
  2. Handles Acronyms

    : tokeniseCase() keeps a run such as HTTP together while creating a boundary before Response in HTTPResponse.
  3. Handles Repeated Separators

    : tokeniseCase() collapses multiple spaces, underscores, hyphens, or other separators into one boundary; digits remain part of the resulting tokens.
  4. Converts to Target Case

    : The targetFormat value determines how the tokens are joined in camel, Pascal, snake, or kebab case.
  5. Returns Result

    : Empty input returns an empty string; otherwise, the transformed tokens are returned.

Wrapping Up

Formatting and reformatting text in JavaScript is an important skill in web development, especially when working with variable names, API responses, or structured data. Today, we've explored how to convert spaceseparated strings into camelCase, PascalCase, snake_case, and kebab-case whilst ensuring that the input is properly sanitised beforehand.

I've also bundled all of this together into a single utility function that preserves word boundaries before transforming the tokens into another naming convention.

Key Takeaways

  • Properly sanitising input ensures consistency in case transformations.
  • Camel case (camelCase) and Pascal case (PascalCase) are common in JavaScript, whilst snake case (snake_case) and kebab case (kebab-case) are widely used in APIs and URLs.
  • A robust conversion utility defines its supported input grammar and preserves word boundaries before joining tokens in the target case.
  • Regular expressions provide an efficient way to transform text structures reliably.

Understanding these techniques will help you work more effectively with different naming conventions, making your JavaScript code cleaner and easier to maintain.


Want to find out more?

If you need senior handson support with a complex React or Next.js platform, migration, performance issue, or technical SEO problem, send me the context and I'll tell you where I can help.