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.