Task Creation and Editing
Introduction: Turning CRUD Into a Real Product Experience
In Unit 1, you built the foundations that make CRUD pleasant instead of painful. You now have a reusable Input primitive, a reusable TaskForm with validation intent, a central API client that understands the backend { data, meta } envelope, and toast feedback so actions feel intentional instead of silent.
Now in this lesson, you’ll take the next step: true interactivity.
By the end of this lesson, your Task Manager will feel much more like a real app. Users will be able to create tasks and land back on the list, edit tasks directly from the detail page, and toggle completion from the list with updates that feel immediate. That shift matters because CRUD is not only about sending requests successfully. It is about giving users a smooth sense that the interface is reacting to what they do.
As always, one important boundary stays in place: don’t touch src/app/api/**. The backend is read-only. Your job in this lesson is to build the frontend product layer on top of it, using the API contract that already exists.
Quick Recall: The Pattern We’re Repeating
Almost everything in this lesson follows the same product-grade loop. First, collect validated values with TaskForm. Then call the backend through api. After that, show toast feedback for success or failure, revalidate the pages that display tasks with mutate(...), and navigate the user to the next sensible screen when appropriate.
That is the difference between “it works” and “it feels like an app.” A page that only submits data may be technically correct, but a page that also refreshes stale views, shows feedback, and moves the user forward feels polished and trustworthy. You will repeat that same loop several times in this lesson, which is why building the reusable primitives first was so important.
Practice 1: Production-Ready Create Flow (/tasks/new)
This code block upgrades /tasks/new into a complete create experience. It uses TaskForm with api.post, shows toast feedback, revalidates the task list, and navigates back to /tasks so the user immediately sees the result of their action.
-
api.post("/api/tasks", values)is the single source of truth for creation. The page does not manually buildfetch(...)calls or parse response shapes itself. That is exactly why the API client exists: page files should focus on flow and UX, not on low-level networking details. -
The toast is the user-facing result of the action. Whether the request succeeds or fails, the user gets immediate feedback instead of being left to guess what happened. Small feedback moments like this are what make the app feel responsive and deliberate.
-
mutate("/api/tasks")keeps the list fresh. When the user returns to/tasks, the cache has already been revalidated, so the newly created item appears without requiring a manual refresh. That makes navigation feel connected to the data layer, rather than like a separate concern.
This create page is a good example of how several small pieces combine into a full product flow. The form handles validation, the API client handles the request, the toast handles messaging, SWR handles freshness, and the router handles where the user goes next.
