Introduction: From Backend-Ready to a Real Frontend

Welcome back! 🎉 At this point, your Task Manager backend is complete and production-ready. All APIs live under src/app/api/**, and from here on out, we treat that backend as read-only. In these final courses, our entire focus shifts to the frontend UI—how users actually experience and interact with the system you’ve already built.

In this lesson, you’ll take a blank Next.js app and progressively turn it into a real, navigable Task Manager interface. You’ll start by setting up global styling and a dashboard entry point, then add shared navigation, and finally connect the UI to live backend data. Along the way, you’ll practice giving clear, scoped Codex prompts that modify only the intended frontend files.

How This Lesson Fits Together

This lesson is structured around three small but meaningful frontend milestones:

  • Establishing a styled application shell with Tailwind and a dashboard home page
  • Introducing a shared dashboard layout with navigation and stable routes
  • Fetching real task data from the existing API and rendering it in the UI

Each practice builds directly on the previous one, and each change happens only in frontend files. Any file under src/app/api/** already works and must not be edited.

A Clear Boundary: Frontend vs Backend

Before we dive in, it’s important to internalize one rule that applies to every practice in this lesson:

  • All backend logic lives in src/app/api/**.
  • From now on, you will not modify anything in that directory.

For example, src/app/api/tasks/route.ts already defines a working API.

  • This endpoint returns data in the shape { data: Task[], meta: { ... } }.
  • The frontend’s job is simply to consume this API, not change it.
  • All UI work happens in src/app/layout.tsx and src/app/(dashboard)/**.

Keeping this boundary clear mirrors how real teams work and makes your Codex prompts much safer and more predictable.

Root layout and Tailwind setup

This code lives in src/app/layout.tsx. It defines the global HTML shell for your whole Next.js app—everything you render will be placed inside this layout. In this environment, Tailwind is provided via a CDN script, so this file is where we enable styling and set a clean default background/text so every page looks “app-like” immediately.

RootLayout is a special Next.js App Router component that wraps every page in your application. That’s why it’s the right place to load global resources and apply default styling—every route will inherit it automatically.

The Tailwind CDN script is required in this sandbox environment so utility classes (like bg-gray-50) actually work. In a typical production app you’d install Tailwind via npm, but here the CDN keeps setup lightweight and predictable.

The <body> classes create a consistent baseline look:

  • min-h-screen prevents short pages from looking cut off
  • bg-gray-50 and text-gray-900 create a clean, readable default theme
The dashboard home page

This code lives in src/app/(dashboard)/page.tsx. It’s the first real UI entry point: a simple dashboard page with a heading, a short explanation, and a link to the Tasks page. We keep it intentionally small so you can quickly confirm routing and Tailwind styling before adding real data and interactivity.

  • This React component is a page component in the App Router, meaning the file path determines the route. Because it’s src/app/(dashboard)/page.tsx, it renders at / (inside the (dashboard) route group).
  • Link from Next.js enables client-side navigation. Instead of doing a full page refresh like a normal <a> tag, Next.js uses this to keep navigation fast and app-like—this becomes more important as your UI grows.
  • The layout and spacing (p-6, space-y-4) make the UI readable immediately. Later, when the dashboard becomes data-driven, these spacing patterns help keep sections visually separated without introducing lots of custom CSS.
Creating a Shared Dashboard Layout with Navigation

A shared layout for every dashboard page lives in src/app/(dashboard)/layout.tsx. Route-group layouts in Next.js wrap every page inside that group—so once we create a dashboard layout, the dashboard home page, tasks page, and filter page can all share the same navigation and spacing automatically.

  • A layout component is like a frame around your pages. The children prop represents whatever page is currently active, and Next.js renders it inside the layout automatically.
  • The <nav> uses Link for fast navigation across routes. These are real URLs (/, /tasks, /tasks/filter?completed=false) so you can click around immediately and confirm routing is correct.
  • The max-w-5xl centered container gives the UI a dashboard feel and prevents content from stretching edge-to-edge on wide screens.
Placeholder pages for navigation targets

These files live at src/app/(dashboard)/tasks/page.tsx and src/app/(dashboard)/tasks/filter/page.tsx. Right now they’re placeholders—intentionally. Navigation should work immediately, and each route should exist before you add complexity.

This route proves that /tasks renders correctly and that the shared dashboard layout wraps it as expected. Starting with placeholders avoids overwhelming complexity and lets you verify routing and layout before adding data fetching.

This page exists so /tasks/filter doesn’t 404. Even as a placeholder, it establishes the URL shape and keeps the app coherent and navigable.

Later, this route will read query parameters like ?completed=false and fetch filtered data.

Connecting the UI to Live Task Data

Once your app has structure (layout and pages), the next step is making it feel real by rendering backend data.

The API already exists and returns tasks wrapped in a consistent response shape:

  • GET /api/tasks{ data: Task[], meta: { timestamp: ... } }

Your UI needs to:

  • fetch from /api/tasks
  • read the array from json.data
  • handle loading and error states
  • render empty states when there are no tasks
Fetching tasks on the dashboard

This code lives in src/app/(dashboard)/page.tsx. We convert the page into a client component so it can use hooks, then fetch tasks and derive useful UI: counts and a “Recent Tasks” list.

  • 'use client' tells Next.js this page runs on the client, which is required for hooks like useEffect and useState. Without it, Next.js treats the file as a server component and the hooks would be invalid.
  • Task is imported as a type from @/lib/tasks, which keeps your UI aligned with backend data shape (id/title/content/completed/dueDate). This helps prevent “guessing” fields and keeps your UI consistent with the project’s single source of truth.
  • StatCard is a small reusable React component. It’s just a function that returns JSX, and we’ll use it to render Total/Completed/Incomplete in a consistent style without repeating markup.

Now the page component that fetches and renders:

Fetching and rendering the full task list

This code lives in src/app/(dashboard)/tasks/page.tsx. It’s similar to the dashboard fetch, but the UI goal is different: show the entire list. Reusing the same fetch pattern across pages is a good sign—you’re building consistent UI behavior.

  • This page is also a client component, for the same reason as the dashboard: it needs hooks for fetching and state. Keeping both pages consistent makes the app easier to maintain as it grows.
  • The fetch logic is intentionally similar to the dashboard. In real projects you might extract a shared hook later, but early on it’s helpful to keep things explicit so you understand exactly what’s happening.
  • The list UI is “structure first”. Right now it shows title + status; later you’ll enrich rows with links, actions, and possibly inline updates, but this simple rendering proves data flow is correct.
Writing Strong Codex Prompts

The quality of your experience with Codex depends heavily on how you write prompts. In this project, you’re practicing a pattern you’ll use constantly in real work: tight scope + explicit expectations.

A strong Codex prompt for this lesson usually includes:

  • A strict file allowlist, like “Modify ONLY these files…”
  • Clear implementation requirements, like “fetch from /api/tasks using useEffect”
  • The backend response rule, like “parse the array from json.data”
  • A safety constraint, like “Do NOT edit anything in src/app/api/**”
  • A completeness request, like “Show the full updated contents of each modified file”

This isn’t just “prompting etiquette”—it’s how you prevent accidental changes and keep the codebase stable while iterating quickly.

Recap

By the end of this lesson, you’ve built the foundation of a real Task Manager UI:

  • Global styling and a clean default layout via src/app/layout.tsx
  • A dashboard entry page that anchors the experience
  • A shared dashboard layout with navigation and stable routes
  • Live task data rendered on / and /tasks, including loading/error/empty handling

From here, you’ll keep expanding this UI in a controlled, professional way—always consuming the backend as-is, and always keeping your Codex prompts scoped to the frontend files you actually intend to change.

Sign up
Join the 1M+ learners on CodeSignal
Be a part of our community of 1M+ users who develop and demonstrate their skills on CodeSignal