Consistent naming improves code readability and maintainability. Follow these conventions throughout the project.
src/
components/
user-profile/
auth-form/
services/
http-client.ts
types/
api-response.types.ts
| Type | Convention | Example |
|---|---|---|
| Components | PascalCase.tsx | UserProfile.tsx |
| Hooks | use-xxx.ts | use-auth.ts |
| Types | xxx.types.ts | api-response.types.ts |
| Schemas | xxx.schema.ts | user.schema.ts |
| Constants | xxx.constants.ts | routes.constants.ts |
| Utilities | kebab-case.ts | http-client.ts |
| Pages | page.tsx (Next.js) | app/dashboard/page.tsx |
| Layouts | layout.tsx (Next.js) | app/dashboard/layout.tsx |
// Variables
const userName = "John";
const isAuthenticated = true;
const maxRetryCount = 3;
// Functions
function getUserById(id: string) { ... }
function formatCurrency(amount: number) { ... }
async function fetchUserProfile() { ... }Use SCREAMING_SNAKE_CASE for true constants (compile-time values):
const MAX_FILE_SIZE = 5 * 1024 * 1024;
const API_VERSION = "v1";
const DEFAULT_PAGE_SIZE = 20;Use camelCase for runtime constants:
const defaultConfig = { ... };
const initialState = { ... };// Component definition
function UserProfile({ user }: UserProfileProps) { ... }
// Component usage
<UserProfile user={currentUser} />
<AuthForm onSubmit={handleAuth} />// Types
type UserId = string;
type ResponseType = "success" | "error" | "warning";
// Interfaces
interface UserProfile {
id: string;
email: string;
name: string;
}
interface ApiResponse<T> {
ok: boolean;
data: T | null;
}interface UserCardProps {
user: User;
onSelect?: (user: User) => void;
}
function UserCard({ user, onSelect }: UserCardProps) { ... }// Custom hooks
function useAuth() { ... }
function useLocalStorage<T>(key: string) { ... }
function useDebounce<T>(value: T, delay: number) { ... }// In component
function handleSubmit(event: FormEvent) { ... }
function handleUserSelect(user: User) { ... }
// Props
interface FormProps {
onSubmit: (data: FormData) => void;
onCancel: () => void;
}const isLoading = true;
const hasError = false;
const canEdit = user.role === "admin";
const shouldRefetch = data === null;<div className="flex items-center gap-4 p-4 bg-secondary rounded-lg">.hero-gradient {
background: var(--gradient-hero);
}
.card-neumorphic {
box-shadow: var(--shadow-neumorphic-dark);
}/api/auth/sign-in
/api/users/[id]
/api/products/featured
?pageSize=20&sortBy=createdAt&isActive=true
// File: src/components/user-card/UserCard.tsx
import { useState } from "react";
import { cn } from "@/lib/cn";
import type { User } from "@/types/user.types";
interface UserCardProps {
user: User;
isSelected?: boolean;
onSelect?: (user: User) => void;
}
const MAX_NAME_LENGTH = 50;
export function UserCard({ user, isSelected = false, onSelect }: UserCardProps) {
const [isHovered, setIsHovered] = useState(false);
const handleClick = () => {
onSelect?.(user);
};
const displayName = user.name.length > MAX_NAME_LENGTH
? `${user.name.slice(0, MAX_NAME_LENGTH)}...`
: user.name;
return (
<div
className={cn(
"p-4 rounded-lg",
isSelected && "ring-2 ring-primary",
isHovered && "bg-secondary/50"
)}
onClick={handleClick}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
<h3>{displayName}</h3>
<p>{user.email}</p>
</div>
);
}