As a software engineer with over a decade of experience building complex web applications, I’ve found that implementing proper code organization and project structure principles in React is key to managing app complexity and scale. After leading multiple development teams and working on dozens of React projects, I’ve identified core React JS best practices that lead to more maintainable codebases that can grow smoothly over time.
In this article, I will share universal React project structure guidelines and Javascript code organization patterns I’ve found to work extremely well for organizing React components, managing styles, state, routing, and more. I’ll support these recommendations with real-world code examples written using modern React + Typescript conventions.
Whether you’re kicking off a new React codebase or looking to improve an existing one, applying these structural React JS best practices will set you up for long-term efficiency and success.
Logical Directory Hierarchy
The first foundation for any React app is to split out files and responsibilities into a logical, hierarchical directory structure. Here is an optimal universal structure I’ve refined over years of React development:
src └─── app/ │ ├─── assets/ │ ├─── common/ │ ├─── components/ │ ├─── config/ │ ├─── hooks/ │ ├─── models/ │ ├─── routes/ │ └─── utils/ └─── services/ └─── store/
This structure ensures distinct separation of responsibilities with a modular, reusable approach:
- assets – Images, fonts, static files
- common – Shared components like buttons and icons
- components – Core app-specific components
- config – Environment configs
- hooks – Custom hook logic
- models – Type interfaces/classes
- routes – Route definitions
- utils – Helpers and utilities
- services – Data services
- store – State management
Nailing the basic directory outline sets a scalable foundation for growth.
Modular Components
With React’s component-based architecture, the components folder is the app’s core. Here is where all UI components live, which should be:
1. Small and Focused
// Good const SearchBar = () => { return ( <input /> <button>Search</button> ) } // Bad const AllSiteHeader = () => { // Displays search bar, nav links, auth buttons // Too many responsibilities }
2. Reusable
Components should be decoupled and parameterized to enable reuse in multiple scenarios.
// Reusable List interface ListProps { data: any[], renderItem: (item: any) => ReactNode } const List = ({data, renderItem}: ListProps) => ( <> {data.map(renderItem)} <> ) const ProductList = () => { const products = []; return ( <List data={products} renderItem={(product) => <ProductItem product={product} /> } /> ) }
3. Independent
Avoid excessive component coupling and aim for independent components with defined contracts via PropTypes and interfaces.
Applying these component principles results in a lean, modular UI architecture far easier to maintain at scale.
Strict State Management
Another key React architecture decision is how to manage state – one of the hardest parts of building apps. My recommendation after years of Refactoring React apps gone wild is this strict rule – keep ALL component state lifted up and managed by a central store with hooks, and restrict component state to UI state only.
Concretely, leverage global state management with React Query, Redux Toolkit or Recoil to manage data, domain state, and app state in a structured, scalable way outside UI components.
// components/ const UserProfile = () => { // Good - manage UI state only const [menuOpen, setMenuOpen] = useState(false) // Bad const [user, setUser] = useState({}) } // store/userSlice const userSlice = createSlice({ name: 'user', initialState: {}, reducers: {} }) export const userActions = userSlice.actions; export const userState = userSlice.state;
This simplifies components, isolates state changes, and centralizes state managament for easier debugging.
For caching, prefetching, performance, React Query also shines for handling data fetching in components without pollution:
// User component using React Query function UserProfile() { const { data } = useQuery('user', fetchUser) return <h1>{data.name}</h1> }
Additional React js Best Practices
Other architecture React js best practices worth calling out:
- Styles – Styled Components over CSS/SASS for encapsulation
- Routing – Centralized routes configuration
- Error Handling – Global error boundaries
- Forms – Shared custom hooks for form logic
- Tests – Unit and integration tests for core flows
- TypeScript – Strict typing for safety/dev DX
There are more app-specific architecture decisions around performance, libraries, DevOps and infrastructure configuration. But in my experience, if you nail the fundamentals – hierarchical structure, modular components, centralized state management – building stable production Reactjs applications becomes much more straightforward.
Reach Out to the Experts
I hope these code with React structure tips help you build more scalable apps going forward. With over a decade of experience as an independent developer, I’ve accumulated extensive expertise in creating react app and across the entire web application stack – from initial architecture to secure cloud infrastructure configuration.
Related Articles
March 6, 2024
Setup WhatsApp Business API with Meta: A Step-by-Step Guide
Learn how to seamlessly integrate WhatsApp Business API with Meta, unlocking…
February 29, 2024
Power of React Lazy Loading for React Performance
React lazy loading defers the loading of non-essential components and assets…
February 28, 2024
Unleashing the Power of React Code Splitting
React code splitting improves page load times by splitting code into separate…