Skip to main content

Next.js Integration

This guide demonstrates how to integrate Crossmint Auth and Wallets in a Next.js application using the App Router with server-side rendering.

Demo Repository

Next.js SSR Demo

Complete working example with authentication and wallet integration

Project Structure

app/
├── api/
│   ├── refresh/
│   │   └── route.ts       # Token refresh endpoint
│   └── logout/
│       └── route.ts       # Logout endpoint
├── components/
│   └── Login.tsx          # Login component
├── providers/
│   └── CrossmintProviders.tsx
├── hooks/
│   └── auth.ts            # Auth helper functions
├── middleware.ts          # Auth middleware
└── page.tsx              # Home page

Installation

npm install @crossmint/client-sdk-react-ui @crossmint/server-sdk

Environment Variables

Create a .env.local file:
# Client-side API key
NEXT_PUBLIC_CLIENT_CROSSMINT_API_KEY=your_client_api_key

# Server-side API key
SERVER_CROSSMINT_API_KEY=your_server_api_key

# Application URL
NEXT_PUBLIC_APP_URL=http://localhost:3000
Get your API keys from the Crossmint Console. You need both client-side and server-side keys.

Setup Providers

Create a providers component to wrap your application:
"use client";

import type { ReactNode } from "react";
import { CrossmintProvider, CrossmintAuthProvider } from "@crossmint/client-sdk-react-ui";

export default function CrossmintProviders({ children }: { children: ReactNode }) {
    return (
        <CrossmintProvider apiKey={process.env.NEXT_PUBLIC_CLIENT_CROSSMINT_API_KEY ?? ""}>
            <CrossmintAuthProvider
                loginMethods={["email", "google"]} // Configure login methods
                refreshRoute="/api/refresh"
                logoutRoute="/api/logout"
            >
                {children}
            </CrossmintAuthProvider>
        </CrossmintProvider>
    );
}

Create API Routes

Refresh Token Route

import { createCrossmint, CrossmintAuth } from "@crossmint/server-sdk";
import type { NextRequest } from "next/server";

export async function POST(request: NextRequest) {
    try {
        const crossmint = createCrossmint({
            apiKey: process.env.SERVER_CROSSMINT_API_KEY || "",
        });
        const crossmintAuth = CrossmintAuth.from(crossmint, {
            cookieOptions: {
                httpOnly: true,
            },
        });
        const response = await crossmintAuth.handleCustomRefresh(request);

        return response as Response;
    } catch (error) {
        console.error(error);
    }
}

Logout Route

import { createCrossmint, CrossmintAuth } from "@crossmint/server-sdk";
import type { NextRequest } from "next/server";

export async function POST(request: NextRequest) {
    try {
        const crossmint = createCrossmint({
            apiKey: process.env.SERVER_CROSSMINT_API_KEY || "",
        });
        const crossmintAuth = CrossmintAuth.from(crossmint);
        const response = await crossmintAuth.logout(request);

        return response as Response;
    } catch (error) {
        console.error(error);
    }
}

Authentication Helpers

Create a helper function to get the auth session server-side:
import { cookies } from "next/headers";
import { createCrossmint, CrossmintAuth } from "@crossmint/server-sdk";

export async function getAuthSession(refreshRoute?: string) {
    const cookieStore = await cookies();
    const jwt = cookieStore.get("crossmint-jwt")?.value;
    const refreshToken = cookieStore.get("crossmint-refresh-token")?.value;

    if (!refreshToken) {
        return;
    }

    try {
        const crossmint = createCrossmint({
            apiKey: process.env.SERVER_CROSSMINT_API_KEY || "",
        });
        const crossmintAuth = CrossmintAuth.from(crossmint, {
            refreshRoute,
        });

        const session = await crossmintAuth.getSession({
            jwt,
            refreshToken,
        });
        return session;
    } catch (_) {
        return;
    }
}

Login Component

Create a client component for authentication:
"use client";

import { useState } from "react";
import { useAuth } from "@crossmint/client-sdk-react-ui";
import { useRouter } from "next/navigation";

