Metronic NextJS
Getting Started

Adding a New Page

Learn how to add a new page to your Metronic Next.js application.

This guide provides step-by-step instructions for adding a new page to the Metronic Next.js application using the App Router.

Overview

Adding a new page to the application involves these key steps:

  1. Deciding where the page belongs
  2. Creating the page component
  3. Adding metadata and layout
  4. Linking to the page
  5. Testing the new page

Step 1: Decide Where the Page Belongs

First, determine whether your page should be:

  • Protected (requires authentication) → place in app/(protected)/
  • Authentication-related → place in app/(auth)/
  • Public → place directly in app/

Step 2: Create the Page Component

For this example, let's create a new protected page for a reports section:

// app/(protected)/reports/page.tsx
export default function ReportsPage() {
  return (
    <div className="space-y-4">
      <h1 className="text-2xl font-bold">Reports</h1>
      <p>View and manage your reports</p>
 
      <div className="bg-card p-6 rounded-lg shadow-sm">
        <h2 className="text-xl font-semibold mb-4">Available Reports</h2>
        <ul className="space-y-2">
          <li>Monthly Sales Report</li>
          <li>User Activity Log</li>
          <li>Performance Metrics</li>
        </ul>
      </div>
    </div>
  );
}

For client-side interactivity, add the 'use client' directive:

// app/(protected)/reports/analytics/page.tsx
'use client';
 
import { useState } from 'react';
 
export default function AnalyticsPage() {
  const [timeRange, setTimeRange] = useState('week');
 
  return (
    <div className="space-y-4">
      <h1 className="text-2xl font-bold">Analytics Dashboard</h1>
 
      <div className="flex space-x-2">
        <button
          className={`px-3 py-1 rounded ${timeRange === 'week' ? 'bg-primary text-white' : 'bg-muted'}`}
          onClick={() => setTimeRange('week')}
        >
          Weekly
        </button>
        <button
          className={`px-3 py-1 rounded ${timeRange === 'month' ? 'bg-primary text-white' : 'bg-muted'}`}
          onClick={() => setTimeRange('month')}
        >
          Monthly
        </button>
      </div>
 
      <div className="bg-card p-6 rounded-lg shadow-sm">
        <h2 className="text-xl font-semibold mb-4">{timeRange === 'week' ? 'Weekly' : 'Monthly'} Analytics</h2>
        <p>Analytics data will appear here</p>
      </div>
    </div>
  );
}

Step 3: Add Metadata and Layout

Page Metadata

Add metadata to your page for better SEO and title display:

// app/(protected)/reports/page.tsx
import { Metadata } from 'next';
 
export const metadata: Metadata = {
  title: 'Reports | Metronic',
  description: 'View and manage your reports',
};
 
export default function ReportsPage() {
  // Component code...
}

Layout (Optional)

If your section needs a specific layout, create a layout file:

// app/(protected)/reports/layout.tsx
export default function ReportsLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <div className="reports-layout">
      <div className="mb-6">
        <nav className="flex space-x-4">
          <a href="/reports" className="text-primary font-medium">Overview</a>
          <a href="/reports/analytics" className="text-muted-foreground hover:text-foreground">Analytics</a>
          <a href="/reports/exports" className="text-muted-foreground hover:text-foreground">Exports</a>
        </nav>
      </div>
      <main>{children}</main>
    </div>
  );
}

Update your navigation menu to include links to the new page:

// app/components/layouts/demo1/components/sidebar.tsx
import Link from 'next/link';
import { BarChartIcon, FileTextIcon } from 'lucide-react';
 
export function Sidebar() {
  const menuItems = [
    { icon: BarChartIcon, path: '/', label: 'Dashboard' },
    { icon: FileTextIcon, path: '/reports', label: 'Reports' },
    // Other items...
  ];
 
  return (
    <aside className="sidebar">
      <nav className="space-y-1">
        {menuItems.map((item) => (
          <Link
            key={item.path}
            href={item.path}
            className="flex items-center px-4 py-2 hover:bg-muted rounded-md"
          >
            <item.icon className="h-5 w-5 mr-3" />
            <span>{item.label}</span>
          </Link>
        ))}
      </nav>
    </aside>
  );
}

