Forms and API Basics
Introduction: Welcome to Part 2 — Real CRUD Starts Here
Welcome to Vibe Coding the Frontend: Task Manager CRUD & UX Polish (Part 2) 🎉
In Part 1, you built a strong UI foundation. You now have a dashboard shell with navigation, reusable components like TaskRow and Button, URL-driven filtering, polished stub routes, and a lightweight toast system that already makes the app feel more interactive.
In Unit 1: Forms and API Basics, you’ll start building the pieces that make real CRUD flows clean and consistent. Instead of jumping straight into large create and edit pages, you’ll first build the reusable primitives those pages depend on. That includes a reusable Input primitive with label, error handling, and ref forwarding, a reusable TaskForm that defines the shape and validation of task data, and a small API client wrapper so fetch(...) logic is not scattered across the codebase.
These building blocks matter because the next two units will use them repeatedly. Once they exist, you can implement full task creation and editing more cleanly, and then move into delete flow and UX polish without rewriting form and request logic in every page. This is the same philosophy you used in Part 1: build small reusable pieces first, then use them to make the app feel real.
Previously: Where We Left Off (End of Part 1)
In the previous unit, you let the URL drive the UI. Filter buttons updated /tasks/filter?completed=true|false, the filter page validated query params and rendered filtered lists using TaskRow, stub routes were upgraded so they felt intentional instead of broken, and a toast system added friendly feedback across the dashboard.
That matters because Part 2 keeps the same overall philosophy. You are still aiming for small reusable primitives, predictable data flow, and clear boundaries between frontend and backend. The frontend consumes src/app/api/**, but it does not rewrite it. The goal is not just to make CRUD “work,” but to make it work in a way that stays organized as the project grows.
Practice 1: Reusable Input Primitive
This code block implements the Input component used across the course. It gives you a consistent, dashboard-friendly input with an optional label, inline error styling, and ref forwarding so form libraries like React Hook Form can connect to the real DOM input safely.
-
Ref forwarding is the library-friendly design choice here. React Hook Form and similar tools often need direct access to the underlying
<input>element, and forwarding the ref makes that integration smooth and predictable. Without that, form libraries may still work in simple cases, but you are more likely to run into awkward wiring or limitations later. -
Error styling is handled in one place, which is exactly what a reusable primitive should do. Instead of every page inventing its own version of “red border plus red message,” the
Inputcomponent switches between normal and danger styles whenevererroris present. That keeps validation feedback visually consistent across the app. -
Accessibility comes from the
label,htmlFor, andidrelationship. When the label points to the input’sid, clicking the label focuses the input, and assistive technologies can announce the label correctly. This is a small detail in code, but it has a real effect on usability and quality.
This component may look simple, but it solves a repeated UI problem well. Once it exists, form pages can focus more on form behavior and less on re-creating the same input markup over and over.
