Topic 2 of 15 · React Developer

React Structure

Lesson TL;DRTopic 2: React Structure 📖 6 min read · 🎯 beginner · 🧭 Prerequisites: introductiontoreact Why this matters Up until now, you've probably been dropping files wherever feels convenient — maybe everyt...
6 min read·beginner·react · project-structure · components · hooks

Topic 2: React Structure

📖 6 min read · 🎯 beginner · 🧭 Prerequisites: introduction-to-react

Why this matters

Up until now, you've probably been dropping files wherever feels convenient — maybe everything in one folder, or just guessing as you go. That works for a tiny experiment, but the moment your React app grows past a handful of components, you'll start losing track of what lives where. I've seen students spend more time hunting for files than actually writing code. The fix isn't working harder — it's setting up a clear folder structure from the start. Get that right early, and your app stays manageable no matter how big it gets.

What You'll Learn

  • Understand the default file tree generated by Create React App and what each folder does
  • Organize components by feature with co-located styles
  • Manage static assets (images, fonts, global CSS) in a dedicated assets/ folder
  • Create reusable custom hooks and context providers in their own directories
  • Separate service and utility functions from UI code, then wire everything together in App.js

The Analogy

Think of a React project like a well-run city hall. The public/ lobby holds documents anyone can walk in and grab (your index.html). The src/ wing is where all the real work happens — different departments (components, hooks, services) each occupy their own office, clearly labeled, so any developer who walks through the door can find what they need without asking for directions. Tossing everything into one giant room might work on day one, but by day thirty you're tripping over cables and losing files. Organized departments — organized code.

Chapter 1: The Default Create React App Structure

When you bootstrap a React project with Create React App, you get this starting point:

my-react-app/
├── node_modules/
├── public/
│   ├── index.html
│   └── ...
├── src/
│   ├── App.css
│   ├── App.js
│   ├── App.test.js
│   ├── index.css
│   ├── index.js
│   └── ...
├── .gitignore
├── package.json
├── README.md
└── yarn.lock
  • node_modules/ — installed packages; never touch this manually.
  • public/ — static files served as-is; index.html is the single HTML shell.
  • src/ — every line of React code you write lives here.
  • package.json — project metadata and dependency list.
  • yarn.lock (or package-lock.json) — dependency lockfile for reproducible installs.

The src/ folder is where all structure decisions are made. Out of the box it's flat — that works for a demo, not for a production application.

Chapter 2: Organizing Components by Feature

the trainer's first rule: components that belong together, live together. Group by feature, not by file type.

src/
├── components/
│   ├── common/
│   │   ├── Button.js
│   │   ├── Input.js
│   │   └── ...
│   ├── Home/
│   │   ├── Home.js
│   │   ├── Home.css
│   │   └── ...
│   ├── About/
│   │   ├── About.js
│   │   ├── About.css
│   │   └── ...
│   └── ...
  • common/ — shared, reusable UI primitives (buttons, inputs, modals) used across multiple features.
  • Feature folders (e.g., Home/, About/) — each contains the component, its styles, and any sub-components that belong exclusively to it.

src/components/Home/Home.js:

import React from 'react';
import './Home.css';

function Home() {
    return (
        <div className="home">
            <h1>Welcome to Vizag</h1>
        </div>
    );
}

export default Home;

src/components/Home/Home.css:

.home {
    text-align: center;
    padding: 20px;
}

Co-locating the CSS with its component means deleting Home/ removes both the markup and the styles — no orphaned CSS left behind.

Chapter 3: Organizing Assets

Static assets — images, fonts, and global stylesheets — belong in a dedicated assets/ folder inside src/.

src/
├── assets/
│   ├── images/
│   │   ├── logo.png
│   │   └── ...
│   ├── fonts/
│   │   ├── custom-font.ttf
│   │   └── ...
│   └── styles/
│       ├── variables.css
│       ├── mixins.css
│       └── ...

Import assets directly into your components using relative paths. Webpack (bundled into CRA) will resolve and fingerprint them at build time.

src/components/Home/Home.js with an image asset:

import React from 'react';
import './Home.css';
import logo from '../../assets/images/logo.png';

function Home() {
    return (
        <div className="home">
            <img src={logo} alt="Logo" />
            <h1>Welcome to Vizag</h1>
        </div>
    );
}

export default Home;

The import logo from '../../assets/images/logo.png' pattern gives you a hashed URL in production builds and a hot-reloaded path in development — no manual cache-busting needed.

Chapter 4: Organizing Hooks and Context

Custom hooks and context providers are logic, not UI. They get their own top-level directories inside src/.

Custom Hooks — src/hooks/

src/
├── hooks/
│   ├── useFetch.js
│   └── useLocalStorage.js

src/hooks/useFetch.js:

import { useState, useEffect } from 'react';

function useFetch(url) {
    const [data, setData] = useState(null);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState(null);

    useEffect(() => {
        const fetchData = async () => {
            try {
                const response = await fetch(url);
                const result = await response.json();
                setData(result);
                setLoading(false);
            } catch (error) {
                setError(error);
                setLoading(false);
            }
        };
        fetchData();
    }, [url]);

    return { data, loading, error };
}

export default useFetch;

useFetch encapsulates the useState + useEffect pattern for data fetching. Any component can call useFetch(url) and get back { data, loading, error } without duplicating the logic.

Context Providers — src/context/

src/
├── context/
│   ├── ThemeContext.js
│   └── AuthContext.js

src/context/ThemeContext.js:

import React, { createContext, useState } from 'react';

const ThemeContext = createContext();

export const ThemeProvider = ({ children }) => {
    const [theme, setTheme] = useState('light');

    return (
        <ThemeContext.Provider value={{ theme, setTheme }}>
            {children}
        </ThemeContext.Provider>
    );
};

