A

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]);