Home/Blog/Programming/Five best practices for React developers
Best Practices for React Developers
Home/Blog/Programming/Five best practices for React developers

Five best practices for React developers

6 min read
May 19, 2025
content
React development best practices
Directory organization
Grouping by features
Grouping by file type
Components and separation of concerns
Handling state and props
State management
Props
Abstraction
Naming conventions
Wrapping up

Become a Software Engineer in Months, Not Years

From your first line of code, to your first day on the job — Educative has you covered. Join 2M+ developers learning in-demand programming skills.

Key takeaways:

  • Organize your React project files by feature (e.g., user, products) or file type (e.g., components, styles) to keep things tidy and scalable.

  • In React, divide your components into stateful (logic-driven) and presentational (UI-focused) to create a more organized and testable architecture.

  • Use setState (or Hooks) for state updates to keep your React state flow clean and minimize excessive prop drilling.

  • Reusable components are your best friend in React—build them to avoid code duplication and boost maintainability.

  • In React, follow naming conventions like PascalCase for components, camelCase for methods, kebab-case for CSS classes, and prefixes for custom hooks to keep your code crystal clear.

React is powerful but with great flexibility comes the potential for chaos. Ever struggled with a tangled directory structure, confusing state management, or components that feel more like spaghetti than clean code? You’re not alone. This post will cover five essential best practices to help you write cleaner, more scalable React apps.

The Road to React: The One with Class Components

Cover
The Road to React: The One with Class Components

If you’re working with a legacy React codebase, then this course will prove useful. React has seen a great deal of change over the years, but there might be situations where you have to work with an older version. In this course, you will learn the fundamentals of V15 React. You will learn how to work with an API, how to organize and test your code as well as how to give your React app state. Throughout this course, you will have hands-on experience building a fully functional Hacker News App. By the end of this course, you will be prepared to build your own applications and have something to showcase in your portfolio. If you’re looking for the updated version of this course featuring V16, please visit The Road to React: The one with Hooks.

13hrs
Beginner
55 Playgrounds
8 Quizzes

React development best practices#

Whether you’re building your first project or scaling an enterprise app, these strategies will help you write code you (and your team) will enjoy maintaining.

Note: Even though React is highly customizable and not opinionated, the strategies discussed here can help you develop efficient and clean React applications. Feel free to adapt these to your specific needs and team preferences.

Let’s delve into five essential areas that can transform how you approach React development.

Directory organization#

React doesn’t impose any specific structure, so you can organize your application however you like. However, the React documentation mentions two main strategies for organizing your code: grouping by features and file type. Let’s briefly review both approaches.

Grouping by features#

In this approach, your application is organized by its core features. The src directory is a core part of any React project, containing the App.js and index.js files. Subdirectories within src represent individual features.

For example, an e-commerce application could have directories like user, products, cart, checkout, etc.

src/
├── App.js
├── index.js
├── user/
│ ├── UserProfile.js
│ ├── UserContext.js
│ └── ...
├── products/
│ ├── ProductList.js
│ ├── ProductDetails.js
│ └── ...
├── cart/
│ └── ...
└── checkout/
└── ...
Directory organization: Grouping by features

Each directory contains that feature’s relevant components, context, and other resources. This structure can be further nested to include components, styling, and tests within each feature directory.

Grouping by file type#

Alternatively, you can organize your code by the type of files it contains, with separate directories for components, context, hooks, styles, and tests. Here’s an example of how that might look:

src/
├── App.js
├── index.js
├── components/
│ ├── UserProfile.js
│ ├── ProductList.js
│ └── ...
├── context/
│ └── UserContext.js
├── css/
│ └── ...
├── hooks/
│ └── ...
└── tests/
└── ...
Directory organization: Grouping by file type

The choice between these methods depends on your project’s specific needs. You might start with a simple structure for smaller applications and let it evolve organically as your application grows. The most important thing is to create a structure that facilitates easy navigation and understanding of your codebase.

For further front-end mastery, apply these JavaScript fundamentals in our React 18-based “Learn React” course to build dynamic, interactive user interfaces using the latest features and best practices of React 18.