export default ThemeContext;

Context files export both the context object (ThemeContext) and the provider component (ThemeProvider). This separation makes it easy to consume the context in deep child components without prop-drilling.

Chapter 5: Organizing Services and Utilities

Business logic that talks to external systems (APIs, auth) goes in services/. Pure helper functions with no side effects go in utils/.

Services — src/services/

src/
├── services/
│   ├── api.js
│   └── auth.js

src/services/api.js:

export const fetchData = async (endpoint) => {
    const response = await fetch(endpoint);
    if (!response.ok) {
        throw new Error('Network response was not ok');
    }
    const data = await response.json();
    return data;
};

Centralizing API calls in services/api.js means swapping fetch for axios — or changing a base URL — is a one-file edit, not a grep across every component.

Utilities — src/utils/

src/
├── utils/
│   ├── helpers.js
│   └── validators.js

src/utils/helpers.js:

export const capitalize = (str) => {
    if (!str) return '';
    return str.charAt(0).toUpperCase() + str.slice(1);
};

Pure functions like capitalize have no dependencies on React or the DOM, making them trivial to unit-test in isolation.

Chapter 6: Putting It Together — src/App.js

With every layer in its place, App.js becomes a clean composition root: import the providers, import the top-level route components, and assemble.

import React from 'react';
import './App.css';
import Home from './components/Home/Home';
import { ThemeProvider } from './context/ThemeContext';

function App() {
    return (
        <ThemeProvider>
            <div className="App">
                <Home />
            </div>
        </ThemeProvider>
    );
}

export default App;

The final src/ tree looks like this:

src/
├── assets/
│   ├── images/
│   ├── fonts/
│   └── styles/
├── components/
│   ├── common/
│   ├── Home/
│   └── About/
├── context/
│   ├── ThemeContext.js
│   └── AuthContext.js
├── hooks/
│   ├── useFetch.js
│   └── useLocalStorage.js
├── services/
│   ├── api.js
│   └── auth.js
├── utils/
│   ├── helpers.js
│   └── validators.js
├── App.css
├── App.js
└── index.js
graph TD
    App["App.js (root)"]
    TP["ThemeProvider (context/)"]
    Home["Home.js (components/Home/)"]
    Logo["logo.png (assets/images/)"]
    Hook["useFetch (hooks/)"]
    Api["api.js (services/)"]
    Util["helpers.js (utils/)"]

    App --> TP
    TP --> Home
    Home --> Logo
    Home --> Hook
    Hook --> Api
    App -.-> Util

🧪 Try It Yourself

Task: Extend the Vizag app with an About feature and a shared capitalize utility.

  1. Create src/components/About/About.js and src/components/About/About.css.
  2. In About.js, import capitalize from ../../utils/helpers and render:
    <h2>{capitalize('about example')}</h2>
    
  3. Import and render <About /> inside App.js alongside <Home />.

Success criterion: The browser shows both "Welcome to Vizag" (from Home) and "About example" with a capital A (from About + capitalize). The styles for each component come from their co-located CSS files.

Starter snippet for About.js:

import React from 'react';
import './About.css';
import { capitalize } from '../../utils/helpers';

function About() {
    return (
        <div className="about">
            <h2>{capitalize('about example')}</h2>
        </div>
    );
}

export default About;

🔍 Checkpoint Quiz

Q1. Why should reusable UI primitives (Button, Input) live in components/common/ rather than inside a feature folder like components/Home/?

A) Because CRA requires it by default B) So they can be imported by any feature without creating circular dependencies or deep relative paths C) To prevent Webpack from tree-shaking them D) Because CSS Modules only work from common/

Q2. Given this import in Home.js:

import logo from '../../assets/images/logo.png';

What does logo hold at runtime?

A) The raw binary of the PNG file B) A resolved URL string (local dev path or hashed production path) that can be used as src C) A React component wrapping an <img> tag D) undefined, because static files can't be imported in JSX

Q3. Look at this useFetch implementation. What value does the hook return when the request is still in flight?

return { data, loading, error };
// initial state: data=null, loading=true, error=null

A) { data: [], loading: false, error: null } B) { data: null, loading: true, error: null } C) { data: null, loading: null, error: null } D) { data: undefined, loading: undefined, error: undefined }

Q4. You're adding a currency formatting function used by three different feature components. Where should it live, and why?

A1. B — common/ components are app-wide primitives. Placing them inside a feature folder would either force other features to import across sibling boundaries (messy) or duplicate the component (worse).

A2. B — Webpack processes the import and returns a URL string. In development it's the local path; in a production build it's a content-hashed URL like /static/media/logo.abc123.png.

A3. B — Initial state sets loading to true and both data and error to null. They stay that way until the fetchData async function resolves or rejects inside the useEffect.

A4. src/utils/helpers.js — it's a pure, side-effect-free function with no React or API dependencies. utils/ is the correct home for shared, testable helper functions. services/ is reserved for functions that communicate with external systems.

🪞 Recap

  • Create React App gives you a flat src/ to start — it's your job to add deliberate structure as the app grows.
  • Group components by feature (e.g., components/Home/) and co-locate their CSS so they're easy to delete or move as a unit.
  • Static assets (images, fonts, global styles) belong in src/assets/ and are imported directly into components.
  • Custom hooks go in src/hooks/ and context providers go in src/context/ — both are logic, not UI.
  • External communication lives in src/services/; pure helper functions live in src/utils/.
  • App.js is the composition root — it wires providers and top-level components together without containing business logic itself.

📚 Further Reading

Like this topic? It’s one of 15 in React Developer.

Block your seat for ₹2,500 and join the next cohort.