Skip to main content

Next.js

Learn how to quickly add authentication and user profile in your Next.js app using Hanko.

Install @teamhanko/hanko-elements

Once you've initialized your Next app, installing hanko-elements provides you with access to the prebuilt components: hanko-auth and hanko-profile.

npm install @teamhanko/hanko-elements

Add the Hanko API URL

Retrieve the API URL from the Hanko console and place it in your .env file.

.env.local
NEXT_PUBLIC_HANKO_API_URL=https://f4****-4802-49ad-8e0b-3d3****ab32.hanko.io

Add <hanko-auth> component

The <hanko-auth> web component adds a login interface to your app. Begin by importing the register function from @teamhanko/hanko-elements into your Next.js component. Call it with the Hanko API URL as an argument to register <hanko-auth> with the browser's CustomElementRegistry. Once done, include it in your JSX.

components/HankoAuth.tsx
"use client";

import { useEffect } from "react";
import { register } from "@teamhanko/hanko-elements";

const hankoApi = process.env.NEXT_PUBLIC_HANKO_API_URL;

export default function HankoAuth() {
useEffect(() => {
register(hankoApi).catch((error) => {
// handle error
});
}, []);

return <hanko-auth />;
}

Now simply import the component you just created (HankoAuth)

app/login/page.tsx
import HankoAuth from "@/components/HankoAuth";

export default function LoginPage() {
return (
<HankoAuth />
);
}

Define event callbacks

The Hanko client from @teamhanko/hanko-elements lets you "listen" for specific events. It simplifies the process of subscribing to events, such as user logins.

components/HankoAuth.tsx
"use client";

import { useEffect, useCallback, useState } from "react";
import { useRouter } from "next/navigation";
import { register, Hanko } from "@teamhanko/hanko-elements";

const hankoApi = process.env.NEXT_PUBLIC_HANKO_API_URL;

export default function HankoAuth() {
const router = useRouter();

const [hanko, setHanko] = useState<Hanko>();

useEffect(() => {
import("@teamhanko/hanko-elements").then(({ Hanko }) =>
setHanko(new Hanko(hankoApi))
);
}, []);

const redirectAfterLogin = useCallback(() => {
// successfully logged in, redirect to a page in your application
router.replace("/dashboard");
}, [router]);

useEffect(
() =>
hanko?.onAuthFlowCompleted(() => {
redirectAfterLogin();
}),
[hanko, redirectAfterLogin]
);

useEffect(() => {
register(hankoApi).catch((error) => {
// handle error
});
}, []);

return <hanko-auth />;
}

Add <hanko-profile> component

The <hanko-profile> component offers an interface for managing email addresses and passkeys.

components/HankoProfile.jsx
"use client"

import { useEffect } from "react";
import { register } from "@teamhanko/hanko-elements";

const hankoApi = process.env.NEXT_PUBLIC_HANKO_API_URL;

export default function HankoProfile() {
useEffect(() => {
register(hankoApi).catch((error) => {
// handle error
});
}, []);

return <hanko-profile />;
}

Implement logout functionality

You can use @teamhanko/hanko-elements to easily manage user logouts. Below, we make a logout button component that you can use anywhere.

components/LogoutButton.tsx
"use client";

import { useState, useEffect } from "react";
import { useRouter } from "next/navigation";
import { Hanko } from "@teamhanko/hanko-elements";

const hankoApi = process.env.NEXT_PUBLIC_HANKO_API_URL;

export function LogoutBtn() {
const router = useRouter();
const [hanko, setHanko] = useState<Hanko>();

useEffect(() => {
import("@teamhanko/hanko-elements").then(({ Hanko }) =>
setHanko(new Hanko(hankoApi ?? ""))
);
}, []);

const logout = async () => {
try {
await hanko?.user.logout();
router.push("/login");
router.refresh();
return;
} catch (error) {
console.error("Error during logout:", error);
}
};

return <button onClick={logout}>Logout</button>;
}

Customize component styles

You can customize the appearance of hanko-auth and hanko-profile components using CSS variables and parts. Refer to our customization guide.

Securing routes with Middleware

To add JWT verification middleware with Hanko in your Next.js application, we're using jose library. However, you're free to choose any other suitable library. This middleware will ensure secure access to specific routes, like /dashboard here, by checking for valid JWT. Here we define the Hanko API URL, extract and verify the JWT from cookies, and redirect unauthorized users to the login page.

middleware.ts
import { NextResponse, NextRequest } from "next/server";

import { jwtVerify, createRemoteJWKSet } from "jose";

const hankoApiUrl = process.env.NEXT_PUBLIC_HANKO_API_URL;

export async function middleware(req: NextRequest) {
const hanko = req.cookies.get("hanko")?.value;

const JWKS = createRemoteJWKSet(
new URL(`${hankoApiUrl}/.well-known/jwks.json`)
);

try {
const verifiedJWT = await jwtVerify(hanko ?? "", JWKS);
} catch {
return NextResponse.redirect(new URL("/login", req.url));
}
}

export const config = {
matcher: ["/dashboard"],
};