React components have one inviolable convention — PascalCase for the component name itself — and several team-by-team conventions for file naming, folder structure, and prop naming. Here's a tour of what works.

PascalCase for component names

React requires PascalCase for component names. This isn't a style preference — it's a parsing rule. JSX treats lowercase tag names as HTML elements:

// HTML element (lowercase = built-in)
<div />

// React component (PascalCase = component reference)
<UserProfile />

If you write <userProfile />, React thinks you mean an HTML element called userProfile, which doesn't exist. This is the most common newcomer mistake.

The PascalCase rule applies to:

  • Component function/class names: function UserProfile() { ... }
  • The JSX tag references: <UserProfile />
  • Named exports of components: export const UserProfile = () => ...

It does not apply to:

  • Props: <UserProfile userName="..." /> — props are camelCase
  • State variables: const [userName, setUserName] = useState('') — camelCase
  • Event handlers: const handleClick = () => ... — camelCase
  • Custom hooks: function useUserData() { ... } — start with use, then camelCase

File naming: three common patterns

React has no enforced file-naming convention. Three patterns are widely used:

1. PascalCase matching the component

components/
  UserProfile.jsx
  UserAvatar.jsx
  UserSettings.jsx

This is the most common pattern. The file name matches the component name exactly. Imports look like:

import UserProfile from './components/UserProfile';

Strengths: visual symmetry between file and component name. Easy to navigate the file tree because file names match the React tree.

Weaknesses: some build systems and operating systems have case-sensitivity quirks (Windows, macOS default filesystems are case-insensitive; Linux is case-sensitive). A typo like ./UserProfile vs ./userprofile might work locally on macOS and fail in CI on Linux.

2. kebab-case file names

components/
  user-profile.jsx
  user-avatar.jsx
  user-settings.jsx

Some teams (especially Vue migrants) use kebab-case for file names while keeping PascalCase for component names:

// In user-profile.jsx
export default function UserProfile() { ... }

// Imported
import UserProfile from './components/user-profile';

Strengths: kebab-case file names avoid case-sensitivity issues. Matches the URL convention.

Weaknesses: visual mismatch between file and component name. The convention is less common in React-specific tooling.

3. Folder per component with index.jsx

components/
  UserProfile/
    index.jsx
    UserProfile.module.css
    UserProfile.test.jsx
    types.ts
  UserAvatar/
    index.jsx
    ...

Each component gets its own folder with related files (styles, tests, types) co-located. Imports use the folder name:

import UserProfile from './components/UserProfile';

Strengths: keeps related files together. Scales well to components with many associated files. Makes it easy to "delete this whole component" by removing the folder.

Weaknesses: more deeply nested directory structure. Many files named index.jsx can be confusing in editor tabs.

Component naming patterns

Beyond PascalCase, several patterns make component names easier to read and group:

Noun-based names for visual components

Components that render visible UI are conventionally nouns: Button, Card, Modal, UserProfile, NavigationBar. They describe what the component is.

Verb-prefixed names for logical/wrapper components

Components that wrap or modify other components without rendering visible UI sometimes use verbs: WithTheme, WithAuth, WrapInBoundary. These are higher-order components or context providers.

Hook-style names for behavior components

Render-prop components or behavior providers sometimes use a noun-first pattern: FocusTrap, ClickOutside, ScrollSpy. They describe a behavior they provide.

Domain prefixes for namespacing

Large codebases often prefix components by feature area: UserProfile, UserAvatar, UserSettings, OrderList, OrderDetail, OrderForm. This makes file trees and import statements easier to scan.

Container vs. presentational components

An older React pattern (less common now with hooks but still useful) split components into two types:

  • Container components: handle data fetching, state, and logic. Often named with a suffix: UserProfileContainer.
  • Presentational components: receive data via props and only render UI. Named without suffix: UserProfile.

With hooks, this split has become less pronounced — most modern components do both. But the naming pattern persists in some codebases.

Custom hook naming

