# Custom auth pages

This guide explains how to build your own auth pages (login, register, forgot password, reset password, confirm email, logout) so they match your site’s layout and style. For a basic site you can keep using the [default auth page](#default-auth-page).

## Overview

You can use a **custom page for every auth flow**:

| Page            | Component             | Server action(s)                    |
|-----------------|-----------------------|-------------------------------------|
| Login           | `LoginPage`           | `loginAction`, `setSessionCookie`   |
| Register        | `RegisterPage`       | `registerAction`                    |
| Forgot password | `ForgotPasswordPage` | `forgotPasswordAction`              |
| Reset password  | `ResetPasswordPage`  | `resetPasswordAction`               |
| Confirm email   | `ConfirmEmailPage`   | `verifyEmailAction`                 |
| Logout          | `LogoutPage`         | `logoutAction`, `setSessionCookie`  |

- **Components**: from `@zen/core/auth/components`
- **Actions**: from `@zen/core/auth/actions`

Create your own routes (e.g. `/login`, `/register`, `/auth/forgot`) and wrap Zen’s components in your layout. Each page follows the same pattern: a **server component** that loads data and passes actions, and a **client wrapper** that handles navigation and renders the Zen component.

---

## Route structure

Choose a URL scheme and use it consistently. Two common options:

**Option A – All under `/auth/*` (like the default)**  
`/auth/login`, `/auth/register`, `/auth/forgot`, `/auth/reset`, `/auth/confirm`, `/auth/logout`

**Option B – Top-level routes**  
`/login`, `/register`, `/forgot`, `/reset`, `/confirm`, `/logout`

The `onNavigate` callback receives one of: `'login' | 'register' | 'forgot' | 'reset'`. Map each to your chosen path, e.g. `router.push(\`/auth/${page}\`)` or `router.push(\`/${page}\`)`.

Reset and confirm pages need `email` and `token` from the URL (e.g. `/auth/reset?email=...&token=...`). Your server page can read `searchParams` and pass them to the component.

---

## Component reference (props)

Use this when wiring each custom page.

| Component             | Props |
|-----------------------|--------|
| **LoginPage**         | `onSubmit` (loginAction), `onSetSessionCookie`, `onNavigate`, `redirectAfterLogin`, `currentUser` |
| **RegisterPage**      | `onSubmit` (registerAction), `onNavigate`, `currentUser` |
| **ForgotPasswordPage**| `onSubmit` (forgotPasswordAction), `onNavigate`, `currentUser` |
| **ResetPasswordPage** | `onSubmit` (resetPasswordAction), `onNavigate`, `email`, `token` (from URL) |
| **ConfirmEmailPage**  | `onSubmit` (verifyEmailAction), `onNavigate`, `email`, `token` (from URL) |
| **LogoutPage**        | `onLogout` (logoutAction), `onSetSessionCookie` (optional) |

---

## 1. Login

**Server:** `app/login/page.js` (or `app/auth/login/page.js`)

```js
import { getSession, loginAction, setSessionCookie } from '@zen/core/auth/actions';
import { LoginPageWrapper } from './LoginPageWrapper';

export default async function LoginRoute() {
  const session = await getSession();
  return (
    <YourSiteLayout>
      <LoginPageWrapper
        loginAction={loginAction}
        setSessionCookie={setSessionCookie}
        currentUser={session?.user ?? null}
      />
    </YourSiteLayout>
  );
}
```

**Client:** `app/login/LoginPageWrapper.js`

```js
'use client';

import { useRouter } from 'next/navigation';
import { LoginPage } from '@zen/core/auth/components';

export function LoginPageWrapper({ loginAction, setSessionCookie, currentUser }) {
  const router = useRouter();
  return (
    <LoginPage
      onSubmit={loginAction}
      onSetSessionCookie={setSessionCookie}
      onNavigate={(page) => router.push(`/auth/${page}`)}
      redirectAfterLogin="/"
      currentUser={currentUser}
    />
  );
}
```

---

## 2. Register

**Server:** `app/register/page.js`

```js
import { getSession, registerAction } from '@zen/core/auth/actions';
import { RegisterPageWrapper } from './RegisterPageWrapper';

export default async function RegisterRoute() {
  const session = await getSession();
  return (
    <YourSiteLayout>
      <RegisterPageWrapper
        registerAction={registerAction}
        currentUser={session?.user ?? null}
      />
    </YourSiteLayout>
  );
}
```

**Client:** `app/register/RegisterPageWrapper.js`

```js
'use client';

import { useRouter } from 'next/navigation';
import { RegisterPage } from '@zen/core/auth/components';

export function RegisterPageWrapper({ registerAction, currentUser }) {
  const router = useRouter();
  return (
    <RegisterPage
      onSubmit={registerAction}
      onNavigate={(page) => router.push(`/auth/${page}`)}
      currentUser={currentUser}
    />
  );
}
```

---

## 3. Forgot password

**Server:** `app/forgot/page.js`

```js
import { getSession, forgotPasswordAction } from '@zen/core/auth/actions';
import { ForgotPasswordPageWrapper } from './ForgotPasswordPageWrapper';

export default async function ForgotRoute() {
  const session = await getSession();
  return (
    <YourSiteLayout>
      <ForgotPasswordPageWrapper
        forgotPasswordAction={forgotPasswordAction}
        currentUser={session?.user ?? null}
      />
    </YourSiteLayout>
  );
}
```

**Client:** `app/forgot/ForgotPasswordPageWrapper.js`

```js
'use client';

import { useRouter } from 'next/navigation';
import { ForgotPasswordPage } from '@zen/core/auth/components';

export function ForgotPasswordPageWrapper({ forgotPasswordAction, currentUser }) {
  const router = useRouter();
  return (
    <ForgotPasswordPage
      onSubmit={forgotPasswordAction}
      onNavigate={(page) => router.push(`/auth/${page}`)}
      currentUser={currentUser}
    />
  );
}
```

---

## 4. Reset password

Requires `email` and `token` from the reset link (e.g. `/auth/reset?email=...&token=...`). Read `searchParams` in the server component and pass them to the client.

**Server:** `app/auth/reset/page.js` (or `app/reset/page.js` with dynamic segment if needed)

```js
import { resetPasswordAction } from '@zen/core/auth/actions';
import { ResetPasswordPageWrapper } from './ResetPasswordPageWrapper';

export default async function ResetRoute({ searchParams }) {
  const params = typeof searchParams?.then === 'function' ? await searchParams : searchParams ?? {};
  const email = params.email ?? '';
  const token = params.token ?? '';
  return (
    <YourSiteLayout>
      <ResetPasswordPageWrapper
        resetPasswordAction={resetPasswordAction}
        email={email}
        token={token}
      />
    </YourSiteLayout>
  );
}
```

**Client:** `app/auth/reset/ResetPasswordPageWrapper.js`

```js
'use client';

import { useRouter } from 'next/navigation';
import { ResetPasswordPage } from '@zen/core/auth/components';

export function ResetPasswordPageWrapper({ resetPasswordAction, email, token }) {
  const router = useRouter();
  return (
    <ResetPasswordPage
      onSubmit={resetPasswordAction}
      onNavigate={(page) => router.push(`/auth/${page}`)}
      email={email}
      token={token}
    />
  );
}
```

---

## 5. Confirm email

Requires `email` and `token` from the verification link (e.g. `/auth/confirm?email=...&token=...`).

**Server:** `app/auth/confirm/page.js`

```js
import { verifyEmailAction } from '@zen/core/auth/actions';
import { ConfirmEmailPageWrapper } from './ConfirmEmailPageWrapper';

export default async function ConfirmRoute({ searchParams }) {
  const params = typeof searchParams?.then === 'function' ? await searchParams : searchParams ?? {};
  const email = params.email ?? '';
  const token = params.token ?? '';
  return (
    <YourSiteLayout>
      <ConfirmEmailPageWrapper
        verifyEmailAction={verifyEmailAction}
        email={email}
        token={token}
      />
    </YourSiteLayout>
  );
}
```

**Client:** `app/auth/confirm/ConfirmEmailPageWrapper.js`

```js
'use client';

import { useRouter } from 'next/navigation';
import { ConfirmEmailPage } from '@zen/core/auth/components';

export function ConfirmEmailPageWrapper({ verifyEmailAction, email, token }) {
  const router = useRouter();
  return (
    <ConfirmEmailPage
      onSubmit={verifyEmailAction}
      onNavigate={(page) => router.push(`/auth/${page}`)}
      email={email}
      token={token}
    />
  );
}
```

---

## 6. Logout

**Server:** `app/auth/logout/page.js`

```js
import { logoutAction, setSessionCookie } from '@zen/core/auth/actions';
import { LogoutPageWrapper } from './LogoutPageWrapper';

export default function LogoutRoute() {
  return (
    <YourSiteLayout>
      <LogoutPageWrapper
        logoutAction={logoutAction}
        setSessionCookie={setSessionCookie}
      />
    </YourSiteLayout>
  );
}
```

**Client:** `app/auth/logout/LogoutPageWrapper.js`

```js
'use client';

import { LogoutPage } from '@zen/core/auth/components';

export function LogoutPageWrapper({ logoutAction, setSessionCookie }) {
  return (
    <LogoutPage
      onLogout={logoutAction}
      onSetSessionCookie={setSessionCookie}
    />
  );
}
```

---

## Protecting routes

Use `protect()` from `@zen/core/auth` and set `redirectTo` to your custom login path:

```js
import { protect } from '@zen/core/auth';

export const middleware = protect({ redirectTo: '/login' });
```

So unauthenticated users are sent to your custom login page.

---

## Default auth page

If you don’t need a custom layout, keep using the built-in auth UI. In `app/auth/[...auth]/page.js`:

```js
export { default } from '@zen/core/auth/page';
```

This serves login, register, forgot, reset, confirm, and logout under `/auth/*` with the default styling.