Components and separation of concerns#

In React, it’s essential to separate logic from presentation. This is where the concept of smart and dumb components comes in. Stateful components manage state and handle logic, while presentational components receive props and render content based on that data.

// Smart component
const ProductContainer = () => {
const [products, setProducts] = useState([]);
useEffect(() => {
fetchProducts().then(setProducts);
}, []);
return <ProductList products={products} />;
};
// Dumb component
const ProductList = ({ products }) => (
<ul>
{products.map(product => (
<li key={product.id}>{product.name}</li>
))}
</ul>
);

Functional components can handle states with React Hooks and the Context API, so deciding where to separate concerns is important. With hooks, every component can handle state—but that doesn’t mean it should. For example, A Button component shouldn’t track API calls.

  • Container components should manage state/data fetching (e.g., ProductDataFetcher).

  • Presentational components should focus on UI (e.g., ProductCard).

It’s best to keep stateful, logic-heavy components separate from simple, presentational components. This improves code readability and makes testing easier. Even with hooks, this separation remains valuable for complex components.

Handling state and props#

React’s data flow is key to understanding how your app functions. There are two primary ways to manage data: state and props.

State management#

Directly mutating state is a common pitfall. Always update the state using the setState() method in class components.

this.setState({ count: this.state.count + 1 });

With React Hooks, use the setter function provided by the useState method:

const [count, setCount] = useState(0);
setCount(count + 1);

Props#

When passing state down through multiple layers of components, props drilling can become cumbersome and difficult to manage. It’s best to avoid passing props too deeply through the component tree. Instead, use state management solutions like Redux or Context API to handle global state more cleanly.

Common pitfall: Passing props through five layers of components? It’s messy and hard to maintain. Use the Context API or a state manager like Redux to avoid this trap.

Abstraction#

Abstraction is a fundamental concept in React that encourages the reusability of components. You can reduce duplication and enhance maintainability by breaking your application down into smaller, reusable components.

For instance, the button components can be abstracted into separate components in a simple counter application. This allows you to reuse the button component wherever needed without rewriting the same logic.

Example: In the following example, each button’s functionality is implemented separately, leading to duplicated code:

import React, { useState } from 'react';
function App() {
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>Increment</button>
<button onClick={() => setCount(count - 1)}>Decrement</button>
<button onClick={() => setCount(0)}>Reset</button>
</div>
);
}
Button’s functionality before abstraction

By abstracting the button into its component, we eliminate duplication and simplify the code:

import React, { useState } from 'react';
// Abstracted Button Component
const Button = ({ label, onClick }) => <button onClick={onClick}>{label}</button>;
function App() {
const [count, setCount] = useState(0);
const handleIncrement = () => setCount(count + 1);
const handleDecrement = () => setCount(count - 1);
const handleReset = () => setCount(0);
return (
<div>
<Button label="Increment" onClick={handleIncrement} />
<Button label="Decrement" onClick={handleDecrement} />
<Button label="Reset" onClick={handleReset} />
</div>
);
}
Button’s functionality after abstraction

The goal of abstraction is to make components as reusable as possible, which makes your application more modular and easier to manage.

Naming conventions#

Proper naming conventions are essential for consistency and clarity in your React codebase. Here are some standard practices:

  • Components: Components should use PascalCase (e.g., MyComponent) and be named based on their function rather than their application-specific feature. This ensures that the component name remains relevant if you refactor or change the feature.

  • Methods: Methods and event handlers should follow camelCase and be named according to their function (e.g., handleClick), not the feature they belong to.

  • Keys: Keys for list items should be unique and stable, avoiding using array indexes as keys. Instead, combine unique object properties: key={item.id}

  • Custom hooks: By convention, custom hooks are named using camelCase and prefixed with use, such as useProducts, useCartState, etc. This helps distinguish them from regular functions.

  • CSS classes: In React projects, CSS class names typically follow the kebab-case convention, where hyphens separate words, and all letters are lowercase. For example: product-card, cart-item, etc.

