Next.js Client
Connect your Next.js app to the Cloudflare Workers authentication API
Prerequisites
This guide assumes you have deployed the Hono server to Cloudflare Workers.
Installation
npx shadcn@latest add https://snippets.thedevdavid.com/r/hono-cloudflare-better-auth-client-nextjs.json
Configuration
1. Environment Variables
NEXT_PUBLIC_API_URL=https://api.yourdomain.com
# Or for development
NEXT_PUBLIC_API_URL=http://localhost:8787
2. Middleware Setup
The middleware protects routes and refreshes sessions:
export default authMiddleware({
protectedRoutes: ["/dashboard", "/settings"],
redirectTo: "/login",
});
Usage
Client Components
"use client";
import { useSession, signOut } from "@/lib/auth-client";
import { useRouter } from "next/navigation";
export function AuthButton() {
const { data: session, loading } = useSession();
const router = useRouter();
if (loading) return <div>Loading...</div>;
if (!session) {
return <button onClick={() => router.push("/login")}>Sign In</button>;
}
return (
<div>
<span>{session.user.email}</span>
<button onClick={() => signOut()}>Sign Out</button>
</div>
);
}
Server Components
import { getBetterAuthSession } from "@/lib/get-session";
import { redirect } from "next/navigation";
export default async function Dashboard() {
const session = await getBetterAuthSession();
if (!session) {
redirect("/login");
}
return <h1>Welcome {session.user.email}!</h1>;
}
Authentication Forms
"use client";
import { signIn } from "@/lib/auth-client";
import { useRouter } from "next/navigation";
import { useState } from "react";
export default function LoginPage() {
const [error, setError] = useState("");
const router = useRouter();
async function handleSubmit(e: React.FormEvent<HTMLFormElement>) {
e.preventDefault();
setError("");
const formData = new FormData(e.currentTarget);
try {
await signIn.email({
email: formData.get("email") as string,
password: formData.get("password") as string,
});
router.push("/dashboard");
} catch (err) {
setError("Invalid credentials");
}
}
return (
<form onSubmit={handleSubmit}>
<input name="email" type="email" required />
<input name="password" type="password" required />
{error && <p>{error}</p>}
<button type="submit">Sign In</button>
</form>
);
}
API Routes
Protected API routes example:
import { getBetterAuthSession } from "@/lib/get-session";
import { NextResponse } from "next/server";
export async function GET() {
const session = await getBetterAuthSession();
if (!session) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
return NextResponse.json({ user: session.user });
}
Common Patterns
Protected Pages
import { getBetterAuthSession } from "@/lib/get-session";
import { redirect } from "next/navigation";
export default async function Settings() {
const session = await getBetterAuthSession();
if (!session) redirect("/login");
// Your protected content
return <div>Settings for {session.user.email}</div>;
}
Custom Hook
import { useSession, signOut } from "@/lib/auth-client";
import { useRouter } from "next/navigation";
export function useAuth() {
const session = useSession();
const router = useRouter();
const logout = async () => {
await signOut();
router.push("/");
};
return {
user: session.data?.user,
loading: session.loading,
logout,
};
}
Resources
How is this guide?
Last updated: 6/2/2025