Chameleon

New Page

Create new pages in Chameleon

New Page

Learn how to create new pages in Chameleon using Next.js App Router and the project structure.

Overview

Chameleon uses Next.js App Router with internationalization support. New pages are created in the src/app/[locale]/ directory structure with proper routing, layouts, and component integration.

Page Structure

Directory Structure

src/app/[locale]/
├── (default)/              # Public pages
│   ├── page.tsx           # Homepage
│   ├── about/
│   │   └── page.tsx       # About page
│   └── contact/
│       └── page.tsx       # Contact page
├── (console)/             # User dashboard pages
│   ├── my-orders/
│   │   └── page.tsx       # User orders
│   └── ai-generator/
│       └── page.tsx       # AI generator
├── (admin)/               # Admin pages
│   └── admin/
│       ├── users/
│       │   └── page.tsx   # User management
│       └── posts/
│           └── page.tsx   # Post management
└── layout.tsx             # Root layout

Route Groups

  • (default) - Public pages accessible to all users
  • (console) - User dashboard pages (requires authentication)
  • (admin) - Admin pages (requires admin access)

Creating a New Page

Basic Page

// src/app/[locale]/(default)/about/page.tsx
import { Metadata } from "next";
import { useTranslations } from "next-intl";

export const metadata: Metadata = {
  title: "About Us",
  description: "Learn more about Chameleon",
};

export default function AboutPage() {
  return (
    <div className="container mx-auto px-4 py-8">
      <h1 className="text-3xl font-bold mb-4">About Chameleon</h1>
      <p className="text-lg">
        Chameleon is an AI-powered content generation platform.
      </p>
    </div>
  );
}

Page with Translations

// src/app/[locale]/(default)/contact/page.tsx
import { useTranslations } from "next-intl";

export default function ContactPage() {
  const t = useTranslations("contact");
  
  return (
    <div className="container mx-auto px-4 py-8">
      <h1 className="text-3xl font-bold mb-4">{t("title")}</h1>
      <p className="text-lg mb-6">{t("description")}</p>
      
      <form className="max-w-md">
        <div className="mb-4">
          <label className="block text-sm font-medium mb-2">
            {t("form.name")}
          </label>
          <input 
            type="text" 
            className="w-full px-3 py-2 border rounded-md"
            placeholder={t("form.name_placeholder")}
          />
        </div>
        
        <button 
          type="submit"
          className="bg-blue-500 text-white px-4 py-2 rounded-md hover:bg-blue-600"
        >
          {t("form.submit")}
        </button>
      </form>
    </div>
  );
}

Protected Page

// src/app/[locale]/(console)/dashboard/page.tsx
import { auth } from "@/auth";
import { redirect } from "next/navigation";

export default async function DashboardPage() {
  const session = await auth();
  
  if (!session) {
    redirect("/auth/signin");
  }
  
  return (
    <div className="container mx-auto px-4 py-8">
      <h1 className="text-3xl font-bold mb-4">
        Welcome, {session.user.name}!
      </h1>
      <p>This is your dashboard.</p>
    </div>
  );
}

Admin Page

// src/app/[locale]/(admin)/admin/settings/page.tsx
import { auth } from "@/auth";
import { redirect } from "next/navigation";

export default async function AdminSettingsPage() {
  const session = await auth();
  const adminEmails = process.env.ADMIN_EMAILS?.split(",");
  
  if (!session || !adminEmails?.includes(session.user.email)) {
    redirect("/");
  }
  
  return (
    <div className="container mx-auto px-4 py-8">
      <h1 className="text-3xl font-bold mb-4">Admin Settings</h1>
      <p>Configure system settings here.</p>
    </div>
  );
}

Dynamic Routes

Single Dynamic Route

// src/app/[locale]/(default)/posts/[slug]/page.tsx
import { Metadata } from "next";

interface PageProps {
  params: { slug: string };
}

export async function generateMetadata({ params }: PageProps): Promise<Metadata> {
  const post = await getPostBySlug(params.slug);
  
  return {
    title: post.title,
    description: post.description,
  };
}

export default async function PostPage({ params }: PageProps) {
  const post = await getPostBySlug(params.slug);
  
  return (
    <article className="container mx-auto px-4 py-8">
      <h1 className="text-3xl font-bold mb-4">{post.title}</h1>
      <div className="prose max-w-none">
        {post.content}
      </div>
    </article>
  );
}

Multiple Dynamic Routes

// src/app/[locale]/(default)/category/[category]/[slug]/page.tsx
interface PageProps {
  params: { 
    category: string;
    slug: string;
  };
}

export default async function CategoryPostPage({ params }: PageProps) {
  const { category, slug } = params;
  const post = await getPostByCategoryAndSlug(category, slug);
  
  return (
    <div>
      <nav className="text-sm text-gray-600 mb-4">
        <a href="/">Home</a> / 
        <a href={`/category/${category}`}>{category}</a> / 
        {post.title}
      </nav>
      
      <article>
        <h1>{post.title}</h1>
        <div>{post.content}</div>
      </article>
    </div>
  );
}

File Locations

  • src/app/[locale]/(default)/ - Public pages
  • src/app/[locale]/(console)/ - User dashboard pages
  • src/app/[locale]/(admin)/ - Admin pages
  • src/app/[locale]/layout.tsx - Root layout
  • src/i18n/pages/ - Page-specific translations

Common Tasks

Create a New Public Page

  1. Create directory: src/app/[locale]/(default)/your-page/
  2. Add page.tsx file
  3. Add translations to src/i18n/pages/your-page/
  4. Test page accessibility

Add Authentication Check

import { auth } from "@/auth";
import { redirect } from "next/navigation";

export default async function ProtectedPage() {
  const session = await auth();
  
  if (!session) {
    redirect("/auth/signin");
  }
  
  // Page content
}

Add Admin Check

const adminEmails = process.env.ADMIN_EMAILS?.split(",");
if (!adminEmails?.includes(session.user.email)) {
  redirect("/");
}

Add Metadata

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

Troubleshooting

Page not accessible

Problem: New page returns 404

Solution:

  1. Check file is named page.tsx
  2. Verify directory structure is correct
  3. Check for TypeScript errors
  4. Restart development server

Authentication not working

Problem: Protected page accessible without login

Solution:

  1. Check auth() import is correct
  2. Verify session check logic
  3. Check redirect path is valid
  4. Test with different user states

Translations not working

Problem: Page shows translation keys

Solution:

  1. Check translation files exist
  2. Verify useTranslations namespace
  3. Check translation key spelling
  4. Test with different locales

Next Steps