Create a Next.js 15 App Router page with Server/Client split
Create a new Next.js 15 App Router page in the x4-mono web app.
The user describes the page. If unclear, ask for:
/settings, /projects/[id])(auth) (public) or (dashboard) (requires login)Pages go in apps/web/src/app/{route-group}/{path}/page.tsx.
Examples:
/settings → apps/web/src/app/(dashboard)/settings/page.tsx/projects/[id] → apps/web/src/app/(dashboard)/projects/[id]/page.tsx/login → apps/web/src/app/(auth)/login/page.tsxReference: apps/web/src/app/(dashboard)/projects/page.tsx
'use client';
import { trpc } from '@x4/shared/api-client';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
export default function MyPage() {
const { data, isLoading, error } = trpc.routerName.list.useQuery({
limit: 20,
offset: 0,
});
if (isLoading) {
return <div className="flex items-center justify-center p-8">Loading...</div>;
}
if (error) {
return <div className="text-destructive p-8">Error: {error.message}</div>;
}
return (
<div className="container mx-auto py-8">
<div className="flex items-center justify-between mb-6">
<h1 className="text-3xl font-bold">Page Title</h1>
<Button>Action</Button>
</div>
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
{data?.items.map((item) => (
<Card key={item.id}>
<CardHeader>
<CardTitle>{item.name}</CardTitle>
</CardHeader>
<CardContent>
<p className="text-muted-foreground">{item.description}</p>
</CardContent>
</Card>
))}
</div>
</div>
);
}
// No 'use client' directive
export default async function MyPage() {
// Server-side data fetching if needed
return (
<div className="container mx-auto py-8">
<h1 className="text-3xl font-bold">Page Title</h1>
{/* Static or server-rendered content */}
</div>
);
}
'use client';
import { useParams } from 'next/navigation';
import { trpc } from '@x4/shared/api-client';
export default function DetailPage() {
const { id } = useParams<{ id: string }>();
const { data, isLoading } = trpc.routerName.get.useQuery({ id });
// ...
}
export default function SectionLayout({ children }: { children: React.ReactNode }) {
return (
<div className="max-w-4xl mx-auto">
<nav>{/* Section navigation */}</nav>
{children}
</div>
);
}
@x4/shared/* for cross-package, @/* for intra-workspace@/components/ui/@x4/shared/api-clientpage.tsx is the convention(auth) or (dashboard))apps/web/src/app/{group}/{path}/page.tsx with appropriate templatelayout.tsx if the section needs shared UIbun turbo dev and visit the URLbun turbo type-check to verify