→ cards/02-client-component

Chapter

02 · Client Component (UI & Interaction)

Interactive UI only: render/react and call server actions; never fetch or decide business rules.

Mental Model

  • Client Components speak to users, not databases.
  • They render, react, and request mutations; they never decide business rules.
  • They are interactive and replaceable; they do not own data.

File Classification

Layer: UI / Interaction
Directive: "use client"
Runtime: Browser
Reads: props or promises only
Writes: ✅ via Server Actions
Prisma: ❌ Never
API Routes: ❌ Never (internal UI)

Should Do

  • Render interactive UI (presentation + events).
  • Consume server data via props or promise + use().
  • Trigger mutations via Server Actions (buttons/forms).
  • Use context only for UI state (theme, edit mode, filters).

Should Not (and where it belongs)

  • Prisma/DB fetch → put in server actions.
  • Business rules → entity/domain layer, not UI.
  • Internal API fetch → call server actions directly.
  • Owning entity truth → keep on the server; client state is UI-only.

Canonical Example

"use client";

import { use } from "react";
import { deleteProject } from "../actions/deleteProject";
import type { Project } from "../types";

export default function ProjectsClient({ projectsPromise }: { projectsPromise: Promise<Project[]>; }) {
const projects = use(projectsPromise);
return (
<ul>
{projects.map((p) => (
<li key={p.id}>
{p.name}
<button onClick={() => deleteProject(p.id)}>Delete</button>
</li>
))}
</ul>
);
}

Form + Server Action

"use client";
import { createProject } from "../actions/createProject";

export function ProjectForm() {
async function onSubmit(formData: FormData) {
await createProject(formData);
}
return (
<form action={onSubmit}>
<input name="name" />
<button type="submit">Create</button>
</form>
);
}

Colour-Coded Miniature

"use client";
import { deleteProject } from "../actions/deleteProject";

export function ProjectRow({ project }) {
return (
<div>
{project.name}
<button onClick={() => deleteProject(project.id)}>Delete</button>
</div>
);
}

Quick Rules

  • Client Components render/handle UX; they never fetch or decide.
  • Mutations go through Server Actions; no fetch to internal APIs.
  • Context is for UI state only; entity truth stays on the server.
  • If it feels “dumb but responsive,” it’s correct.