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.
Separate responsibilities
Keep display components focused on their props.
Locate ownership
Make the source of changing data easy to find.
Grow intentionally
Split files when the boundary improves navigation and reuse.
Follow information through the application
Owner
App holds the student records.
List
StudentList controls repeated presentation.
Row
StudentRow describes one record.
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
Apply the folder decision
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.