Skip to content

Instantly share code, notes, and snippets.

@nblackburn
Last active December 15, 2023 03:19
Show Gist options
  • Star 84 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save nblackburn/875e6ff75bc8ce171c758bf75f304707 to your computer and use it in GitHub Desktop.
Save nblackburn/875e6ff75bc8ce171c758bf75f304707 to your computer and use it in GitHub Desktop.
Convert a string from camel case to kebab case.
module.exports = (string) => {
return string.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2').toLowerCase();
};
@amiceli
Copy link

amiceli commented Aug 24, 2017

Nice !

@nblackburn
Copy link
Author

@amiceli Thanks!

@maximelafarie
Copy link

Awesome, thank you @nblackburn!

@Mobiletainment
Copy link

Simple and elegant! Well done!

@blemaire
Copy link

Small bug here, directives which include numbers do not convert correctly
The below fixes it:

module.exports = (string) => {
    return string.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
};

@lwxbr
Copy link

lwxbr commented Jul 26, 2018

Thank you!

@tedconn
Copy link

tedconn commented Nov 8, 2018

In Java:

private static String camelToKebabCase(String str) {
    return str.replaceAll("([a-z0-9])([A-Z])", "$1-$2").toLowerCase();
}

@csvn
Copy link

csvn commented Mar 28, 2019

A small part can be added to change how uppercase sections are transformed, e.g. HTMLInputElement.

module.exports = (string) => {
    return string
      .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
      .replace(/([A-Z])([A-Z])(?=[a-z])/g, '$1-$2')
      .toLowerCase();
};

The above will produce html-input-element instead of htmlinput-element.

@bradgreens
Copy link

bradgreens commented Jun 5, 2019

@csvn I really liked your version, but backed out for 2 reasons:

  1. Can create unexpected result with something like HTMLBRElement (I geek'd out and looked at all the el types)
  2. We're using the function to map against database classes, where we should not have such a casing issue (so far?) that the ol' DOM has

@nblackburn
Copy link
Author

I have made some tweaks to address most of the pain points outlined in this discussion.

@steve-ross
Copy link

string) => {
    return string
      .replace(/([a-z0-9])([A-Z])/g, '$1-$2')
      .replace(/([A-Z])([A-Z])(?=[a-z])/g, '$1-$2')
      .toLowerCase();
};

THANKS! handles "FooBar" -> "foo-bar" 👏🏻

@pastukh-dm
Copy link

@csvn perfect for me, thanks!

@wpatter6
Copy link

Stole this for a converter tool codepen: https://codepen.io/wpatter6/pen/wvweWZa

@thejhh
Copy link

thejhh commented Jan 1, 2020

Nice! :)

@rawlinc
Copy link

rawlinc commented Mar 24, 2021

UPDATED (5/6/2021):

The solution in my original comment worked in most browsers except Safari, which currently does not support lookbehind assertions in regular expressions. Here is an updated version that should work in any recent browser version, including Safari. It isn't too dissimilar from solutions posted above but does support all of the test cases outlined below, so thanks to those who posted the original solutions that got me most of the way to what I was looking for:

module.exports = (string) => {
    return string
        .replace(/\B([A-Z])(?=[a-z])/g, '-$1')
        .replace(/\B([a-z0-9])([A-Z])/g, '$1-$2')
        .toLowerCase();
};

When Safari starts supporting lookbehind assertions, the original solution would be viable for production work but I'm using the solution above until that happens.

ORIGINAL COMMENT:

Here is another one:

module.exports = (string) => {
    return string.replace(/\B(?:([A-Z])(?=[a-z]))|(?:(?<=[a-z0-9])([A-Z]))/g, '-$1$2').toLowerCase();
};

It supports the following conversions (either camel or pascal case to kebab case):

fooBar                              ->  foo-bar
FooBar                              ->  foo-bar
AFooBar                             ->  a-foo-bar
AFooBarACRONYM                      ->  a-foo-bar-acronym
ACRONYMFooBar                       ->  acronym-foo-bar
FooACRONYMBar                       ->  foo-acronym-bar
ACRONYMFooACRONYMBarACRONYM         ->  acronym-foo-acronym-bar-acronym
ACRONYM1Foo1ACRONYM2Bar1ACRONYM3    ->  acronym1-foo1-acronym2-bar1-acronym3

@ibockowsky
Copy link

Here is another one:

module.exports = (string) => {
    return string.replace(/\B(?:([A-Z])(?=[a-z]))|(?:(?<=[a-z0-9])([A-Z]))/g, '-$1$2').toLowerCase();
};

It supports the following conversions (either camel or pascal case to kebab case):

fooBar                              ->  foo-bar
FooBar                              ->  foo-bar
AFooBar                             ->  a-foo-bar
AFooBarACRONYM                      ->  a-foo-bar-acronym
ACRONYMFooBar                       ->  acronym-foo-bar
FooACRONYMBar                       ->  foo-acronym-bar
ACRONYMFooACRONYMBarACRONYM         ->  acronym-foo-acronym-bar-acronym
ACRONYM1Foo1ACRONYM2Bar1ACRONYM3    ->  acronym1-foo1-acronym2-bar1-acronym3

unluckily it's not support in Safari :(

@bradgreens
Copy link

unluckily it's not support in Safari :(

I was curious why.... so here is why.

https://stackoverflow.com/a/51568859

Looks like Safari doesn't support lookbehind yet (that is, your (?<=/)).

@rawlinc
Copy link

rawlinc commented May 6, 2021

@bradgreens @ibockowsky Yeah, I should have reported back when I found out about lack of support with lookbehind assertions in Safari. I ran into that during browser testing too.

I've updated my original comment with what I used to get around Safari's lack of lookbehind assertion support.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment