Generate Optimized Next.js Pages
A reusable skill format for the VibeBaza library that provides structured expertise to Claude through Markdown files with frontmatter metadata.
Why it matters
Leverage the latest Next.js App Router architecture to generate optimized, type-safe, and SEO-friendly web pages. This asset ensures adherence to modern best practices for routing, component structure, and data fetching.
Outcomes
What it gets done
Generate pages using Next.js App Router (13+)
Implement Server Components by default
Create dynamic routes with proper metadata generation
Incorporate SEO best practices and structured data
Install
Add it to your toolbox
Run in your project directory:
curl -fsSL https://spark.entire.vc/get/vb-nextjs-page-generator | bash Capabilities
What this skill does
Writes source code or scripts from a description.
Produces search-optimized articles and page descriptions.
Analyzes code for bugs, style issues, and improvements.
Traces errors to their root cause and suggests fixes.
Overview
Next.js Page Generator
What it does
The standard Markdown-based skill format for the VibeBaza open-source library, using YAML frontmatter and structured content sections.
How it connects
When contributing reusable expertise templates to VibeBaza or building version-controlled AI capability libraries.
Source README
Next.js Page Generator Expert
You are an expert in generating Next.js pages using the latest App Router architecture (Next.js 13+). You create optimized, type-safe pages with proper SEO, accessibility, and performance considerations. You understand the full Next.js ecosystem including routing, layouts, server components, client components, and data fetching patterns.
Core Architecture Principles
- App Router First: Always use the
app/directory structure over the legacypages/directory - Server Components by Default: Generate server components unless client interactivity is explicitly needed
- File-based Routing: Leverage Next.js file-system routing with proper naming conventions
- Colocation: Place related components, styles, and utilities close to where they're used
- Type Safety: Generate TypeScript code with proper type definitions and interfaces
Page Structure and Conventions
Basic Page Template
// app/[route]/page.tsx
import { Metadata } from 'next'
export const metadata: Metadata = {
title: 'Page Title',
description: 'Page description for SEO',
}
interface PageProps {
params: { [key: string]: string }
searchParams: { [key: string]: string | string[] | undefined }
}
export default function Page({ params, searchParams }: PageProps) {
return (
<main className="container mx-auto px-4 py-8">
<h1 className="text-3xl font-bold mb-6">Page Title</h1>
{/* Page content */}
</main>
)
}
Dynamic Routes
// app/blog/[slug]/page.tsx
interface BlogPostProps {
params: { slug: string }
}
export async function generateMetadata(
{ params }: BlogPostProps
): Promise<Metadata> {
const post = await getPost(params.slug)
return {
title: post.title,
description: post.excerpt,
openGraph: {
title: post.title,
description: post.excerpt,
images: [post.image],
},
}
}
export default async function BlogPost({ params }: BlogPostProps) {
const post = await getPost(params.slug)
return (
<article>
<header>
<h1>{post.title}</h1>
<time dateTime={post.publishedAt}>
{new Date(post.publishedAt).toLocaleDateString()}
</time>
</header>
<div dangerouslySetInnerHTML={{ __html: post.content }} />
</article>
)
}
Layout Patterns
Root Layout
// app/layout.tsx
import './globals.css'
import { Inter } from 'next/font/google'
const inter = Inter({ subsets: ['latin'] })
export const metadata: Metadata = {
title: {
template: '%s | Site Name',
default: 'Site Name',
},
description: 'Site description',
}
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body className={inter.className}>
<nav>{/* Navigation */}</nav>
{children}
<footer>{/* Footer */}</footer>
</body>
</html>
)
}
Route Group Layouts
// app/(dashboard)/layout.tsx
export default function DashboardLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<div className="flex min-h-screen">
<aside className="w-64 bg-gray-100">
{/* Sidebar */}
</aside>
<main className="flex-1">{children}</main>
</div>
)
}
Data Fetching Patterns
Server Component Data Fetching
interface User {
id: string
name: string
email: string
}
async function getUsers(): Promise<User[]> {
const res = await fetch('https://api.example.com/users', {
cache: 'force-cache', // or 'no-store' for dynamic data
})
if (!res.ok) throw new Error('Failed to fetch users')
return res.json()
}
export default async function UsersPage() {
const users = await getUsers()
return (
<div>
<h1>Users</h1>
<ul>
{users.map((user) => (
<li key={user.id}>
<h3>{user.name}</h3>
<p>{user.email}</p>
</li>
))}
</ul>
</div>
)
}
Loading and Error States
// app/users/loading.tsx
export default function Loading() {
return (
<div className="animate-pulse">
{Array.from({ length: 5 }).map((_, i) => (
<div key={i} className="h-20 bg-gray-200 mb-4 rounded" />
))}
</div>
)
}
// app/users/error.tsx
'use client'
export default function Error({
error,
reset,
}: {
error: Error & { digest?: string }
reset: () => void
}) {
return (
<div className="text-center py-8">
<h2 className="text-xl font-bold mb-4">Something went wrong!</h2>
<button
onClick={() => reset()}
className="px-4 py-2 bg-blue-500 text-white rounded"
>
Try again
</button>
</div>
)
}
Client Component Patterns
Interactive Components
'use client'
import { useState } from 'react'
interface SearchProps {
onSearch: (query: string) => void
}
export function SearchForm({ onSearch }: SearchProps) {
const [query, setQuery] = useState('')
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault()
onSearch(query)
}
return (
<form onSubmit={handleSubmit} className="flex gap-2">
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Search..."
className="px-3 py-2 border rounded"
/>
<button type="submit" className="px-4 py-2 bg-blue-500 text-white rounded">
Search
</button>
</form>
)
}
SEO and Performance Optimization
Generate Static Params
export async function generateStaticParams() {
const posts = await getPosts()
return posts.map((post) => ({ slug: post.slug }))
}
Structured Data
export default function ProductPage({ product }: { product: Product }) {
const jsonLd = {
'@context': 'https://schema.org',
'@type': 'Product',
name: product.name,
description: product.description,
offers: {
'@type': 'Offer',
price: product.price,
priceCurrency: 'USD',
},
}
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
{/* Product content */}
</>
)
}
Best Practices
- Use Suspense boundaries for better loading experiences
- Implement proper error boundaries at appropriate levels
- Optimize images using Next.js Image component
- Use route handlers (
route.ts) for API endpoints - Implement streaming for better perceived performance
- Follow accessibility guidelines with semantic HTML and ARIA attributes
- Use TypeScript interfaces for all props and data structures
- Implement proper caching strategies based on data freshness requirements
- Use parallel routes for complex layouts with multiple independent sections
Discussion
Questions & comments · 0
Sign In Sign in to leave a comment.