camelCase vs snake_case: A Guide to Text Case Conversion
Every programming language, framework, and file-system has its own preferred naming style. Whether you're converting a variable name, renaming a file, or cleaning up an API response, understanding text case conventions saves time and prevents bugs.
The Five Most Common Cases
| Case | Example | Also Called |
|---|---|---|
camelCase |
myVariableName |
Lower camel case |
PascalCase |
MyVariableName |
Upper camel case, StudlyCase |
snake_case |
my_variable_name |
Underscore case |
SCREAMING_SNAKE_CASE |
MY_VARIABLE_NAME |
Constant case, UPPER_SNAKE |
kebab-case |
my-variable-name |
Lisp case, spinal case, slug case |
There are also rarer variants like TRAIN-CASE (My-Variable-Name), dot.case (my.variable.name), and flatcase (myvariablename), but the five above cover 95% of real-world usage.
When to Use Each Case
camelCase
- JavaScript / TypeScript: variables, function names, object keys
- Java / Kotlin / Swift: variables, method names
- JSON keys (by convention):
{"firstName": "Ada"} - React props:
onClick,isVisible,maxLength
PascalCase
- Classes and types in most languages:
UserAccount,HttpClient - React components:
<MyButton />,<ProductCard /> - C# / .NET: methods and properties use PascalCase, not camelCase
- TypeScript interfaces and enums:
interface ApiResponse,enum Status
snake_case
- Python: the official PEP 8 style for variables and functions
- Ruby: variables, methods, file names
- PostgreSQL / MySQL: table names, column names (
created_at,user_id) - PHP: older codebases and WordPress follow snake_case for functions
SCREAMING_SNAKE_CASE
- Constants in most languages:
MAX_RETRY_COUNT,API_BASE_URL - Environment variables:
DATABASE_URL,NODE_ENV - Enum values in Go and C:
STATUS_OK,HTTP_NOT_FOUND
kebab-case
- HTML attributes:
data-user-id,aria-label - CSS classes:
.nav-bar,.hero-section - URL slugs and file names:
how-to-use-regex.html,my-component.tsx - CLI flags:
--dry-run,--output-format
The Conversion Algorithm
Any case-to-case conversion has two phases:
- Tokenise — split the input into words
- Rejoin — merge words in the target format
Phase 1: Tokenise
The trick is normalising all separators before splitting:
Input: "myVariableName" OR "my_variable_name" OR "my-variable-name"
Step 1 – insert underscore before each uppercase letter:
"my_Variable_Name" (camelCase only)
Step 2 – replace hyphens, spaces, dots with underscores:
"my_variable_name"
Step 3 – collapse repeated underscores → split on "_" → lowercase each word:
["my", "variable", "name"]
Phase 2: Rejoin
| Target case | Join rule |
|---|---|
snake_case |
words.join("_") |
SCREAMING_SNAKE_CASE |
words.join("_").toUpperCase() |
kebab-case |
words.join("-") |
camelCase |
first word lowercase, rest title-cased, no separator |
PascalCase |
all words title-cased, no separator |
Code Examples
JavaScript / TypeScript
function toWords(str) {
return str
.replace(/([a-z])([A-Z])/g, "$1_$2") // split camelCase
.replace(/[\s\-\.]+/g, "_") // normalise separators
.replace(/_+/g, "_")
.toLowerCase()
.split("_")
.filter(Boolean);
}
const toSnake = s => toWords(s).join("_");
const toScream = s => toWords(s).join("_").toUpperCase();
const toKebab = s => toWords(s).join("-");
const toCamel = s => toWords(s).map((w, i) => i === 0 ? w : w[0].toUpperCase() + w.slice(1)).join("");
const toPascal = s => toWords(s).map(w => w[0].toUpperCase() + w.slice(1)).join("");
console.log(toSnake("myVariableName")); // "my_variable_name"
console.log(toCamel("my_variable_name")); // "myVariableName"
console.log(toPascal("kebab-case-str")); // "KebabCaseStr"
console.log(toKebab("PascalCaseInput")); // "pascal-case-input"
Python
import re
def to_words(s: str) -> list[str]:
s = re.sub(r"([a-z])([A-Z])", r"\1_\2", s) # split camelCase
s = re.sub(r"[\s\-\.]", "_", s) # normalise separators
return [w for w in s.lower().split("_") if w]
def to_snake(s): return "_".join(to_words(s))
def to_scream(s): return "_".join(to_words(s)).upper()
def to_kebab(s): return "-".join(to_words(s))
def to_camel(s): w = to_words(s); return w[0] + "".join(x.capitalize() for x in w[1:])
def to_pascal(s): return "".join(x.capitalize() for x in to_words(s))
print(to_snake("myVariableName")) # my_variable_name
print(to_camel("my_variable_name")) # myVariableName
print(to_pascal("kebab-case-str")) # KebabCaseStr
Go
package main
import (
"regexp"
"strings"
)
var splitRe = regexp.MustCompile(`([a-z])([A-Z])`)
var sepRe = regexp.MustCompile(`[\s\-\.]+`)
func toWords(s string) []string {
s = splitRe.ReplaceAllString(s, "${1}_${2}")
s = sepRe.ReplaceAllString(s, "_")
s = strings.ToLower(s)
parts := strings.Split(s, "_")
out := parts[:0]
for _, p := range parts {
if p != "" { out = append(out, p) }
}
return out
}
func toSnake(s string) string { return strings.Join(toWords(s), "_") }
func toScream(s string) string { return strings.ToUpper(strings.Join(toWords(s), "_")) }
func toKebab(s string) string { return strings.Join(toWords(s), "-") }
func toCamel(s string) string {
ws := toWords(s)
for i := 1; i < len(ws); i++ {
ws[i] = strings.Title(ws[i])
}
return strings.Join(ws, "")
}
func toPascal(s string) string {
ws := toWords(s)
for i, w := range ws { ws[i] = strings.Title(w) }
return strings.Join(ws, "")
}
PHP
function to_words(string $s): array {
$s = preg_replace('/([a-z])([A-Z])/', '$1_$2', $s);
$s = preg_replace('/[\s\-\.]+/', '_', $s);
return array_filter(explode('_', strtolower($s)));
}
function to_snake(string $s): string { return implode('_', to_words($s)); }
function to_scream(string $s): string { return strtoupper(implode('_', to_words($s))); }
function to_kebab(string $s): string { return implode('-', to_words($s)); }
function to_camel(string $s): string {
$ws = array_values(to_words($s));
return $ws[0] . implode('', array_map('ucfirst', array_slice($ws, 1)));
}
function to_pascal(string $s): string {
return implode('', array_map('ucfirst', to_words($s)));
}
echo to_snake("myVariableName"); // my_variable_name
echo to_camel("my_variable_name"); // myVariableName
Quick Conversion Reference
| Input | → snake | → SCREAM | → kebab | → camel | → Pascal |
|---|---|---|---|---|---|
helloWorld |
hello_world |
HELLO_WORLD |
hello-world |
helloWorld |
HelloWorld |
my-component |
my_component |
MY_COMPONENT |
my-component |
myComponent |
MyComponent |
USER_ID |
user_id |
USER_ID |
user-id |
userId |
UserId |
GetHTTPResponse |
get_http_response |
GET_HTTP_RESPONSE |
get-http-response |
getHttpResponse |
GetHttpResponse |
Edge Cases to Watch For
Acronyms — conventions differ:
- Google/GitHub style: treat the whole acronym as one word →
xmlParser,httpClient - Microsoft/.NET style: capitalise first letter only →
XmlParser,HttpClient - Either is fine — just be consistent within a project.
Numbers — usually attached to the preceding word:
my2ndElement→["my2nd", "element"](most tokenisers)- Some tokenisers split at digit boundaries:
my_2nd_element
Single-word inputs — no separators, no change needed. "hello" → "hello" in every case except PascalCase → "Hello" and SCREAMING → "HELLO".
Empty strings and null — always guard: if (!s) return s;
FAQ
Which case is "standard" for JSON?
No official standard, but camelCase is the most common in REST APIs (following JavaScript conventions). snake_case is used by GitHub, Stripe, and many Python APIs.
Does it matter which case I use?
Only within a codebase — mixed conventions are a red flag. Pick one per context (variables, files, URLs) and enforce it with a linter.
How do I enforce naming conventions automatically?
ESLint (camelcase rule), Prettier (limited), pylint, golangci-lint (revive), and most IDE code-style checkers all support naming rules.
Why is kebab-case invalid for JavaScript variable names?
The hyphen - is the subtraction operator in JavaScript, so my-variable is parsed as my minus variable. Use kebab-case only for CSS, HTML attributes, URLs, and file names.
What is Title Case?
Title Case capitalises the first letter of each word with spaces: "My Variable Name". Useful for UI labels and headings, not for code identifiers.