# page-layer
> This skill should be used when the user asks to 'create a page', 'add a route', 'create a layout', 'add metadata', or 'set up a dynamic route'. Provides guidance for Next.js 15 App Router pages, layouts, and route handlers in app/**/*.tsx.
- Author: Suneet Misra
- Repository: sun33t/suneet-codes
- Version: 20251212134000
- Stars: 0
- Forks: 0
- Last Updated: 2026-02-07
- Source: https://github.com/sun33t/suneet-codes
- Web: https://mule.run/skillshub/@@sun33t/suneet-codes~page-layer:20251212134000
---
---
name: page-layer
description: "This skill should be used when the user asks to 'create a page', 'add a route', 'create a layout', 'add metadata', or 'set up a dynamic route'. Provides guidance for Next.js 15 App Router pages, layouts, and route handlers in app/**/*.tsx."
---
# Page Layer Skill
## Scope
- `app/**/page.tsx` - Next.js page components
- `app/**/layout.tsx` - Layout components
- `app/**/not-found.tsx` - Not found pages
- `app/**/loading.tsx` - Loading states
- `app/**/error.tsx` - Error boundaries
## Decision Tree
### Creating a new page?
1. **Determine route**: Map URL to folder structure in `app/`
2. **Create folder**: `app/[route-name]/`
3. **Create `page.tsx`**: Export default async function
4. **Add metadata**: Export `metadata` object or `generateMetadata` function
5. **Use layout components**: Container, PageIntro, etc.
### Creating a dynamic route?
1. **Create folder with brackets**: `app/[param]/` or `app/[...slug]/`
2. **Type params as Promise**: `params: Promise<{ param: string }>`
3. **Await params**: `const { param } = await params;`
4. **Add `generateStaticParams`**: For static generation
5. **Add `generateMetadata`**: For dynamic meta tags
### Adding page metadata?
1. **Static metadata**: Export `metadata` object
2. **Dynamic metadata**: Export async `generateMetadata` function
3. **Include OpenGraph**: title, description, images, type
4. **Use env variables**: `env.PROJECT_BASE_TITLE`, etc.
### Adding a layout?
1. **Create `layout.tsx`** in route folder
2. **Accept `children` prop**
3. **Wrap with structural components**
4. **Export metadata if needed** (inherited by child pages)
## Quick Templates
### Basic Page
```tsx
import type { Metadata } from "next";
import { Container } from "@/components/layout/container";
import { PageIntro } from "@/components/layout/page-intro";
export const metadata: Metadata = {
title: "Page Title",
description: "Page description for SEO",
};
export default function PageName() {
return (
Page content description
{/* Page content */}
);
}
```
### Dynamic Route Page (Next.js 15)
```tsx
import type { Metadata } from "next";
import { notFound } from "next/navigation";
type Props = {
params: Promise<{ slug: string }>;
};
export async function generateStaticParams() {
// Return array of param objects for static generation
return [{ slug: "example-1" }, { slug: "example-2" }];
}
export async function generateMetadata({ params }: Props): Promise {
const { slug } = await params;
// Fetch data and return metadata
return {
title: `Dynamic Title for ${slug}`,
description: "Dynamic description",
};
}
export default async function Page({ params }: Props) {
const { slug } = await params;
// Fetch data
const data = getData(slug);
if (!data) {
notFound();
}
return (
{data.title}
);
}
```
### Layout
```tsx
import type { Metadata } from "next";
export const metadata: Metadata = {
title: {
template: "%s | Section Name",
default: "Section Name",
},
};
export default function SectionLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
{children}
);
}
```
### Not Found Page
```tsx
import { NotFound } from "@/components/shared/not-found";
export default function NotFoundPage() {
return ;
}
```
### Metadata with OpenGraph
```tsx
import type { Metadata } from "next";
import { getCldImageUrl } from "next-cloudinary";
import { env } from "@/lib/config/env";
import { withCloudinaryCloudName } from "@/lib/utils/withCloudinaryCloudName";
const ogImageUrl = getCldImageUrl({
width: 1200,
height: 630,
src: withCloudinaryCloudName("path/to/image"),
});
export const metadata: Metadata = {
title: "Page Title",
description: "Page description",
openGraph: {
title: "Page Title",
description: "Page description",
url: "/page-path",
images: [ogImageUrl],
type: "website",
locale: "en_GB",
siteName: env.PROJECT_BASE_TITLE,
},
};
```
## Mistakes
- ❌ Missing `await params` in Next.js 15 (params is now a Promise)
- ❌ `"use client"` on pages (should be server components)
- ❌ Missing `generateStaticParams` for dynamic routes (breaks static export)
- ❌ Not calling `notFound()` for missing data
- ❌ Hardcoding URLs instead of using route config
- ❌ Missing metadata/OpenGraph tags
## Validation
After changes, run:
```bash
.claude/skills/page-layer/scripts/validate-page-patterns.sh
pnpm build # Full build validates routes
pnpm typecheck # TypeScript validation
```
## Route Structure
```
app/
├── layout.tsx # Root layout (required)
├── page.tsx # Home page (/)
├── not-found.tsx # Global 404
├── about/
│ └── page.tsx # /about
├── articles/
│ ├── page.tsx # /articles
│ └── [slug]/
│ ├── page.tsx # /articles/[slug]
│ └── not-found.tsx # Article 404
├── contact/
│ └── page.tsx # /contact
└── projects/
└── page.tsx # /projects
```
## Next.js 15 Breaking Changes
**Params are now Promises**:
```tsx
// Next.js 14 (old)
export default function Page({ params }: { params: { slug: string } }) {
const { slug } = params;
}
// Next.js 15 (current)
export default async function Page({ params }: { params: Promise<{ slug: string }> }) {
const { slug } = await params;
}
```