Step 5: Create Dynamic Routes (Optional)

For pages that need dynamic parameters:

app/
└── (protected)/
    └── reports/
        └── [id]/
            └── page.tsx
// app/(protected)/reports/[id]/page.tsx
export default function ReportDetailPage({ params }: { params: { id: string } }) {
  return (
    <div className="space-y-4">
      <h1 className="text-2xl font-bold">Report Details</h1>
      <p>Viewing Report ID: {params.id}</p>
 
      <div className="bg-card p-6 rounded-lg shadow-sm">
        <h2 className="text-xl font-semibold mb-4">Report Content</h2>
        <p>Report data will appear here</p>
      </div>
    </div>
  );
}

Step 6: Create API Routes (Optional)

If your page needs a specific API endpoint:

// app/api/reports/route.ts
import { NextResponse } from 'next/server';
 
export async function GET() {
  // In a real app, fetch from database
  const reports = [
    { id: '1', name: 'Monthly Sales' },
    { id: '2', name: 'User Activity' },
  ];
 
  return NextResponse.json({ reports });
}

Step 7: Test Your New Page

  1. Start the development server:
npm run dev
  1. Navigate to your new page:

Working with Protected Routes

The (protected) route group uses a layout with authentication checks:

// app/(protected)/layout.tsx
'use client';
 
import { useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { useSession } from 'next-auth/react';
 
export default function ProtectedLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  const { status } = useSession();
  const router = useRouter();
 
  useEffect(() => {
    if (status === 'unauthenticated') {
      router.push('/signin');
    }
  }, [status, router]);
 
  if (status === 'loading') {
    return <div>Loading...</div>;
  }
 
  return status === 'authenticated' ? children : null;
}

Special Files

Next.js supports special files for common UI patterns:

Loading State

// app/(protected)/reports/loading.tsx
export default function Loading() {
  return <div className="p-6 text-center">Loading reports...</div>;
}

Error Handling

// app/(protected)/reports/error.tsx
'use client';
 
export default function Error({
  error,
  reset,
}: {
  error: Error;
  reset: () => void;
}) {
  return (
    <div className="p-6 text-center">
      <h2 className="text-xl text-red-600">Something went wrong!</h2>
      <p className="mt-2 mb-4">{error.message}</p>
      <button
        onClick={() => reset()}
        className="px-4 py-2 bg-primary text-white rounded-md"
      >
        Try again
      </button>
    </div>
  );
}

Not Found

// app/(protected)/reports/[id]/not-found.tsx
export default function ReportNotFound() {
  return (
    <div className="p-6 text-center">
      <h2 className="text-xl font-bold">Report Not Found</h2>
      <p className="mt-2">The report you're looking for doesn't exist.</p>
    </div>
  );
}

Best Practices

  1. Follow the App Router Structure:

    • Page components go in page.tsx files
    • Layouts go in layout.tsx files
    • Shared components go in app/components/
  2. Use Server vs. Client Components Appropriately:

    • Keep most components as Server Components (no 'use client' directive)
    • Only use 'use client' when you need:
      • React hooks
      • Event listeners
      • Browser-only APIs
      • Client-side state
  3. Organize Routes Logically:

    • Use route groups (folders with parentheses) to organize routes
    • Keep related pages together
    • Use nested layouts for shared UI elements
  4. Use Metadata API:

    • Add page metadata for better SEO
    • Use dynamic metadata for data-dependent pages
  5. Handle Loading and Error States:

    • Create loading.tsx files for loading states
    • Create error.tsx files for error handling
    • Use not-found.tsx for 404 pages

Troubleshooting

Page Not Found

  • Check that your file is named page.tsx
  • Ensure the path in the browser matches your folder structure
  • Check for typos in folder or file names

Component Not Rendering

  • Check browser console for errors
  • If using client components, ensure 'use client' is at the top
  • Verify imports are correct

Layout Issues

  • Ensure the page is in the correct folder structure
  • Check that the layout component is correctly defined
  • Verify the component hierarchy makes sense