Skip to content

Building Components

A well-structured React app is made of many small components, each responsible for one thing. The challenge is deciding where to draw the lines — what should be its own component versus what should stay inline.

Each component should do one thing clearly. A CategoryCard renders a single budget category. A TransactionList renders a list of transactions. An IncomeRow renders a single income source. These are easy to test, easy to read, and easy to reuse.

A component doing too much — rendering a form, managing a list, and handling navigation all at once — becomes hard to follow and hard to change. When a component function grows beyond ~80 lines, it is usually doing too much.

In ZeroBudget, the income section shows a list of income sources and a form to add new ones. Here is a minimal version:

export default function IncomeSection() {
return (
<section className="income-section">
<h2>Income</h2>
<ul>
<li>Paycheck — $3,500</li>
<li>Freelance — $800</li>
</ul>
</section>
);
}

This renders static data. It has one job: display the income section. The data is hardcoded for now — props will make it dynamic in the next lesson.

Every component file exports one component as the default export:

export default function IncomeSection() { ... }

Or equivalently:

function IncomeSection() { ... }
export default IncomeSection;

Use the first form — it is shorter and keeps the name visible at the top of the file. Named exports (export function) are reserved for files that export utilities or multiple small helpers.

To use a component in another file, import it:

import IncomeSection from './components/IncomeSection/IncomeSection';
function App() {
return (
<main>
<IncomeSection />
</main>
);
}

The import path does not need the .jsx extension — Vite resolves it automatically. Use relative paths from the importing file.

Start sketching ZeroBudget’s component tree. The app needs:

  • App — root, holds layout
  • MonthNav — month navigation bar
  • IncomeSection — income list + add form
  • LeftToAssign — the hero number showing unassigned money
  • CategoryCard — one card per spending category
  • TransactionForm — global form to add a transaction
  • TransactionList — list of transactions inside a category card

Build each as an empty placeholder first so the structure is visible before the logic arrives:

export default function LeftToAssign() {
return <div className="left-to-assign">$0.00 Left to Assign</div>;
}
  1. Create src/components/IncomeSection/IncomeSection.jsx with a component that renders a <section> containing an <h2>Income</h2> and a hardcoded <ul> with two income items.
  2. Create src/components/LeftToAssign/LeftToAssign.jsx with a placeholder that renders a <div> showing $0.00 Left to Assign.
  3. Import and render both components in App.jsx.
  4. Confirm both appear in the browser.
  • Each component should have one clearly defined responsibility.
  • Files use PascalCase names and a default export matching the component name.
  • Import components with relative paths — Vite resolves the .jsx extension automatically.
  • Build placeholder components first to establish structure before adding logic.