React Best Practices Every Developer Should Follow
2025-07-31
React has come a long way, and so have the expectations for writing good React code. Whether you're building a side project or working on a large production app, here are some best practices that will make your codebase cleaner, more scalable, and easier to maintain.
// 1. Keep Components Small and Focused
// Avoid
function Dashboard(): JSX.Element {
// too much going on here
return <></>;
}
// Better
function DashboardHeader(): JSX.Element {
return <h1>Header</h1>;
}
function DashboardStats(): JSX.Element {
return <div>Stats</div>;
}
function Dashboard(): JSX.Element {
return (
<>
<DashboardHeader />
<DashboardStats />
</>
);
}
// 2. Prefer Functional Components and Hooks
import { useState, useEffect } from "react";
function Profile(): JSX.Element {
const [user, setUser] = useState<{ name: string } | null>(null);
useEffect(() => {
fetch("/api/user")
.then((res) => res.json())
.then(setUser);
}, []);
return <div>{user ? user.name : "Loading..."}</div>;
}
// 3. Use Meaningful Names
// Bad
function Stuff(): JSX.Element {
return <></>;
}
// Good
function UserProfileCard(): JSX.Element {
return <div>User Profile</div>;
}
// 4. Keep Props Simple
type ProfileProps = {
user: { name: string };
onUpdate: () => void;
};
// Bad
<Profile
user={user}
fetchUser={fetchUser}
onUserUpdate={onUserUpdate}
theme={theme}
config={config}
/>;
// Good
<Profile user={user} onUpdate={onUserUpdate} />;
// 5. Co-locate Files and Logic
// Suggested folder structure:
// /components
// /Profile
// index.tsx
// styles.module.css
// useProfile.ts
// Profile.test.tsx
// 6. Extract Reusable Logic into Custom Hooks
function useUser(): { name: string } | null {
const [user, setUser] = useState<{ name: string } | null>(null);
useEffect(() => {
fetch("/api/user")
.then((res) => res.json())
.then(setUser);
}, []);
return user;
}
// 7. Use TypeScript
type User = {
id: number;
name: string;
};
function ProfileTS({ user }: { user: User }): JSX.Element {
return <p>{user.name}</p>;
}
// 8. Avoid Inline Functions in JSX (when possible)
// Avoid
<button onClick={() => handleClick(id)}>Click</button>;
// Better
const handleClickWithId = () => handleClick(id);
<button onClick={handleClickWithId}>Click</button>;
// 9. Use Suspense and Lazy Loading
import React, { Suspense } from "react";
const ChatWidget = React.lazy(() => import("./ChatWidget"));
function App(): JSX.Element {
return (
<Suspense fallback={<div>Loading...</div>}>
<ChatWidget />
</Suspense>
);
}
// 10. Write Tests
import { render, screen } from "@testing-library/react";
test("shows loading initially", () => {
render(<Profile />);
expect(screen.getByText(/loading/i)).toBeInTheDocument();
});
// 11. Optimize with memo, useMemo, and useCallback
import { useMemo } from "react";
const expensiveValue = useMemo(() => computeHeavyThing(data), [data]);