Make the interface a system
React encourages you to compose a page from focused components rather than treat it as one large template. You will work with component boundaries, properties and state so repeated interface patterns remain consistent and changes stay understandable.
Choose how the page reaches the browser
Gatsby and Next.js provide ways to build and render modern web experiences. Server-side rendering and static generation describe different moments when page output is prepared. Understanding those options helps you connect the needs of a page to an appropriate rendering approach instead of choosing a framework feature by habit.
Connect the view and ship it
A frontend becomes an application when its interface connects to real services and handles the states those services create. The stage then introduces publishing through Cloudflare or Vercel, so the work moves from a local development view to a web address people can use.
From concept to working code
These short excerpts show how the ideas in each stage appear in real projects. They are starting points to read, question, adapt and test—not code to paste without understanding.
Compose reusable interface pieces
ProgramCard owns the markup for one program, while ProgramList owns the collection and its heading. Passing data through props avoids copying the same card markup and gives each repeated item a stable key.
function ProgramCard({ title, duration, href }) {
return (
<article className="program-card">
<p className="program-card__duration">{duration}</p>
<h2>{title}</h2>
<a href={href} aria-label={`Explore the ${title} program`}>
View program <span aria-hidden="true">→</span>
</a>
</article>
);
}
export function ProgramList({ programs }) {
return (
<section aria-labelledby="programs-title">
<h1 id="programs-title">Choose a learning path</h1>
<div className="program-grid">
{programs.map((program) => (
<ProgramCard key={program.id} {...program} />
))}
</div>
</section>
);
}Fetch data where the page is rendered
This App Router page loads its data in an async Server Component. A five-minute revalidation window suits content that changes occasionally; an explicit error check prevents a failed API response from being presented as valid data.
async function getPrograms() {
const response = await fetch("https://api.example.com/programs", {
next: { revalidate: 300 },
});
if (!response.ok) throw new Error("Programs could not be loaded");
return response.json();
}
export default async function ProgramsPage() {
const programs = await getPrograms();
return (
<main>
<h1>Programs</h1>
<ProgramList programs={programs} />
</main>
);
}Read it, then make it yoursChange one requirement, predict what should happen, then run the code and check your prediction. In review, be ready to explain the trade-off you chose and one improvement you would make next.