Next.js Frontend

TurboStack uses Next.js 16 with the App Router for a modern, performant frontend.

Key Features

App Router

File-based routing with layouts and nested routes

Turbopack

Lightning-fast development builds

React 19

Server Components, Actions, and latest features

TypeScript

Full type safety with strict mode

Directory Structure


apps/frontend/
├── app/
│ ├── (marketing)/ # Public pages
│ │ ├── page.tsx # Homepage
│ │ └── pricing/ # Pricing page
│ ├── (auth)/ # Auth pages (no header)
│ │ ├── login/
│ │ ├── register/
│ │ ├── forgot-password/
│ │ └── reset-password/
│ ├── (admin)/ # Protected pages
│ │ ├── dashboard/
│ │ ├── settings/
│ │ └── users/
│ ├── api/ # API routes
│ ├── context/ # React contexts
│ ├── layout.tsx # Root layout
│ └── globals.css # Global styles
├── components/
│ ├── ui/ # shadcn/ui components
│ └── ... # Custom components
├── lib/
│ ├── api.ts # API client
│ └── utils.ts # Utilities
└── hooks/ # Custom React hooks


Route Groups

Route groups organize your pages without affecting the URL structure:
Public pages with full header and footer.
/              → Homepage
/pricing       → Pricing page
/about         → About page
// app/(marketing)/layout.tsx
export default function MarketingLayout({ children }) {
  return (
    <>
      <Header />
      <main>{children}</main>
      <Footer />
    </>
  );
}

Authentication

Auth Context

The app uses React Context for client-side auth state:
"use client";

import { useAuth } from "@/app/context/auth-context";

export function UserMenu() {
  const { user, isLoading, logout } = useAuth();

  if (isLoading) return <Skeleton />;
  if (!user) return <LoginButton />;

  return (
    <DropdownMenu>
      <DropdownMenuTrigger>
        <Avatar src={user.avatar} fallback={user.name} />
      </DropdownMenuTrigger>
      <DropdownMenuContent>
        <DropdownMenuItem onClick={() => router.push("/settings")}>
          Settings
        </DropdownMenuItem>
        <DropdownMenuItem onClick={logout}>Logout</DropdownMenuItem>
      </DropdownMenuContent>
    </DropdownMenu>
  );
}

Server-Side Session

Check authentication in Server Components:
// app/(panel)/dashboard/page.tsx
import { getSession } from "@/lib/auth-server";
import { redirect } from "next/navigation";

export default async function DashboardPage() {
  const session = await getSession();

  if (!session) {
    redirect("/login");
  }

  return (
    <div>
      <h1>Welcome, {session.name}!</h1>
      <p>Role: {session.role}</p>
    </div>
  );
}

Protected Routes

Use the AuthGuard component for client-side protection:
import { AuthGuard } from "@/components/auth-guard";

export default function SettingsPage() {
  return (
    <AuthGuard requireEmailVerified>
      <SettingsForm />
    </AuthGuard>
  );
}
AuthGuard supports options like requireEmailVerified and allowedRoles= {["ADMIN"]} for fine-grained access control.

Environment Variables

Prefix with NEXT_PUBLIC_ to expose to browser:
NEXT_PUBLIC_API_URL=http://localhost:4101
NEXT_PUBLIC_APP_NAME=TurboStack
const apiUrl = process.env.NEXT_PUBLIC_API_URL;

Adding New Pages

1

Create the Page File

// app/(marketing)/features/page.tsx
export default function FeaturesPage() {
  return (
    <div className="container mx-auto py-12">
      <h1 className="text-4xl font-bold">Features</h1>
      <p>Discover what TurboStack offers.</p>
    </div>
  );
}
2

Add Metadata (Optional)

import type { Metadata } from "next";

export const metadata: Metadata = {
  title: "Features | TurboStack",
  description: "Explore TurboStack features",
};
3

Update Navigation

Add link to your header/sidebar navigation.

Development

Start Frontend

bash bun run dev:web

Build

bash cd apps/frontend && bun run build
The web app runs on http://localhost:4100.
Use Server Actions for mutations and client-side fetching with useAuth context for queries to leverage Next.js optimizations.