export default function Login() {
    const router = useRouter();
    const { login, logout, user, status } = useAuth();
    const [isLoading, setIsLoading] = useState(false);

    const handleLogin = async () => {
        setIsLoading(true);
        try {
            await login();
            router.refresh();
        } catch (error) {
            console.error("Login failed:", error);
        } finally {
            setIsLoading(false);
        }
    };

    const handleLogout = async () => {
        setIsLoading(true);
        try {
            await logout();
            router.refresh();
        } catch (error) {
            console.error("Logout failed:", error);
        } finally {
            setIsLoading(false);
        }
    };

    return (
        <div className="flex flex-col items-center gap-4">
            {status === "logged-in" ? (
                <>
                    <p>Welcome, User ID: {user?.id}</p>
                    <button
                        onClick={handleLogout}
                        disabled={isLoading}
                        className="px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600 disabled:opacity-50"
                    >
                        {isLoading ? "Logging out..." : "Logout"}
                    </button>
                </>
            ) : (
                <button
                    onClick={handleLogin}
                    disabled={isLoading}
                    className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 disabled:opacity-50"
                >
                    {isLoading ? "Logging in..." : "Login with Crossmint"}
                </button>
            )}
        </div>
    );
}

Main Page

Implement server-side authentication in your page:
import CrossmintProviders from "@/providers/CrossmintProviders";
import Login from "@/components/Login";
import { getAuthSession } from "@/hooks/auth";

export default async function Home() {
    const refreshRoute = `${process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000"}/api/refresh`;
    const session = await getAuthSession(refreshRoute);
    const userId = session?.userId;

    return (
        <div className="min-h-screen p-8">
            <main className="flex flex-col gap-8 items-center">
                <h1 className="text-2xl font-bold">
                    Welcome to Crossmint Auth Next.js Demo
                </h1>
                {userId ? (
                    <div>
                        <p>You are logged in!</p>
                        <p>Your user ID is: {userId}</p>
                    </div>
                ) : null}

                <CrossmintProviders>
                    <Login />
                </CrossmintProviders>
            </main>
        </div>
    );
}

Middleware (Optional)

Add middleware for protected routes:
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
import { createCrossmint, CrossmintAuth } from "@crossmint/server-sdk";

// Only run middleware on specific routes
export const config = {
    matcher: "/protected/:path*",
    runtime: "nodejs",
};

export async function middleware(request: NextRequest) {
    // Skip middleware for API routes and static files
    if (request.nextUrl.pathname.startsWith("/api") || request.nextUrl.pathname.startsWith("/_next")) {
        return NextResponse.next();
    }

    const response = NextResponse.next();

    const jwt = request.cookies.get("crossmint-jwt")?.value;
    const refreshToken = request.cookies.get("crossmint-refresh-token")?.value;

    if (!refreshToken) {
        return response;
    }

    try {
        const crossmint = createCrossmint({
            apiKey: process.env.SERVER_CROSSMINT_API_KEY || "",
        });
        const crossmintAuth = CrossmintAuth.from(crossmint);

        const { jwt: newJwt, refreshToken: newRefreshToken } = await crossmintAuth.getSession({
            jwt,
            refreshToken,
        });

        // Only update response cookies if tokens have changed
        if (newJwt !== jwt || newRefreshToken.secret !== refreshToken) {
            response.cookies.set("crossmint-jwt", newJwt);
            response.cookies.set("crossmint-refresh-token", newRefreshToken.secret);
        }
    } catch (_) {
        // If auth fails, clear cookies and redirect to home
        response.cookies.delete("crossmint-jwt");
        response.cookies.delete("crossmint-refresh-token");
    }

    return response;
}

Next Steps

Add Wallets

Learn how to create and manage wallets

Server-Side Auth

Implement server-side authentication

React Native

Build mobile apps with Crossmint

API Reference

Explore the full API

Troubleshooting

  • Verify your API keys are correct
  • Check that you’re using the right environment (staging vs production)
  • Ensure cookies are being set correctly (check browser dev tools)
Make sure your domain is whitelisted in the Crossmint Console under your project settings.
  • Verify the refresh route is configured correctly
  • Check that httpOnly cookies are enabled in production
  • Ensure the refresh endpoint is accessible

Build docs developers (and LLMs) love