DEV Community

Ashish Kumar
Ashish Kumar

Posted on

Naming Things: When to Use camelCase, snake_case, kebab-case, and PascalCase

There are two hard problems in computer science: cache invalidation, naming things, and off-by-one errors. The naming-things one is the only one we can actually argue about all afternoon, so we do.

This post is a practical reference for when each casing convention is correct, where they collide (databases meet APIs meet frontends meet URLs), and how to handle the conversions without sprinkling _.camelCase() calls across your codebase like prayers.

The five conventions you'll see

Convention Example Where it lives
camelCase userEmail JavaScript variables, JSON keys, Java/Swift methods
PascalCase UserEmail Class names, type names, React components, C# everything
snake_case user_email Python variables, SQL columns, Ruby methods, env vars (sort of)
kebab-case user-email URLs, CSS classes, HTML attributes, file names, npm packages
SCREAMING_SNAKE_CASE USER_EMAIL Constants, environment variables

That's the picture. The rest of the post is about why each one ended up where it did, and what to do when you have to cross between them.

camelCase — the JavaScript default

JavaScript has used camelCase since the language existed. Variable names, function names, object keys, parameter names — all camelCase. The DOM API uses it (document.querySelector, addEventListener), browser globals use it, every framework follows it.

const userEmail = 'jane@example.com'
function isValidEmail(input) { /* ... */ }
fetch('/api/users').then(res => res.json())
Enter fullscreen mode Exit fullscreen mode

Java, Swift, Kotlin, and TypeScript also use camelCase for variables and methods (PascalCase for classes). If you're writing in any of these languages and the team convention is "use camelCase", that's not a stylistic preference; that's the language convention.

One exception worth knowing. Some JavaScript codebases use snake_case for properties that come straight from the database or from a Python/Ruby API to avoid mid-flight conversion bugs. It's a defensible choice, but it does leak the backend's convention into the frontend forever.

PascalCase — for things that get instantiated

PascalCase (which is just camelCase with the first letter capitalized) signals "this is a type, not a value." Used for:

  • Classes: class UserRepository {}
  • TypeScript types and interfaces: interface User {}, type LoginResult = ...
  • React components: function UserAvatar({ user }). JSX literally requires this — <userAvatar /> is treated as an HTML element, <UserAvatar /> as a component.
  • Enum values in some style guides: enum Status { Active, Pending, Banned }

The mental model: if you can new it, type-check against it, or render it as a component, it's PascalCase. If it's a value you read or call, it's camelCase. Mostly that line is sharp; once in a while you'll see a class meant to be used like a singleton (Math, JSON) and the line blurs.

snake_case — Python, SQL, and the systems layer

snake_case dominates Python (PEP 8 mandates it), SQL columns (most style guides at least), Ruby, Rust (mostly), and any language that came out of the Unix systems-programming world.

def get_user_by_id(user_id):
    return db.query("SELECT user_email FROM users WHERE id = %s", (user_id,))
Enter fullscreen mode Exit fullscreen mode

The trade-off vs camelCase is readability of long names: get_user_by_id is slightly easier to read than getUserById for most people, especially with screen readers. The cost is that snake_case is one extra character per word boundary, which adds up in 50,000-line codebases.

Environment variables are kind of snake_case but uppercase: DATABASE_URL, API_KEY, NODE_ENV. This is convention from POSIX, and it's universal — never use lowercase or camelCase for env vars. Tools assume uppercase.

kebab-case — anywhere a hyphen is legal and an underscore isn't

This is the rule. kebab-case lives wherever syntax permits hyphens but not underscores (or where hyphens are the established convention):

  • URLs: /blog/why-rust-matters. Search engines parse hyphens as word separators; underscores get treated as part of one word.
  • CSS class names: .user-avatar, .is-active. Conventions like BEM lean hard on kebab-case.
  • HTML attributes: data-user-id, aria-label.
  • CLI flags: --dry-run, --no-color.
  • npm package names: lodash, eslint-plugin-react. Underscores are technically allowed but discouraged.
  • File names in many ecosystems: user-service.ts, 404-not-found.html.

