> ## Documentation Index
> Fetch the complete documentation index at: https://docs.rownd.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Next.js

> Integrate Rownd instant accounts and authentication into your Next.js project, including support for server-side rendering

## Quick Navigation

| Section                                       | Description                          |
| --------------------------------------------- | ------------------------------------ |
| [Installation](#installation)                 | How to install the Next.js SDK       |
| [Basic Setup](#basic-setup)                   | Setting up providers and middleware  |
| [Server-Side Features](#server-side-features) | Server components and authentication |
| [Client-Side Features](#client-side-features) | Client components and hooks          |
| [Authentication Flow](#authentication-flow)   | Complete auth implementation         |
| [Protected Routes](#protected-routes)         | Route protection strategies          |
| [Data Access](#data-access)                   | Accessing user data (server/client)  |
| [Best Practices](#best-practices)             | Recommended patterns                 |
| [Type Definitions](#type-definitions)         | TypeScript interfaces                |
| [Examples](#examples)                         | Common use cases                     |

## Installation

```bash theme={null}
npm install @rownd/next
# or
yarn add @rownd/next
```

## Basic Setup

### 1. Provider Setup

In your root `layout.tsx`:

```tsx theme={null}
import { RowndProvider } from '@rownd/next';

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html>
      <body>
        <RowndProvider
          appKey="<your app key>"
          apiUrl="<optional: your api url>"  // Enterprise only
          hubUrlOverride="<optional: your hub url>"  // Enterprise only
          postLoginRedirect="/dashboard"  // Optional
          postLogoutRedirect="/"  // Optional
        >
          {children}
        </RowndProvider>
      </body>
    </html>
  );
}
```

### 2. Middleware Setup

In your `middleware.ts`:

```typescript theme={null}
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { withRowndMiddleware } from '@rownd/next/server';

export const middleware = withRowndMiddleware((request: NextRequest) => {
  return NextResponse.next();
});

export const config = {
  matcher: [
    // Required for Rownd token handling
    '/api/rownd-token-callback',
    // Add your protected routes
    '/protected/:path*',
    '/api/protected/:path*'
  ]
};
```

## Server-Side Features

The `@rownd/next/server` package provides server-side authentication utilities:

```typescript theme={null}
import {
  withRowndRequireSignIn,
  getRowndUser,
  getAccessToken,
  isAuthenticated,
  getRowndUserId,
} from '@rownd/next/server';
import { cookies } from 'next/headers';
```

### Server-Side Functions

| Function                 | Description                                                           | Parameters                                           | Return Type                        | Usage Example                                      |
| ------------------------ | --------------------------------------------------------------------- | ---------------------------------------------------- | ---------------------------------- | -------------------------------------------------- |
| `getRowndUser`           | Retrieves the current authenticated user's data from Rownd            | `cookies: ReadonlyRequestCookies`                    | `Promise<RowndServerUser \| null>` | `const user = await getRowndUser(cookies)`         |
| `getRowndUserId`         | Gets the current user's ID from server context                        | `cookies: ReadonlyRequestCookies`                    | `Promise<string \| null>`          | `const userId = await getRowndUserId(cookies)`     |
| `getRowndAccessToken`    | Retrieves the current access token from server context                | `cookies: ReadonlyRequestCookies`                    | `Promise<string \| null>`          | `const token = await getRowndAccessToken(cookies)` |
| `isAuthenticated`        | Checks if there is an authenticated user in server context            | `cookies: ReadonlyRequestCookies`                    | `Promise<boolean>`                 | `const isAuth = await isAuthenticated(cookies)`    |
| `withRowndMiddleware`    | Higher-order function for Next.js middleware to handle authentication | `middleware: NextMiddleware`                         | `NextMiddleware`                   | See middleware example below                       |
| `withRowndRequireSignIn` | Higher-order function to protect routes/pages/API handlers            | `handler: Function, cookies: ReadonlyRequestCookies` | `Function`                         | See protected routes example below                 |

### Example Usage

```typescript theme={null}
// Middleware setup
export const config = {
  matcher: [
    '/api/rownd-token-callback',  // Required for token handling
    '/protected/:path*'         // Your protected routes
  ]
};

// Protected API route
async function handler(req: Request) {
  const userId = await getRowndUserId(cookies);
  const token = await getRowndAccessToken(cookies);
  const user = await getRowndUser(cookies);
  
  if (!userId) {
    return Response.json({ error: 'Not authenticated' }, { status: 401 });
  }
  
  return Response.json({ userId, user });
}

export const GET = withRowndRequireSignIn(handler, cookies);
```

## Client-Side Features

The client-side features mirror the React SDK functionality but must be used within client components:

### The useRownd Hook

The `useRownd` hook provides comprehensive authentication and user management functionality:

#### State Properties

| Property           | Type                  | Description                         | Example Usage                                        |   |               |            |                            |                                      |
| ------------------ | --------------------- | ----------------------------------- | ---------------------------------------------------- | - | ------------- | ---------- | -------------------------- | ------------------------------------ |
| `is_initializing`  | `boolean`             | Whether Rownd is still loading      | `if (is_initializing) return <Loading />`            |   |               |            |                            |                                      |
| `is_authenticated` | `boolean`             | Whether user is currently signed in | `if (is_authenticated) showDashboard()`              |   |               |            |                            |                                      |
| `access_token`     | `string \| null`      | Current JWT access token            | `headers: { Authorization: Bearer ${access_token} }` |   |               |            |                            |                                      |
| `user.data`        | `Record<string, any>` | User's profile data                 | `const { first_name, email } = user.data`            |   | `user.groups` | `string[]` | Groups the user belongs to | `if (user.groups.includes('admin'))` |

#### Authentication Methods

| Method             | Description               | Parameters                                                                                       | Return Type       |
| ------------------ | ------------------------- | ------------------------------------------------------------------------------------------------ | ----------------- |
| `requestSignIn()`  | Triggers sign-in flow     | `{ auto_sign_in?: boolean, identifier?: string, method?: string, post_login_redirect?: string }` | `void`            |
| `signOut()`        | Signs out current user    | None                                                                                             | `void`            |
| `getAccessToken()` | Gets current access token | `{ waitForToken?: boolean, timeoutMs?: number }`                                                 | `Promise<string>` |

#### User Data Methods

| Method            | Description                  | Parameters                    | Return Type     |
| ----------------- | ---------------------------- | ----------------------------- | --------------- |
| `setUser()`       | Updates multiple user fields | `Record<string, any>`         | `Promise<void>` |
| `setUserValue()`  | Updates a single user field  | `(field: string, value: any)` | `Promise<void>` |
| `manageAccount()` | Opens account management UI  | None                          | `void`          |

#### Additional Features

| Feature                | Description                    | Parameters                                                             | Return Type          |
| ---------------------- | ------------------------------ | ---------------------------------------------------------------------- | -------------------- |
| `getFirebaseIdToken()` | Gets Firebase ID token         | None                                                                   | `Promise<string>`    |
| `getAppConfig()`       | Gets app configuration         | None                                                                   | `Promise<AppConfig>` |
| `passkeys`             | Passkey authentication methods | `{ register: () => Promise<void>, authenticate: () => Promise<void> }` | Object               |
| `events`               | Event system for state changes | `addEventListener(event: string, callback: Function)`                  | EventEmitter         |

### Example Usage

```tsx theme={null}
'use client';

function ProfileManager() {
  const {
    is_authenticated,
    is_initializing,
    user,
    setUser,
    manageAccount,
    getAccessToken
  } = useRownd();

  if (is_initializing) return <div>Loading...</div>;
  if (!is_authenticated) return <div>Please sign in</div>;

  const updateProfile = async () => {
    await setUser({
      first_name: 'John',
      last_name: 'Doe'
    });
  };

  const fetchProtectedData = async () => {
    const token = await getAccessToken({ waitForToken: true });
    // Use token for API calls
  };

  return (
    <div>
      <h1>Welcome {user.data.first_name}!</h1>
      <button onClick={updateProfile}>Update Profile</button>
      <button onClick={manageAccount}>Manage Account</button>
    </div>
  );
}
```

## Authentication Flow

### Combined Server/Client Authentication

```tsx theme={null}
// app/profile/page.tsx
import { getRowndUser } from '@rownd/next/server';
import { cookies } from 'next/headers';
import { ClientProfile } from './client-profile';

// Server Component
export default async function ProfilePage() {
  const serverUser = await getRowndUser(cookies);
  
  if (!serverUser) {
    return <div>Please sign in</div>;
  }
  
  // Initial data from server
  return (
    <div>
      <h1>Server-rendered Profile</h1>
      <pre>{JSON.stringify(serverUser.data, null, 2)}</pre>
      
      {/* Client component for real-time updates */}
      <ClientProfile initialData={serverUser} />
    </div>
  );
}

// client-profile.tsx
'use client';

export function ClientProfile({ initialData }) {
  const { user, is_initializing } = useRownd();
  
  if (is_initializing) {
    return <div>Syncing...</div>;
  }
  
  return (
    <div>
      <h2>Real-time Profile Updates</h2>
      <pre>{JSON.stringify(user.data, null, 2)}</pre>
    </div>
  );
}
```

## Protected Routes

### Server-Side Protection

```typescript theme={null}
// middleware.ts
import { withRowndMiddleware } from '@rownd/next/server';

export const middleware = withRowndMiddleware((request: NextRequest) => {
  // Protect specific paths
  if (request.nextUrl.pathname.startsWith('/protected')) {
    // Additional custom logic
  }
  return NextResponse.next();
});
```

### Component-Level Protection

```tsx theme={null}
// Protected Server Component
import { withRowndRequireSignIn } from '@rownd/next/server';
import { cookies } from 'next/headers';

async function ProtectedPage() {
  const user = await getRowndUser(cookies);
  return <div>Protected Content for {user.data.email}</div>;
}

export default withRowndRequireSignIn(ProtectedPage, cookies);

// Protected Client Component
'use client';

function ProtectedClientComponent() {
  const { is_authenticated, is_initializing } = useRownd();
  
  if (is_initializing) return <div>Loading...</div>;
  if (!is_authenticated) return <div>Please sign in</div>;
  
  return <div>Protected Client Content</div>;
}
```

## Data Access

### Server-Side Data Access

```tsx theme={null}
async function ServerComponent() {
  const user = await getRowndUser(cookies);
  const token = await getAccessToken(cookies);
  
  // Make authenticated API calls
  const response = await fetch('https://api.example.com/data', {
    headers: {
      Authorization: `Bearer ${token}`
    }
  });
  
  return <div>User: {user.data.email}</div>;
}
```

### Client-Side Data Access

```tsx theme={null}
'use client';

function ClientComponent() {
  const { user, getAccessToken } = useRownd();
  
  const fetchData = async () => {
    const token = await getAccessToken();
    // Make authenticated API calls
  };
  
  return <div>User: {user.data.email}</div>;
}
```

## Best Practices

1. **Server/Client Separation**
   ```tsx theme={null}
   // Server Component
   export default async function Page() {
     const serverUser = await getRowndUser(cookies);
     return <ClientComponent initialData={serverUser} />;
   }

   // Client Component
   'use client';
   export function ClientComponent({ initialData }) {
     const { user } = useRownd();
     // Use initialData for SSR, user for updates
   }
   ```

2. **Efficient Token Handling**
   ```typescript theme={null}
   // Server-side
   const token = await getAccessToken(cookies);
   // Token includes app_id and user_id

   // Client-side
   const { getAccessToken } = useRownd();
   const token = await getAccessToken();
   ```

3. **Error Boundaries**
   ```tsx theme={null}
   'use client';

   class RowndErrorBoundary extends React.Component {
     // Implementation
   }
   ```

## Type Definitions

### Core Types

| Type              | Description             | Interface                                                                                                                         |
| ----------------- | ----------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `RowndServerUser` | Server-side user type   | `{ data: Record<string, any>; access_token: string; app_id: string; user_id: string; }`                                           |
| `RowndUser`       | Client-side user type   | `{ data: Record<string, any>; groups: string[]; redacted_fields: string[]; verified_data: Record<string, any>; meta: UserMeta; }` |
| `UserMeta`        | User metadata           | `{ created_at: string; updated_at: string; last_sign_in: string; }`                                                               |
| `SignInOptions`   | Sign-in configuration   | `{ auto_sign_in?: boolean; identifier?: string; method?: AuthMethod; post_login_redirect?: string; }`                             |
| `TokenOptions`    | Token retrieval options | `{ waitForToken?: boolean; timeoutMs?: number; }`                                                                                 |
| `AuthMethod`      | Authentication methods  | `'email' \| 'phone' \| 'google' \| 'apple' \| 'passkey' \| 'anonymous'`                                                           |

### Server-Side Types

```typescript theme={null}
// Server-side interfaces
interface RowndServerUser {
  data: Record<string, any>;
  access_token: string;
  app_id: string;
  user_id: string;
}

interface RowndServerConfig {
  app_key: string;
  api_url?: string;
  hub_url_override?: string;
}

type RowndMiddlewareConfig = {
  matcher: string[];
};

// Server utility types
type GetUserFn = (cookies: ReadonlyRequestCookies) => Promise<RowndServerUser | null>;
type GetTokenFn = (cookies: ReadonlyRequestCookies) => Promise<string | null>;
type IsAuthenticatedFn = (cookies: ReadonlyRequestCookies) => Promise<boolean>;
```

### Client-Side Types

```typescript theme={null}
// Client-side interfaces
interface RowndUser {
  data: Record<string, any>;
  groups: string[];
  redacted_fields: string[];
  verified_data: Record<string, any>;
  meta: {
    created_at: string;
    updated_at: string;
    last_sign_in: string;
  };
  is_loading: boolean;
}

interface UseRowndReturn {
  is_initializing: boolean;
  is_authenticated: boolean;
  user: RowndUser;
  requestSignIn: (options?: SignInOptions) => void;
  signOut: () => void;
  getAccessToken: (options?: TokenOptions) => Promise<string>;
  setUser: (data: Record<string, any>) => Promise<void>;
  setUserValue: <T>(field: string, value: T) => Promise<void>;
  manageAccount: () => void;
  getFirebaseIdToken: () => Promise<string>;
  getAppConfig: () => Promise<AppConfig>;
  passkeys: PasskeyMethods;
  events: EventEmitter;
}
```

### TypeScript Examples

#### Server-Side Example

```typescript theme={null}
// app/api/user/route.ts
import { 
  getRowndUser, 
  getRowndAccessToken,
  withRowndRequireSignIn 
} from '@rownd/next/server';
import { cookies } from 'next/headers';
import { NextResponse } from 'next/server';

interface UserPreferences {
  theme: 'light' | 'dark';
  notifications: boolean;
}

async function handler(req: Request) {
  try {
    const user = await getRowndUser(cookies);
    const token = await getRowndAccessToken(cookies);
    
    if (!user) {
      return NextResponse.json(
        { error: 'Not authenticated' },
        { status: 401 }
      );
    }
    
    // Type-safe user data access
    const preferences: UserPreferences = {
      theme: user.data.theme || 'light',
      notifications: user.data.notifications ?? true
    };
    
    return NextResponse.json({
      user_id: user.user_id,
      preferences,
      token
    });
  } catch (error) {
    return NextResponse.json(
      { error: 'Server error' },
      { status: 500 }
    );
  }
}

export const GET = withRowndRequireSignIn(handler, cookies);
```

#### Client-Side Example

```typescript theme={null}
// components/user-profile.tsx
'use client';

import { useRownd } from '@rownd/next';
import { useState } from 'react';

interface ProfileFormData {
  first_name: string;
  last_name: string;
  email: string;
  preferences: {
    theme: 'light' | 'dark';
    notifications: boolean;
  };
}

interface ProfileProps {
  initialData?: Partial<ProfileFormData>;
}

export function UserProfile({ initialData }: ProfileProps) {
  const { 
    user,
    is_authenticated,
    is_initializing,
    setUser,
    getAccessToken
  } = useRownd();
  
  const [formData, setFormData] = useState<ProfileFormData>({
    first_name: initialData?.first_name || '',
    last_name: initialData?.last_name || '',
    email: initialData?.email || '',
    preferences: initialData?.preferences || {
      theme: 'light',
      notifications: true
    }
  });

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    try {
      await setUser(formData);
      const token = await getAccessToken({ waitForToken: true });
      
      // Make authenticated API call
      await fetch('/api/user/preferences', {
        method: 'POST',
        headers: {
          'Authorization': `Bearer ${token}`,
          'Content-Type': 'application/json'
        },
        body: JSON.stringify(formData.preferences)
      });
    } catch (error) {
      console.error('Failed to update profile:', error);
    }
  };

  if (is_initializing) {
    return <div>Loading profile...</div>;
  }

  if (!is_authenticated) {
    return <div>Please sign in to view your profile</div>;
  }

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label htmlFor="first_name">First Name:</label>
        <input
          id="first_name"
          type="text"
          value={formData.first_name}
          onChange={e => setFormData(prev => ({
            ...prev,
            first_name: e.target.value
          }))}
        />
      </div>

      <div>
        <label htmlFor="email">Email:</label>
        <input
          id="email"
          type="email"
          value={formData.email}
          onChange={e => setFormData(prev => ({
            ...prev,
            email: e.target.value
          }))}
        />
        {user.verified_data.email === formData.email && (
          <span>✓ Verified</span>
        )}
      </div>

      <div>
        <label>
          <input
            type="checkbox"
            checked={formData.preferences.notifications}
            onChange={e => setFormData(prev => ({
              ...prev,
              preferences: {
                ...prev.preferences,
                notifications: e.target.checked
              }
            }))}
          />
          Enable Notifications
        </label>
      </div>

      <button type="submit">Save Changes</button>
    </form>
  );
}
```

#### Combined Server/Client Example

```typescript theme={null}
// app/profile/page.tsx
import { getRowndUser } from '@rownd/next/server';
import { cookies } from 'next/headers';
import { UserProfile } from '@/components/user-profile';

interface PageProps {
  params: Record<string, string>;
  searchParams: Record<string, string>;
}

export default async function ProfilePage({ params, searchParams }: PageProps) {
  const user = await getRowndUser(cookies);
  
  if (!user) {
    return <div>Please sign in to view your profile</div>;
  }
  
  // Pass server-fetched data to client component
  return (
    <div>
      <h1>User Profile</h1>
      <UserProfile 
        initialData={{
          first_name: user.data.first_name,
          last_name: user.data.last_name,
          email: user.data.email,
          preferences: user.data.preferences
        }} 
      />
    </div>
  );
}
```

## Examples

### Full Authentication Flow

```tsx theme={null}
// app/auth/page.tsx
import { getRowndUser } from '@rownd/next/server';
import { cookies } from 'next/headers';
import { AuthClient } from './auth-client';

export default async function AuthPage() {
  const serverUser = await getRowndUser(cookies);
  return <AuthClient initialUser={serverUser} />;
}

// auth-client.tsx
'use client';

export function AuthClient({ initialUser }) {
  const { 
    is_initializing,
    is_authenticated,
    user,
    requestSignIn,
    signOut
  } = useRownd();

  if (is_initializing) {
    return <div>Loading...</div>;
  }

  return (
    <div>
      {is_authenticated ? (
        <div>
          <h1>Welcome {user.data.first_name}!</h1>
          <button onClick={signOut}>Sign Out</button>
        </div>
      ) : (
        <button onClick={() => requestSignIn()}>Sign In</button>
      )}
    </div>
  );
}
```

For more examples and detailed client-side features, refer to the [React SDK documentation](/sdk-reference/web/react#examples).
