We use tracking cookies to understand how you use the product and help us improve it. For more information on how we store cookies, read our  privacy policy.

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():

app/dashboard/page.tsx
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:

app/admin/page.tsx
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:

components/UserProfile.tsx
'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:

components/AuthContent.tsx
'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:

app/api/user/route.ts
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:

app/api/admin/users/route.ts
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:

actions/updateProfile.ts
'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:

components/user/SignInForm.tsx (simplified)
'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 credentials
  • setActive() 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:

components/user/SignUpForm.tsx (simplified)
'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 code
  • attemptEmailAddressVerification() 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:

app/profile/page.tsx
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:

components/Navigation.tsx
'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:

components/ApiCall.tsx
'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:

  1. Sign Up: Navigate to /sign-up and create a test account
  2. Email Verification: Check email for verification code (or use 424242 in dev mode)
  3. Sign In: Sign in at /sign-in with your credentials
  4. Protected Routes: Try accessing protected pages without auth
  5. 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