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
| Feature | Status |
|---|---|
| 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/nextjs2. Environment Variables
Add these to .env.local:
# Clerk Authentication
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=YOUR_PUBLISHABLE_KEY
CLERK_SECRET_KEY=YOUR_SECRET_KEYGet 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, useproxy.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=/dashboardThese 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
- Sign up for Clerk at clerk.com
- Create a new application
- Copy API keys from Dashboard → API Keys
- Add keys to
.env.local - Run
bun dev - Navigate to
http://localhost:3000 - Test sign in/sign up flow
Production Deployment
- Add environment variables to Vercel:
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEYCLERK_SECRET_KEY- Optional: Custom redirect URLs
- Deploy to Vercel
- Update allowed domains in Clerk Dashboard
- Test authentication flow
🐛 Troubleshooting
"Clerk: Missing publishableKey"
- ✅ Verify
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEYis set - ✅ Restart dev server after adding env variables
- ✅ Check env variable name (must start with
NEXT_PUBLIC_)
"Clerk: Missing secretKey"
- ✅ Verify
CLERK_SECRET_KEYis 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
appearanceprop on<ClerkProvider> - ✅ Check CSS class conflicts
- ✅ Use Clerk's theme customization
📦 Required Dependencies
{
"dependencies": {
"@clerk/nextjs": "latest"
}
}🔗 Useful Links
📋 Implementation Checklist
- Install
@clerk/nextjspackage - Create Clerk account and application
- Add environment variables to
.env.local - Create
proxy.tswithclerkMiddleware() - Wrap app with
<ClerkProvider>inapp/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/nextjsor@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_KEYserver-side only
DO NOT ❌
- Never use deprecated
authMiddleware()(outdated) - Never use
_app.tsxor Pages Router patterns - Never commit real keys to git
- Never expose
CLERK_SECRET_KEYin client-side code - Never reference old Clerk v4 or earlier patterns
🎯 Next Steps
- Basic Auth: Follow Quick Start guide above
- Custom UI: Implement custom sign-in/sign-up pages
- User Management: Add profile editing, account settings
- Role-Based Access: Implement user roles and permissions
- Webhooks: Set up Clerk webhooks for user events
- Analytics: Track authentication metrics
Last updated: 2025-12-03 For questions about Clerk setup, refer to official documentation