CROP
ProjectsCROP Frontend

Clerk Authentication Setup

Application: CROP Public App (crop-front-mu.vercel.app) Last Updated: 2025-12-03 Status: ✅ Implemented (Basic Auth) Provider: Clerk(https://clerk.com/)

Clerk Authentication Setup

Application: CROP Public App (crop-front-mu.vercel.app) Last Updated: 2025-12-03 Status: ✅ Implemented (Basic Auth) Provider: Clerk

Current Implementation

FeatureStatus
ClerkProvider in layout✅ Done
Sign-in/Sign-up pages✅ Done
AuthButtons component✅ Done
Middleware (proxy.ts)✅ Done
Public routes config✅ Done
Shopping cart (no auth required)✅ Done

Protected Routes (Future)

  • /checkout/* - requires sign-in
  • /account/* - requires sign-in
  • /orders/* - requires sign-in

🔑 Quick Start

1. Install Dependencies

bun add @clerk/nextjs

2. Environment Variables

Add these to .env.local:

# Clerk Authentication
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=YOUR_PUBLISHABLE_KEY
CLERK_SECRET_KEY=YOUR_SECRET_KEY

Get your keys from Clerk Dashboard → API Keys

⚠️ IMPORTANT: These are placeholders. Get actual keys from your Clerk Dashboard.

3. Create Middleware

⚠️ IMPORTANT: For Next.js 16+, use middleware.ts. For Next.js ≤15, use proxy.ts. The code is identical - only the filename differs.

// middleware.ts (Next.js 16+) or proxy.ts (Next.js ≤15)
import { clerkMiddleware } from "@clerk/nextjs/server";

export default clerkMiddleware();

export const config = {
  matcher: [
    // Skip Next.js internals and all static files, unless found in search params
    "/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)",
    // Always run for API routes
    "/(api|trpc)(.*)",
  ],
};

4. Wrap App with ClerkProvider

// app/layout.tsx
import type { Metadata } from "next";
import { ClerkProvider } from "@clerk/nextjs";
import "./globals.css";

export const metadata: Metadata = {
  title: "Clinton Tractor Parts Store",
  description: "Premium agricultural equipment parts",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <ClerkProvider>
      <html lang="en">
        <body>{children}</body>
      </html>
    </ClerkProvider>
  );
}

🎨 UI Components

Sign In/Sign Up Buttons

// components/auth/auth-buttons.tsx
"use client";

import {
  SignInButton,
  SignUpButton,
  SignedIn,
  SignedOut,
  UserButton,
} from "@clerk/nextjs";
import { Button } from "@/components/ui/button";

export function AuthButtons() {
  return (
    <>
      <SignedOut>
        <div className="flex gap-2">
          <SignInButton mode="modal">
            <Button variant="outline">Sign In</Button>
          </SignInButton>
          <SignUpButton mode="modal">
            <Button>Sign Up</Button>
          </SignUpButton>
        </div>
      </SignedOut>
      <SignedIn>
        <UserButton afterSignOutUrl="/" />
      </SignedIn>
    </>
  );
}

Conditional Rendering

// app/page.tsx
import { SignedIn, SignedOut } from "@clerk/nextjs";

export default function HomePage() {
  return (
    <div>
      <SignedOut>
        <h1>Welcome! Please sign in to continue.</h1>
      </SignedOut>
      <SignedIn>
        <h1>Welcome back!</h1>
      </SignedIn>
    </div>
  );
}

🔒 Protecting Routes

Server-Side Protection

// app/dashboard/page.tsx
import { auth } from "@clerk/nextjs/server";
import { redirect } from "next/navigation";

export default async function DashboardPage() {
  const { userId } = await auth();

  if (!userId) {
    redirect("/sign-in");
  }

  return (
    <div>
      <h1>Protected Dashboard</h1>
      <p>User ID: {userId}</p>
    </div>
  );
}

Client-Side Protection

// components/protected-content.tsx
"use client";

import { useAuth } from "@clerk/nextjs";
import { useRouter } from "next/navigation";
import { useEffect } from "react";

export function ProtectedContent({ children }: { children: React.ReactNode }) {
  const { isLoaded, userId } = useAuth();
  const router = useRouter();

  useEffect(() => {
    if (isLoaded && !userId) {
      router.push("/sign-in");
    }
  }, [isLoaded, userId, router]);

  if (!isLoaded) {
    return <div>Loading...</div>;
  }

  if (!userId) {
    return null;
  }

  return <>{children}</>;
}

Middleware-Based Protection

// middleware.ts (Next.js 16+) or proxy.ts (Next.js ≤15)
import { clerkMiddleware, createRouteMatcher } from "@clerk/nextjs/server";

const isProtectedRoute = createRouteMatcher([
  "/dashboard(.*)",
  "/account(.*)",
  "/orders(.*)",
]);

export default clerkMiddleware(async (auth, req) => {
  if (isProtectedRoute(req)) {
    await auth.protect();
  }
});

export const config = {
  matcher: [
    "/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)",
    "/(api|trpc)(.*)",
  ],
};

👤 User Data Access

Server Components

// app/profile/page.tsx
import { currentUser } from "@clerk/nextjs/server";

export default async function ProfilePage() {
  const user = await currentUser();

  if (!user) return null;

  return (
    <div>
      <h1>Profile</h1>
      <p>Name: {user.firstName} {user.lastName}</p>
      <p>Email: {user.emailAddresses[0]?.emailAddress}</p>
      <img src={user.imageUrl} alt="Profile" />
    </div>
  );
}

Client Components

"use client";

import { useUser } from "@clerk/nextjs";

export function UserProfile() {
  const { isLoaded, isSignedIn, user } = useUser();

  if (!isLoaded) return <div>Loading...</div>;
  if (!isSignedIn) return null;

  return (
    <div>
      <p>Hello, {user.firstName}!</p>
      <p>{user.emailAddresses[0]?.emailAddress}</p>
    </div>
  );
}

🎨 Custom Sign In/Sign Up Pages

Create Sign In Page

// app/sign-in/[[...sign-in]]/page.tsx
import { SignIn } from "@clerk/nextjs";

export default function SignInPage() {
  return (
    <div className="flex min-h-screen items-center justify-center">
      <SignIn
        appearance={{
          elements: {
            rootBox: "mx-auto",
            card: "shadow-lg",
          },
        }}
        routing="path"
        path="/sign-in"
        signUpUrl="/sign-up"
        afterSignInUrl="/dashboard"
      />
    </div>
  );
}

Create Sign Up Page

// app/sign-up/[[...sign-up]]/page.tsx
import { SignUp } from "@clerk/nextjs";

export default function SignUpPage() {
  return (
    <div className="flex min-h-screen items-center justify-center">
      <SignUp
        appearance={{
          elements: {
            rootBox: "mx-auto",
            card: "shadow-lg",
          },
        }}
        routing="path"
        path="/sign-up"
        signInUrl="/sign-in"
        afterSignUpUrl="/dashboard"
      />
    </div>
  );
}

Update Environment Variables

Add these to .env.local to configure custom sign-in/sign-up pages:

# Custom page URLs
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up

# Fallback redirect URLs (where to go after authentication)
NEXT_PUBLIC_CLERK_SIGN_IN_FALLBACK_REDIRECT_URL=/
NEXT_PUBLIC_CLERK_SIGN_UP_FALLBACK_REDIRECT_URL=/
NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/dashboard
NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/dashboard

These environment variables tell Clerk:

  • Where your sign-in/sign-up pages are located
  • Where to redirect users after successful authentication
  • Fallback URLs if users visit the pages directly

🎨 Theming & Customization

Dark Mode Support

// app/layout.tsx
import { ClerkProvider } from "@clerk/nextjs";
import { dark } from "@clerk/themes";

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <ClerkProvider
      appearance={{
        baseTheme: dark, // or undefined for light mode
        variables: {
          colorPrimary: "#3b82f6", // Your brand color
          colorBackground: "#ffffff",
          colorText: "#000000",
        },
        elements: {
          formButtonPrimary: "bg-primary hover:bg-primary/90",
          card: "shadow-lg rounded-lg",
        },
      }}
    >
      <html lang="en">
        <body>{children}</body>
      </html>
    </ClerkProvider>
  );
}

🔐 API Route Protection

// app/api/protected/route.ts
import { auth } from "@clerk/nextjs/server";
import { NextResponse } from "next/server";

export async function GET() {
  const { userId } = await auth();

  if (!userId) {
    return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
  }

  // Your protected logic here
  return NextResponse.json({ message: "Protected data", userId });
}

🧪 Testing

Local Development

  1. Sign up for Clerk at clerk.com
  2. Create a new application
  3. Copy API keys from Dashboard → API Keys
  4. Add keys to .env.local
  5. Run bun dev
  6. Navigate to http://localhost:3000
  7. Test sign in/sign up flow

Production Deployment

  1. Add environment variables to Vercel:
    • NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY
    • CLERK_SECRET_KEY
    • Optional: Custom redirect URLs
  2. Deploy to Vercel
  3. Update allowed domains in Clerk Dashboard
  4. Test authentication flow

🐛 Troubleshooting

"Clerk: Missing publishableKey"

  • ✅ Verify NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY is set
  • ✅ Restart dev server after adding env variables
  • ✅ Check env variable name (must start with NEXT_PUBLIC_)

"Clerk: Missing secretKey"

  • ✅ Verify CLERK_SECRET_KEY is set in .env.local
  • ✅ Never expose secret key in client-side code
  • ✅ Restart dev server

Redirect loops

  • ✅ Check middleware matcher config
  • ✅ Ensure sign-in/sign-up pages are not protected
  • ✅ Verify redirect URLs in Clerk Dashboard

Styling issues

  • ✅ Use appearance prop on <ClerkProvider>
  • ✅ Check CSS class conflicts
  • ✅ Use Clerk's theme customization

📦 Required Dependencies

{
  "dependencies": {
    "@clerk/nextjs": "latest"
  }
}


📋 Implementation Checklist

  • Install @clerk/nextjs package
  • Create Clerk account and application
  • Add environment variables to .env.local
  • Create proxy.ts with clerkMiddleware()
  • Wrap app with <ClerkProvider> in app/layout.tsx
  • Create auth buttons component
  • Test sign in/sign up flow locally
  • Create custom sign-in/sign-up pages
  • Protect sensitive routes (checkout, account, orders)
  • Add user profile display
  • Configure appearance/theming
  • Add environment variables to Vercel
  • Test production deployment
  • Update allowed domains in Clerk Dashboard

⚠️ CRITICAL NOTES

DO ✅

  • Use clerkMiddleware() from @clerk/nextjs/server
  • Import from @clerk/nextjs or @clerk/nextjs/server
  • Use App Router approach (not Pages Router)
  • Store keys only in .env.local (never in code)
  • Use placeholders in documentation
  • Keep CLERK_SECRET_KEY server-side only

DO NOT ❌

  • Never use deprecated authMiddleware() (outdated)
  • Never use _app.tsx or Pages Router patterns
  • Never commit real keys to git
  • Never expose CLERK_SECRET_KEY in client-side code
  • Never reference old Clerk v4 or earlier patterns

🎯 Next Steps

  1. Basic Auth: Follow Quick Start guide above
  2. Custom UI: Implement custom sign-in/sign-up pages
  3. User Management: Add profile editing, account settings
  4. Role-Based Access: Implement user roles and permissions
  5. Webhooks: Set up Clerk webhooks for user events
  6. Analytics: Track authentication metrics

Last updated: 2025-12-03 For questions about Clerk setup, refer to official documentation

On this page