Custom hooks have a strict naming convention: they must start with use followed by a camelCase descriptor. useFormState, useDebounce, useUserData, useLocalStorage.

The use prefix is detected by the React Hooks ESLint plugin and used to enforce the rules of hooks. Hooks named without the use prefix don't get the lint coverage and can lead to subtle bugs.

The descriptor should be a noun phrase (useFormState) or a verb phrase (useDebounce) describing what the hook does. Avoid generic names like useUtility.

Prop naming

Props are camelCase. Convention is to use descriptive names that read like English:

  • isLoading for booleans (also hasError, canEdit)
  • onClick, onSubmit for event handlers (lowercase on + PascalCase event)
  • userName, userId for data (camelCase, descriptive)
  • children for content (React's built-in name; don't rename)
  • renderHeader, renderFooter for render props

The most important convention: event handler props are on + the event. onClick for click handlers. onSubmit for form submits. onChange for input changes. This matches React's built-in event prop names.

Boolean props

Boolean props should be true/false statements, not flags. Three patterns work:

  • isDisabled (state)
  • hasIcon (existence)
  • canEdit (permission)

Avoid:

  • disabled={true} versus disabled={false} reads OK but ambiguous as just disabled. Some teams use isDisabled for clarity.
  • noBorder — negated booleans get confusing. Prefer hasBorder={false}.
  • type="primary" for boolean-ish values when it should just be a string enum.

Avoiding name collisions

React doesn't enforce uniqueness of component names within a project — multiple files can export components called Button. This is fine for small projects but causes confusion at scale.

Common patterns to avoid collisions:

  • Prefix by feature area: OrderButton, NavButton, DialogButton rather than three different Button components.
  • Compound naming for variants: PrimaryButton, SecondaryButton, IconButton.
  • Namespace by import path: a generic Button is fine if imports clearly distinguish them (import { Button } from '@/components/forms';).

Index files and barrel exports

A common pattern is to expose components through a barrel index.js file:

// components/index.js
export { UserProfile } from './UserProfile';
export { UserAvatar } from './UserAvatar';
export { UserSettings } from './UserSettings';

// Usage
import { UserProfile, UserAvatar } from '@/components';

This makes imports tidier but creates a few downsides: harder for build tools to tree-shake (some bundlers can't optimize barrel exports as well), longer import chains, slower IDE autocomplete. Use barrels sparingly — they pay off in small APIs (public component libraries) and cost in large codebases.

Folder structure patterns

Three common patterns for organizing React components in a project:

By type

src/
  components/
    Button.jsx
    Card.jsx
    Modal.jsx
  pages/
    Home.jsx
    UserProfile.jsx
  hooks/
    useAuth.js
    useUserData.js

Simple and obvious. Works for small projects. Breaks down at scale because every feature touches every folder.

By feature

src/
  features/
    users/
      components/
      hooks/
      api.js
    orders/
      components/
      hooks/
      api.js
  shared/
    components/
    hooks/

Each feature owns its own components, hooks, and logic. Scales well. The downside is that "shared" easily becomes a dumping ground.

By route

src/
  app/
    page.jsx
    users/
      page.jsx
      [id]/
        page.jsx
    orders/
      page.jsx

The Next.js app-router pattern. Components live where they're rendered. Easy to reason about routing.

Bulk renaming

If you're refactoring a codebase from one convention to another — say, renaming all components from kebab-case file names to PascalCase, or shifting from userProfile camelCase imports to UserProfile — our bulk converter handles a list of identifiers in one pass. Combine the output with a sed script or codemod to update all references.

For converting a single component name between camelCase and PascalCase, the PascalCase converter is fastest.

The bigger principle

React conventions vary between teams more than most languages. The convention itself matters less than:

  1. That your team has a documented convention
  2. That the convention is enforced by linters where possible
  3. That new code follows the convention consistently
  4. That old code is gradually refactored when touched

The cost of mixed conventions shows up in code review, onboarding, and the slow tax of every developer mentally translating between styles. Pick a convention, document it, and stick to it.