Below is a quick reference guide summarizing best practices for naming various coding elements:

Item

Convention

Example

Components

PascalCase

ProductCard

Methods

camelCase

handleClick

Custom Hooks

Prefix with use

useProductList

CSS classes

kebab-case

product-card

List keys

Unique and stable

key={product.id}

Wrapping up#

These best practices serve as a foundation for writing maintainable React applications, but remember that the best code is the one that works for your team and project. As you continue developing with React, you’ll discover which patterns work best for your needs.

Remember: React doesn’t care how you pass data around, it just cares that you do.

The developers who edit and read your code later will need to be able to decipher and improve upon your contributions.

Cover
React Deep Dive: From Beginner to Advanced

The primary goal of this course is to give you an excellent introduction to React and its ecosystem, tackle advanced features, and teach you as many best practices as possible. Furthermore, this course digs deeper into the most popular, latest, and straight forward ways to use React.

16hrs 40mins
Beginner
4 Challenges
23 Quizzes

React mastery takes practice. Start by applying these best practices in your next project. Experiment with organizing directories, refining component logic, and tightening your naming conventions. If you’re ready to level up further, check out Educative’s React Deep Dive course for hands-on challenges and advanced insights.

Happy learning!


Frequently Asked Questions

What is the best practice for React state updates?

The best practice for updating the state in React is to use the state updater function when the new state depends on the previous state. This ensures that updates are based on the latest state and prevents issues due to asynchronous updates.

  • Use functional updates with setState or useState: This approach ensures that even if multiple updates are queued, each update gets the latest state.

    setCount(prevCount => prevCount + 1);
    
  • Avoid directly mutating the state: Instead of modifying the state object directly (which won’t trigger a rerender), always return a new object or array when updating the state.

    //  Incorrect
    state.items.push(newItem); // This mutates the existing state  
    setItems(state.items); // Won't trigger re-render
    
    // Correct
    setItems(prevItems => [...prevItems, newItem]); // Creates a new array  
    
  • Batch updates when necessary: React may batch multiple state updates in one render to optimize performance. If multiple state updates rely on the same event, it is best to combine them when possible.

  • Use useReducer for complex state logic: If your component’s state logic is complex, consider using useReducer instead of useState to manage the state more predictably.

Can I learn React in 7 days?

Yes, you can learn the basics of React in 7 days, but mastering React takes more time.

While 7 days is enough to get started, becoming proficient with React requires practice, real-world projects, and understanding advanced concepts like Context API, React Router, and performance optimizations.

Which is the best practice for React Hooks usage?

To use React Hooks effectively, follow these best practices:

  • Use Hooks at the top level: Never call hooks inside loops, conditions, or nested functions. Hooks must always run in the same order.

    // Incorrect
    if (someCondition) {
      const [count, setCount] = useState(0);
    }
    
    // Correct
    const [count, setCount] = useState(0);
    if (someCondition) {
      // Do something
    }
    
  • Follow the dependency array rules for useEffect: Ensure dependencies in useEffect are correctly listed to avoid unnecessary re-renders or stale closures.

    useEffect(() => {
      console.log(user);
    }, [user]); // Runs only when 'user' changes
    
  • Use useMemo and useCallback for performance optimization: Memoize expensive calculations and functions to prevent unnecessary re-renders.

    const memoizedValue = useMemo(() => computeExpensiveValue(data), [data]);
    const memoizedCallback = useCallback(() => doSomething(param), [param]);
    
  • Use useReducer for complex state logic: If state transitions involve multiple steps, useReducer provides better organization than useState.

  • Extract custom hooks for reusable logic: Instead of duplicating logic, extract reusable behavior into a custom hook.

    function useFetchData(url) {
      const [data, setData] = useState(null);
      useEffect(() => {
        fetch(url).then(response => response.json()).then(setData);
      }, [url]);
      return data;
    }
    

Following these best practices allows you to write cleaner, more efficient, and maintainable React code.


Written By:
Christina Kopecky

Free Resources