LogoStarterkitpro
Walkthrough

Pages and Layouts

Learn how to structure static, protected, and paid pages and layouts in your Next.js application.

In the Next.js App Router, page.tsx files define the unique UI for a route, while layout.tsx files define UI that is shared across multiple routes within a segment. Layouts are particularly useful for enforcing access control.

Static Page

These pages are publicly accessible and don't require any authentication or special access. They are typically used for landing pages, about pages, etc.

Example:

Static Page Seo

Here is basic overview of static page. The libs/seo folder helps you set SEO tags and structure data to better rank on Google. Also add this tatic page to the app/sitemap.ts. Make sure to customize SEO tags.

app/example/page.tsx
// No special imports needed for protection
import { Button } from "@/components/ui/button";
import { constructMetadata } from "@/lib/seo/metadata";
import { Metadata } from "next";
import Image from "next/image";
 
// Optionally define metadata
export const metadata: Metadata = constructMetadata({
  title: "Example | Starterkitpro",
  description: "Explore Static page, How it looks",
  openGraph: {
    type: "website",
    // pass the image other wise default opengraph will work.
    // images: [
    //  {
    //    url: "/images/example-og.png",
    //    width: 1200,
    //   height: 630,
    //    alt: "Starterkitpro example",
    //  },
    // ],
    siteName: "Starterkitpro",
  },
  keywords: ["example page", "static page"],
  canonicalUrlRelative: "/example",
});
 
export default function StaticPage() {
  return (
    <main className="min-h-screen p-12 pb-24 text-center">
      <section className="max-w-xl mx-auto space-y-8">
        <h1 className="text-4xl font-extrabold">Welcome!</h1>
        <p className="text-lg">Discover the amazing features we offer.</p>
        {/* Example image */}
        <Image
          src="/placeholder.svg" // Replace with actual image path
          alt="Placeholder Feature Image"
          width={500}
          height={250}
          className="rounded-lg mx-auto bg-muted"
        />
        <Button size="lg">Get Started</Button>
      </section>
    </main>
  );
}

Protected Page / Layout

These routes require the user to be authenticated. You can enforce this either at the layout level (protecting all pages in a segment) or at the individual page level.

Layout Enforces Protection

The layout.tsx file checks for an authenticated user. If the user is not logged in, it redirects them (e.g., to the login page defined in appConfig.auth.login) before any child page.tsx components in that segment are rendered. Child pages within this layout can assume authentication.

Example Layout (app/protected/layout.tsx):

app/protected/layout.tsx
import { getCurrentUser } from "@/lib/session";
import { redirect } from "next/navigation";
import { appConfig } from "@/lib/app-config";
 
interface ProtectedLayoutProps {
  children: React.ReactNode;
}
 
export default async function ProtectedLayout({
  children,
}: ProtectedLayoutProps) {
  const user = await getCurrentUser();
 
  if (!user) {
    redirect(appConfig.auth.login); // Redirect to login if not authenticated
  }
 
  // User is authenticated, render the layout and children
  return (
    <div className="min-h-screen flex flex-col">
      {/* You could add a dashboard-specific header/sidebar here */}
      <main className="flex-grow p-8">{children}</main>
      {/* You could add a dashboard-specific footer here */}
    </div>
  );
}

These routes require the user to be both authenticated AND have specific access rights (e.g., an active subscription confirmed by the hasAccess flag). Similar to protected routes, you can enforce this at the layout or page level.

Layout Enforces Auth + Access

The layout first checks for authentication, redirecting to login if necessary. Then, it checks for the required access flag (user.hasAccess). If the user lacks access, it redirects them (e.g., to the billing page defined in appConfig.lemonsqueezy.billingRoute) before rendering child components. Child pages can assume authentication and access.

Example Layout (app/premium-feature/layout.tsx):

app/premium-feature/layout.tsx
import { getCurrentUser } from "@/lib/session";
import { redirect } from "next/navigation";
import { appConfig } from "@/lib/app-config";
 
interface PaidLayoutProps {
  children: React.ReactNode;
}
 
export default async function PaidLayout({ children }: PaidLayoutProps) {
  const user = await getCurrentUser();
 
  // 1. Check Authentication
  if (!user) {
    redirect(appConfig.auth.login);
  }
 
  // 2. Check Access (assuming hasAccess is on the user session object)
  if (!user.hasAccess) {
    redirect(appConfig.lemonsqueezy.billingRoute); // Redirect to billing/upgrade
  }
 
  // User is authenticated and has access
  return (
    <div className="min-h-screen flex flex-col">
      {/* Optional: Premium-specific header/sidebar */}
      <main className="flex-grow p-8">{children}</main>
    </div>
  );
}

On this page