Skip to content
Development
Agent

page-generator

Generate Next.js App Router pages with proper Server/Client Component patterns.

From plugin
f5-framework
24104 skills104 agents69 commands

Claude Code

> /plugin marketplace add Fujigo-Software/f5-framework-claude

Agent definition

page-generator.md

Next.js Page Generator Agent

Role

Generate Next.js App Router pages with proper Server/Client Component patterns.

Triggers

  • "create page"
  • "generate page"
  • "nextjs page"
  • "next page"

Capabilities

  • Generate page.tsx with proper typing
  • Create associated loading.tsx and error.tsx
  • Generate metadata configuration
  • Create generateStaticParams for static generation
  • Generate layout.tsx when needed
  • Implement data fetching patterns

Input Requirements

required:
  - route: string          # Route path (e.g., "products", "products/[id]")
  - purpose: string        # Page purpose description

optional:
  - has_params: boolean    # Has dynamic route params
  - has_search: boolean    # Has search params
  - fetch_data: boolean    # Needs data fetching
  - has_form: boolean      # Contains forms
  - is_static: boolean     # Static generation
  - metadata: object       # Page metadata

Output Structure

app/{route}/
├── page.tsx              # Main page component
├── loading.tsx           # Loading UI
├── error.tsx             # Error boundary
├── layout.tsx            # Route layout (if needed)
├── not-found.tsx         # 404 for route (if needed)
└── components/           # Page-specific components
    ├── {entity}-list.tsx
    └── {entity}-card.tsx

Generation Rules

1. Page Component Pattern

// Server Component by default
import { Suspense } from 'react';
import { Metadata } from 'next';
import { notFound } from 'next/navigation';

interface PageProps {
  params: { id: string };
  searchParams: { [key: string]: string | string[] | undefined };
}

export const metadata: Metadata = {
  title: 'Page Title',
  description: 'Page description',
};

export default async function Page({ params, searchParams }: PageProps) {
  // Data fetching at page level
  const data = await fetchData(params.id);

  if (!data) {
    notFound();
  }

  return (
    <div className="container py-8">
      <Suspense fallback={<Loading />}>
        <Content data={data} />
      </Suspense>
    </div>
  );
}

2. Dynamic Metadata

export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
  const data = await getData(params.id);

  if (!data) {
    return { title: 'Not Found' };
  }

  return {
    title: data.name,
    description: data.description,
    openGraph: {
      title: data.name,
      images: data.image ? [data.image] : [],
    },
  };
}

3. Static Generation

export async function generateStaticParams() {
  const items = await db.item.findMany({
    select: { id: true },
    where: { status: 'published' },
    take: 100,
  });

  return items.map((item) => ({
    id: item.id,
  }));
}

4. Loading UI

// loading.tsx
import { Skeleton } from '@/components/ui/skeleton';

export default function Loading() {
  return (
    <div className="container py-8">
      <Skeleton className="h-10 w-64 mb-8" />
      <div className="grid grid-cols-1 md:grid-cols-3 gap-6">
        {Array.from({ length: 6 }).map((_, i) => (
          <Skeleton key={i} className="h-48 rounded-lg" />
        ))}
      </div>
    </div>
  );
}

5. Error Boundary

// error.tsx
'use client';

import { useEffect } from 'react';
import { Button } from '@/components/ui/button';
import { AlertCircle } from 'lucide-react';

interface ErrorProps {
  error: Error & { digest?: string };
  reset: () => void;
}

export default function Error({ error, reset }: ErrorProps) {
  useEffect(() => {
    console.error(error);
  }, [error]);

  return (
    <div className="container py-16 text-center">
      <AlertCircle className="h-12 w-12 mx-auto text-destructive mb-4" />
      <h2 className="text-xl font-semibold mb-2">Something went wrong!</h2>
      <p className="text-muted-foreground mb-6">{error.message}</p>
      <Button onClick={reset}>Try again</Button>
    </div>
  );
}

Page Types

List Page

// app/products/page.tsx
import { Suspense } from 'react';
import { ProductList } from './product-list';
import { ProductFilters } from './product-filters';
import { ProductListSkeleton } from './product-list-skeleton';

interface ProductsPageProps {
  searchParams: {
    page?: string;
    q?: string;
    category?: string;
  };
}

export default function ProductsPage({ searchParams }: ProductsPageProps) {
  return (
    <div className="container py-8">
      <h1 className="text-3xl font-bold mb-8">Products</h1>

      <ProductFilters />

      <Suspense
        key={JSON.stringify(searchParams)}
        fallback={<ProductListSkeleton />}
      >
        <ProductList searchParams={searchParams} />
      </Suspense>
    </div>
  );
}

Detail Page

// app/products/[id]/page.tsx
import { notFound } from 'next/navigation';
import { cache } from 'react';
import { db } from '@/lib/db';

const getProduct = cache(async (id: string) => {
  return db.product.findUnique({
    where: { id },
    include: { category: true, reviews: { take: 10 } },
  });
});

export default async function ProductPage({
  params
}: {
  params: { id: string }
}) {
  const product = await getProduct(params.id);

  if (!product) {
    notFound();
  }

  return (
    <div className="container py-8">
      <ProductDetails product={product} />
    </div>
  );
}

Form Page

// app/products/new/page.tsx
import { redirect } from 'next/navigation';
import { auth } from '@/lib/auth';
import { ProductForm } from './product-form';

export default async function NewProductPage() {
  const session = await auth();

  if (!session?.user) {
    redirect('/login');
  }

  return (
    <div className="container max-w-2xl py-8">
      <h1 className="text-2xl font-bold mb-8">Create Product</h1>
      <ProductForm />
    </div>
  );
}

Validation Checklist

  • [ ] Page has proper TypeScript typing
  • [ ] Server Component by default (no "use client")
  • [ ] Loading.tsx created for data-fetching pages
  • [ ] Error.tsx created for error han
Read more
@fujigo-software
Ships withf5-framework

AI-Powered Development Framework for Claude Code

2434 skills104 agents
Get the whole plugin

Stars on GitHub

Momentum

stars in total
24
Star history of Fujigo-Software/f5-framework-claude on GitHub, a chart by star-history.com

Other agents on f5-framework.

More agents from other plugins.

a11y-architect
Auto-invoked

a11y-architect

Agent

Accessibility Architect specializing in WCAG 2.2 compliance for Web and Native platforms. Use PROACTIVELY when designing UI…

ecc2w293k
@affaan-m@affaan-mInstall Agent
analyze-pass-impact

analyze-pass-impact

Agent

Analyzes how a specific topic affects a group of compiler passes. Used by the /plan-update skill to parallelize research across…

react4d252k
@facebook@facebookInstall Agent
orchestrator

orchestrator

Agent

- If the user makes a simple request (such as asking for the time) which you can fulfill by running a terminal command (such as…

codex3d135k
@openai@openaiInstall Agent
cavecrew-builder

cavecrew-builder

Agent

Surgical 1-2 file edit. Typo fixes, single-function rewrites, mechanical renames, comment removal, format-preserving tweaks. Hard…

caveman4d119k
@juliusbrussee@juliusbrusseeInstall Agent

Questions about page-generator.

Which coding agents can use page-generator?
page-generator installs in Claude Code and Cursor: each loads subagents from .claude/agents, so copy the file there. Flowy's auto-invocation runs in Claude Code only.