Usage & Integration
Practical examples of using Clerk authentication in Plainform components and pages
Learn how to use Clerk authentication in your Plainform application with practical examples.
Server Components
Access user data in Server Components using auth() or currentUser():
import { auth, currentUser } from '@clerk/nextjs/server';
import { redirect } from 'next/navigation';
export default async function DashboardPage() {
// Get user ID only (faster)
const { userId } = await auth();
if (!userId) {
redirect('/sign-in');
}
// Get full user object when needed
const user = await currentUser();
return (
<div>
<h1>Welcome, {user.firstName}!</h1>
<p>Email: {user.emailAddresses[0].emailAddress}</p>
</div>
);
}Check Permissions
Verify user roles or permissions:
import { auth } from '@clerk/nextjs/server';
export default async function AdminPage() {
const { userId, has } = await auth();
if (!userId || !has({ role: 'admin' })) {
return <div>Access denied</div>;
}
return <div>Admin dashboard</div>;
}Client Components
Use Clerk hooks in Client Components for reactive auth state:
'use client';
import { useUser } from '@clerk/nextjs';
export function UserProfile() {
const { user, isLoaded, isSignedIn } = useUser();
if (!isLoaded) return <div>Loading...</div>;
if (!isSignedIn) return <div>Please sign in</div>;
return (
<div className="flex items-center gap-3">
<img
src={user.imageUrl}
alt={user.fullName || 'User'}
className="h-10 w-10 rounded-full"
/>
<div>
<p className="font-semibold">{user.fullName}</p>
<p className="text-sm text-muted-foreground">
{user.primaryEmailAddress?.emailAddress}
</p>
</div>
</div>
);
}Conditional Rendering
Show different content based on auth state:
'use client';
import { useAuth } from '@clerk/nextjs';
import Link from 'next/link';
export function AuthContent() {
const { isSignedIn, isLoaded } = useAuth();
if (!isLoaded) return <div>Loading...</div>;
return isSignedIn ? (
<Link href="/dashboard">Go to Dashboard</Link>
) : (
<div className="flex gap-4">
<Link href="/sign-up">Create Account</Link>
<Link href="/sign-in">Sign In</Link>
</div>
);
}API Routes
Protect API routes and verify authentication:
import { auth } from '@clerk/nextjs/server';
import { NextResponse } from 'next/server';
export async function GET() {
const { userId } = await auth();
if (!userId) {
return new NextResponse('Unauthorized', { status: 401 });
}
const userData = await getUserData(userId);
return NextResponse.json(userData);
}Role-Based Access
Restrict API access by role:
import { auth } from '@clerk/nextjs/server';
import { NextResponse } from 'next/server';
export async function GET() {
const { userId, has } = await auth();
if (!userId || !has({ role: 'admin' })) {
return new NextResponse('Forbidden', { status: 403 });
}
const users = await getAllUsers();
return NextResponse.json(users);
}Server Actions
Verify authentication in Server Actions:
'use server';
import { auth } from '@clerk/nextjs/server';
import { revalidatePath } from 'next/cache';
import { prisma } from '@/lib/prisma/prisma';
export async function updateProfile(formData: FormData) {
const { userId } = await auth();
if (!userId) {
throw new Error('Unauthorized');
}
const name = formData.get('name') as string;
await prisma.user.update({
where: { clerkId: userId },
data: { name },
});
revalidatePath('/profile');
return { success: true };
}Custom Authentication Forms
Plainform uses custom sign-in and sign-up forms built with Clerk hooks for full design control. These are located in components/user/.
The only pre-built Clerk component used in Plainform is <UserProfile> for profile management. All auth forms are custom-built.
Custom Sign-In Form
The sign-in form uses useSignIn hook:
'use client';
import { useSignIn } from '@clerk/nextjs';
import { useState } from 'react';
import { useRouter } from 'next/navigation';
export function SignInForm() {
const { signIn, setActive, isLoaded } = useSignIn();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const router = useRouter();
if (!isLoaded) return <div>Loading...</div>;
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError('');
try {
const result = await signIn.create({
identifier: email,
password,
});
if (result.status === 'complete') {
await setActive({ session: result.createdSessionId });
router.push('/');
}
} catch (err: any) {
setError(err.errors?.[0]?.message || 'Sign in failed');
}
};
return (
<form onSubmit={handleSubmit}>
{error && <div className="text-destructive">{error}</div>}
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
required
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
required
/>
<button type="submit">Sign In</button>
</form>
);
}Key points:
signIn.create()validates credentialssetActive()establishes the session- Error handling via Clerk's error format
- Full control over UI and validation
Custom Sign-Up Form
The sign-up form uses useSignUp hook with email verification:
'use client';
import { useSignUp } from '@clerk/nextjs';
import { useState } from 'react';
import { useRouter } from 'next/navigation';
export function SignUpForm() {
const { signUp, setActive, isLoaded } = useSignUp();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [verifying, setVerifying] = useState(false);
const [code, setCode] = useState('');
const [error, setError] = useState('');
const router = useRouter();
if (!isLoaded) return <div>Loading...</div>;
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
setError('');
try {
await signUp.create({
emailAddress: email,
password,
});
// Send verification email
await signUp.prepareEmailAddressVerification({
strategy: 'email_code',
});
setVerifying(true);
} catch (err: any) {
setError(err.errors?.[0]?.message || 'Sign up failed');
}
};
const handleVerify = async (e: React.FormEvent) => {
e.preventDefault();
setError('');
try {
const result = await signUp.attemptEmailAddressVerification({
code,
});
if (result.status === 'complete') {
await setActive({ session: result.createdSessionId });
router.push('/');
}
} catch (err: any) {
setError(err.errors?.[0]?.message || 'Verification failed');
}
};
if (verifying) {
return (
<form onSubmit={handleVerify}>
{error && <div className="text-destructive">{error}</div>}
<input
type="text"
value={code}
onChange={(e) => setCode(e.target.value)}
placeholder="Verification code"
required
/>
<button type="submit">Verify Email</button>
</form>
);
}
return (
<form onSubmit={handleSubmit}>
{error && <div className="text-destructive">{error}</div>}
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
required
/>
<input
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
required
/>
<button type="submit">Sign Up</button>
</form>
);
}Key points:
- Two-step process: create account → verify email
prepareEmailAddressVerification()sends verification codeattemptEmailAddressVerification()validates the code- State management for verification flow
The actual implementations in components/user/ include additional features like OAuth buttons, form validation with Zod, and styled UI components. These examples show the core Clerk integration pattern.
Pre-Built Components
User Profile
Use Clerk's <UserProfile> component for profile management:
import { UserProfile } from '@clerk/nextjs';
export default function ProfilePage() {
return (
<div className="flex justify-center p-8">
<UserProfile
appearance={{
elements: {
rootBox: 'w-full max-w-2xl',
card: 'shadow-lg',
},
}}
/>
</div>
);
}User Button
Add a user menu button to your navigation:
'use client';
import { UserButton } from '@clerk/nextjs';
export function Navigation() {
return (
<nav className="flex items-center justify-between p-4">
<div>Logo</div>
<UserButton afterSignOutUrl="/" />
</nav>
);
}Session Tokens
Access session tokens for external API calls:
'use client';
import { useAuth } from '@clerk/nextjs';
export function ApiCall() {
const { getToken } = useAuth();
const fetchData = async () => {
const token = await getToken();
const response = await fetch('https://api.example.com/data', {
headers: {
Authorization: `Bearer ${token}`,
},
});
return response.json();
};
return <button onClick={fetchData}>Fetch Data</button>;
}Testing Authentication
After implementing authentication:
- Sign Up: Navigate to
/sign-upand create a test account - Email Verification: Check email for verification code (or use
424242in dev mode) - Sign In: Sign in at
/sign-inwith your credentials - Protected Routes: Try accessing protected pages without auth
- Sign Out: Test sign-out functionality
In development, Clerk provides test mode. Use code 424242 for email verification or check your terminal for magic links.
Next Steps
How is this guide ?
Last updated on