Why hyphens for URLs specifically? Because /blog/why_rust_matters shows up in Google search ranking as one word ("why_rust_matters") rather than three. The SEO impact is real.

SCREAMING_SNAKE_CASE — constants and signals

The all-caps form is a signal that says "this value is fixed at build/deploy time, not runtime."

const MAX_RETRIES = 5
const DEFAULT_TIMEOUT_MS = 30_000
const FEATURE_FLAGS = ['beta-search', 'new-checkout']
Enter fullscreen mode Exit fullscreen mode
DATABASE_URL = os.environ['DATABASE_URL']
DEBUG = False
Enter fullscreen mode Exit fullscreen mode

In JavaScript, this convention is fading — some style guides treat all const as constant enough to use camelCase, reserving SCREAMING_CASE only for truly immutable, module-scoped values. Pick a rule and apply it consistently; what kills readability is a codebase where some constants are uppercase and some aren't with no rule.

Where conventions collide

Real applications cross between conventions constantly. The friction points:

Database to API. Postgres column user_email becomes JSON key userEmail becomes JavaScript variable userEmail. Most ORMs (Sequelize, Prisma, ActiveRecord, SQLAlchemy) handle this automatically with options like underscored: true.

API to URL. A resource called userProfile in your code lives at /user-profile, not /userprofile or /userProfile. URL-case conversion is a 5-line function but the boundary where you do it matters: at the route definition, not at the controller.

File names to imports. import { UserAvatar } from './user-avatar.tsx'. The file is kebab-case, the export is PascalCase. Most teams hold this convention; some force file names to match their default export. Either is fine; pick one.

Env to config. DATABASE_URL becomes config.databaseUrl in code. Don't keep them named identically — that's how secret leaks happen, when someone does console.log(config) and your env shows up in logs.

Conversion is mechanical — make it boring

The single biggest mistake teams make is converting cases by hand, in scattered places, inconsistently. Pick one of these approaches and apply it everywhere:

1. Convert at the boundary. API response gets converted to camelCase as soon as it arrives, before any business logic touches it:

import { camelCase } from 'lodash'
const data = await res.json().then(deepCamelCase)
Enter fullscreen mode Exit fullscreen mode

2. Use a schema layer. Zod, io-ts, or whatever validation library you use can transform field names as part of parsing.

3. Generate the converters. If your API has an OpenAPI spec, generators like openapi-typescript-codegen produce camelCased clients automatically.

For one-off conversions while writing or refactoring, browser tools handle the boilerplate. The text utilities at text.renderlog.in include dedicated converters for each case style — camelCase, snake_case, kebab-case, PascalCase, Title Case, sentence case — useful when you're translating naming for a config schema, generating SQL DDL from a TypeScript type, or wrangling a CSV with mixed-case headers. The whole site runs client-side, so config files and column names don't get uploaded anywhere.

Anti-patterns I see in code review

Mixed cases in one scope. const user_email next to const userName in the same file. Pick one.

Stutter in identifiers. user.userEmail, Order.orderId. The parent object already gives you the namespace; drop the prefix and write user.email, order.id.

Acronyms inconsistently cased. Is it parseHTML or parseHtml? userID or userId? Both work; what's wrong is using both in the same codebase. Most style guides land on "treat acronyms as words" — parseHtml, userId, loadJsonFile. ESLint and most linters can enforce this.

Casing that fights the language. Writing getUserById in Python or get_user_by_id in JavaScript is a hill that's not worth dying on. Match the language's culture.

TL;DR

You're naming a... Use...
JS/TS variable, JSON key camelCase
Class, type, React component PascalCase
Python variable, SQL column snake_case
URL, CSS class, file name, CLI flag kebab-case
Build-time constant, env var SCREAMING_SNAKE_CASE

Pick a rule per layer, enforce it with a linter, convert at boundaries, and don't mix conventions inside one file. When you're switching between cases in your head, browser-based case converters save the muscle memory for the actually hard problem — which is still cache invalidation.


If this was useful, I've also built a handful of other free, browser-based tools — no signup, no uploads, everything runs client-side:

Top comments (0)