ProgramGuru

Organize a React Project

Organize a React Project

A school report begins in one file, then gains a student row, filter, form and storage connection. Locating a mark correction becomes difficult when every responsibility is mixed into one large component.

Project organization groups related code and defines how files depend on each other. A module exports values that other modules import. Useful boundaries follow responsibilities and change patterns, rather than a required number of folders.

  1. Separate responsibilities

    Keep display components focused on their props.

  2. Locate ownership

    Make the source of changing data easy to find.

  3. Grow intentionally

    Split files when the boundary improves navigation and reuse.

Follow information through the application

  1. Owner

    App holds the student records.

  2. List

    StudentList controls repeated presentation.

  3. Row

    StudentRow describes one record.

Read this relationship alongside the complete code. This diagram explains the flow; it does not execute React.

Begin with visible responsibilities

App owns the student records and correction handler. StudentList receives the records and maps them to rows. StudentRow only describes one record. These boundaries work even while everything is in one file, making them easier to understand before introducing imports.

Separating presentation from ownership helps a reader answer two questions: where does the value come from, and where can it change? A row should not create its own duplicate of the full student array. React’s importing and exporting guide explains how these component boundaries become file boundaries.

A complete application with three responsibilities

The example remains a single runnable App.jsx. Correct Maya creates a new array and replaces Maya’s object while preserving Ravi’s entry. The list and row receive the latest records through props. Their separate functions make the data flow visible without extra project setup.

Stable student IDs provide keys. The display components do not need to know whether records came from a local array or a future server request, provided the parent supplies the agreed record shape.

React · JavaScript with JSX

import React, { useState } from 'react';
function StudentRow({ student }) {
  return <li>{student.name}: {student.mark}</li>;
}
function StudentList({ students }) {
  return <ul>{students.map(student => <StudentRow key={student.id} student={student} />)}</ul>;
}
export default function App() {
  const [students, setStudents] = useState([
    { id: '007', name: 'Maya', mark: 35 },
    { id: '009', name: 'Ravi', mark: 28 }
  ]);
  function correctMaya() {
    setStudents(previous => previous.map(student =>
      student.id === '007' ? { ...student, mark: 37 } : student));
  }
  return <main><h1>School report</h1><StudentList students={students} />
    <button onClick={correctMaya}>Correct Maya</button></main>;
}

Expected screen and behavior

Initially: School report, Maya: 35, Ravi: 28 and Correct Maya.
Correct Maya changes only Maya’s displayed mark to 37.

Turn a boundary into an export

When StudentRow grows, move its function to StudentRow.jsx and export it. StudentList.jsx imports that component and exports the list. App.jsx imports the list. The arrow of dependency goes from the owner toward the presentation it uses; StudentRow does not need to import App.

The following two-file excerpt illustrates the import contract, not another independently runnable App. Both named files must exist in a configured React project. A default export is imported without braces; named exports use braces. Mixing those styles causes a loading error before the row can render.

// StudentRow.jsx
export default function StudentRow({ student }) {
  return <li>{student.name}: {student.mark}</li>;
}
// StudentList.jsx
import StudentRow from './StudentRow.jsx';
export default function StudentList({ students }) {
  return <ul>{students.map(student => <StudentRow key={student.id} student={student} />)}</ul>;
}

Choose a layout that answers useful questions

A small project might contain src/App.jsx and a src/students folder containing StudentList.jsx, StudentRow.jsx and studentRules.js. The first two describe screens; studentRules.js can hold plain calculations that need no React rendering. Related tests can live nearby or in a consistent tests folder.

This is an example layout, not a mandatory framework. Avoid moving a three-line function into several files solely to imitate a larger application. As features grow, organizing by students, attendance and reports can keep each feature’s code together. Shared components should become shared because multiple features need the same contract, not because their names sound general.

Worked practice: show the number of records

Add a count to StudentList without creating another state variable. The complete answer derives the count from its students prop. Correct Maya must change the mark while the count stays two.

The derived value belongs next to the list presentation. Maintaining an independent count would require every addition and removal to synchronize two values. A component boundary should clarify a responsibility, not add unnecessary state.

React · JavaScript with JSX

import React, { useState } from 'react';
function StudentRow({ student }) {
  return <li>{student.name}: {student.mark}</li>;
}
function StudentList({ students }) {
  return <section><p>Students: {students.length}</p><ul>{students.map(student => <StudentRow key={student.id} student={student} />)}</ul></section>;
}
export default function App() {
  const [students, setStudents] = useState([
    { id: '007', name: 'Maya', mark: 35 },
    { id: '009', name: 'Ravi', mark: 28 }
  ]);
  function correctMaya() {
    setStudents(previous => previous.map(student =>
      student.id === '007' ? { ...student, mark: 37 } : student));
  }
  return <main><h1>School report</h1><StudentList students={students} />
    <button onClick={correctMaya}>Correct Maya</button></main>;
}

Expected screen and behavior

Initially: Students: 2 and both records.
Correct Maya changes her mark; Students: 2 remains.

Keep imports and ownership understandable

Circular imports occur when modules depend back on each other. They can make initialization and responsibility harder to follow. Move a shared rule into a lower-level module when both screens need it, rather than importing one screen into another merely to access its helper.

Splitting files does not make data private or persistent. Browser bundles can include imported client code and data. Keep secrets on the server, and make saving an explicit operation rather than a surprising import-time effect. Use consistent path spelling and verify on the actual deployment system, where filename case can differ from your laptop’s behavior.

Check the display boundary

Which responsibility belongs in StudentRow here?
Choose one answer. You can try again.

Answer and explanation

Describing the supplied student’s name and mark.

Correct. The row is a focused presentation component.

Apply the folder decision

When is moving a component into another file most useful?
Choose one answer. You can try again.

Answer and explanation

When its coherent responsibility, size or reuse makes the boundary helpful.

Correct. The split should improve reading and maintenance.

Organize around responsibility and data flow

Define focused components, keep state ownership visible and use module exports when file boundaries help. Group related work consistently, avoid circular dependencies and derive values from their actual owner. A clear small project can grow without adopting unnecessary layers.

Connect this with component composition, immutable array updates and tests of the resulting behavior.

Reaching the recap and completing the quizzes are separate milestones.

    Next lesson: React with TypeScript →