# Data collection

Learn how to collect information for personalization.

This guide provides practical examples of using the Next.js SDK to enrich user profiles and sessions with information relevant to your business.

## User data

You can persist user-related information in one of the two ways described below depending on whether it is related to the user's profile or their current session.

### Profile data

You can enrich user profiles with details collected via forms, surveys, or other means.

These pieces of information, called *attributes*, are categorized as *standard* or *custom* depending on whether it is generic or specific to your organization.

#### Standard attributes

These are [predefined attributes](/reference/user/standard-attributes) that are common most businesses, such as the user's name, email, and interests.

You can set standard attributes using the [`user.edit`](/reference/sdk/javascript/api/user/edit) method. For example, let's say you have a newsletter subscription form, and you want to add the email address to the user profile on Croct.

This is what it would look like:

**Updating user profiles**

**JavaScript**

```jsx
import type {FormEvent} from 'react';
import {useCroct} from '@croct/plug-next';

export function NewsletterForm() {
const croct = useCroct();
const subscribe = event => {
  const form = new FormData(event.currentTarget);
  const email = form.get('email');

  // Your logic to persist the email
  api.subscribe(email);

  croct.user.edit()
    .set('email', email)
    .save();
};

return (
  <form onSubmit={subscribe}>
    📫 Subscribe to our newsletter!
    <input type="email" placeholder="Email"/>
    <button type="submit">Subscribe</button>
  </form>
);
}
```

**TypeScript**

```tsx
import type {FormEvent, ReactElement} from 'react';
import {useCroct} from '@croct/plug-next';

export function NewsletterForm(): ReactElement {
const croct = useCroct();
const subscribe = (event: FormEvent<HTMLFormElement>): void => {
  const form = new FormData(event.currentTarget);
  const email = String(form.get('email'));

  // Your logic to persist the email
  api.subscribe(email);

  croct.user.edit()
    .set('email', email)
    .save();
};

return (
  <form onSubmit={subscribe}>
    📫 Subscribe to our newsletter!
    <input type="email" placeholder="Email"/>
    <button type="submit">Subscribe</button>
  </form>
);
}
```

The [`user.edit`](/reference/sdk/javascript/api/user/edit) method returns a [`Patch`](/reference/sdk/javascript/api/patch) instance that allows you to chain multiple operations together.

You could alternatively write:

```tsx
const patch = croct.user.edit();
patch.set('email', email);

await patch.save();
```

When you call [`save`](/reference/sdk/javascript/api/patch/save), it saves changes to the user profile and returns a promise that confirms the operation.

You can then access standard attributes in your queries using the [`user`](/reference/cql/context#user) variable:

```cql
user's email ends with "@croct.com"
```

For a complete list of attributes, see [User profile reference](/reference/cql/data-types/user/user).

#### Custom attributes

In addition to the standard attributes, you can add [custom attributes](/reference/user/custom-attributes) to enrich user profiles with information relevant to your business.

For example, let's say you have a SaaS application and you want to ask your users about their area of expertise so you can personalize the onboarding experience.

This is what it would look like:

**JavaScript**

```jsx
import {useCroct} from '@croct/plug-next';

export function ExpertiseSelector() {
const croct = useCroct();

const onChange = event => {
  croct.user.edit()
    .set('custom.expertise', event.target.value)
    .save();
};

return (
  <select onChange={onChange}>
    <option value="Engineering">Engineering</option>
    <option value="Design">Design</option>
    <option value="Marketing">Marketing</option>
  </select>
);
}
```

**TypeScript**

```tsx
import type {ReactElement, ChangeEvent} from 'react';
import {useCroct} from '@croct/plug-next';

export function ExpertiseSelector(): ReactElement {
const croct = useCroct();

const onChange = (event: ChangeEvent<HTMLSelectElement>): void => {
  croct.user.edit()
    .set('custom.expertise', event.target.value)
    .save();
};

return (
  <select onChange={onChange}>
    <option value="Engineering">Engineering</option>
    <option value="Design">Design</option>
    <option value="Marketing">Marketing</option>
  </select>
);
}
```

Note that custom attributes are prefixed with `custom` to avoid conflicts with standard attributes. However, you do not need to include the prefix in your queries:

```cql
user's expertise is "Engineering"
```

### Session data

Besides user profiles, you can also store information relevant to the user's current session.

Storing session information can help you keep track of important the user's current session that may not be relevant to their profile. For example, you can store the plan the user selected on the pricing page, or any other information you want to record during the user's visit.

To store session information, use the [`session.edit`](/reference/sdk/javascript/api/session/edit) method:

**Saving session data**

**JavaScript**

```jsx
import {useCroct} from '@croct/plug-next';

export function PlanCard({plan, price}) {
const croct = useCroct();

const onSelect = () => {
  croct.session.edit()
    .set('plan', plan)
    .save();
};

return (
  <div>
    <strong>{plan}</strong>
    <p>${price}</p>
    <button onClick={onSelect}>Select</button>
  </div>
);
}
```

**TypeScript**

```tsx
import type {ReactElement} from 'react';
import {useCroct} from '@croct/plug-next';

type PlanCardProps = {
plan: string,
price: number,
}

export function PlanCard({plan, price}: PlanCardProps): ReactElement {
const croct = useCroct();

const onSelect = (): void => {
  croct.session.edit()
    .set('plan', plan)
    .save();
};

return (
  <div>
    <strong>{plan}</strong>
    <p>${price}</p>
    <button onClick={onSelect}>Select</button>
  </div>
);
}
```

In the same way as with user profiles, the [`session.edit`](/reference/sdk/javascript/api/session/edit) method returns a [`Patch`](/reference/sdk/javascript/api/patch) instance that allows you to chain multiple operations together.

```tsx
const patch = croct.session.edit();
patch.set('plan', plan);

await patch.save();
```

When you call [`save`](/reference/sdk/javascript/api/patch/save), it saves changes to the session and returns a promise that confirms the operation. You can then access session information in your queries using the [`session`](/reference/cql/context#session) variable:

```cql
session's plan is "premium"
```

For a complete list of attributes, see [Session reference](/reference/cql/data-types/session/web-session).

## User identity

By default, all users are considered anonymous. However, if your application has logged-in areas, you may want to link the Croct user profile with your application's user ID. This allows you to personalize the user experience consistently across devices and sessions.

The SDK offers two options for identifying users, which you can choose depending on how you manage user sessions in your application.

> **Automatic handling**
>
> If you have a secure way to determine the user ID from the request, such as a session token or JWT token, the [proxy/middleware](api/functions/with-croct) provides a seamless solution that manages the entire process for you.
>
> Here is an example using [Next Auth](https://next-auth.js.org/) and JWT tokens:
>
> **Next ≤ 15 — JavaScript**
>
> ```js
> import jwt from 'next-auth/jwt';
> import {withCroct} from "@croct/plug-next/middleware";
>
> const secret = process.env.SECRET;
>
> export const config = {
>   matcher: "/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)"
> };
>
> export const middleware = withCroct({
> userIdResolver: async req => (await jwt.getToken({req, secret}))?.sub ?? null,
> });
> ```
>
> **Next ≤ 15 — TypeScript**
>
> ```ts
> import jwt from 'next-auth/jwt';
> import {withCroct} from "@croct/plug-next/middleware";
>
> const secret = process.env.SECRET;
>
> export const config = {
>   matcher: "/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)"
> };
>
> export const middleware = withCroct({
> userIdResolver: async req => (await jwt.getToken({req, secret}))?.sub ?? null,
> });
> ```
>
> **Next ≥ 16 — JavaScript**
>
> ```js
> import jwt from 'next-auth/jwt';
> import {withCroct} from "@croct/plug-next/proxy";
>
> const secret = process.env.SECRET;
>
> export const config = {
>   matcher: "/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)"
> };
>
> export const proxy = withCroct({
> userIdResolver: async req => (await jwt.getToken({req, secret}))?.sub ?? null,
> });
> ```
>
> **Next ≥ 16 — TypeScript**
>
> ```ts
> import jwt from 'next-auth/jwt';
> import {withCroct} from "@croct/plug-next/proxy";
>
> const secret = process.env.SECRET;
>
> export const config = {
>   matcher: "/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)"
> };
>
> export const proxy = withCroct({
> userIdResolver: async req => (await jwt.getToken({req, secret}))?.sub ?? null,
> });
> ```

> **Manual handling**
>
> If you do not have a way to automatically identify users, you can use the [`identify`](api/functions/identify) and [`anonymize`](api/functions/anonymize) functions to handle the process manually.
>
> The following example shows how to identify users when they log in:
>
> **App router — JavaScript**
>
> **components/LoginForm.jsx**
>
> ```jsx
> 'use client';
>
> import {login} from '@/app/services';
> import {identifyUser} from '@/app/actions';
>
> export function LoginForm() {
>   const onSubmit = async form => {
>     const username = form.get('username');
>     const password = form.get('password');
>
>     // Your login logic
>     if (await login(username, password)) {
>       // Identify the user
>       await identifyUser(username);
>     }
>   };
>
>   return (
>     <form action={onSubmit}>
>       <input type="text" name="username" />
>       <input type="password" name="password" />
>       <button type="submit">Login</button>
>     </form>
>   );
> };
> ```
>
> **app/actions.js**
>
> ```jsx
> 'use server';
>
> import {identify} from '@croct/plug-next/server';
>
> export async function identifyUser(userId) {
>   await identify(userId);
> }
> ```
>
> **App router — TypeScript**
>
> **components/LoginForm.tsx**
>
> ```tsx
> 'use client';
>
> import type {ReactElement} from 'react';
> import {login} from '@/app/services';
> import {identifyUser} from '@/app/actions';
>
> export function LoginForm(): ReactElement {
>     const onSubmit = async (form: FormData): Promise<void> => {
>       const username = form.get('username');
>       const password = form.get('password');
>
>       // Your login logic
>       if (await login(username, password)) {
>         // Identify the user
>         await identifyUser(username);
>       }
>     };
>
>     return (
>       <form action={onSubmit}>
>         <input type="text" name="username" />
>         <input type="password" name="password" />
>         <button type="submit">Login</button>
>       </form>
>     );
> };
> ```
>
> **app/actions.ts**
>
> ```tsx
> 'use server';
>
> import {identify} from '@croct/plug-next/server';
>
> export async function identifyUser(userId: string): Promise<void> {
>   await identify(userId);
> }
> ```
>
> **Page router — JavaScript**
>
> **pages/login.js**
>
> ```jsx
> export function LoginForm() {
>   const onSubmit = async event => {
>     event.preventDefault();
>
>     const form = new FormData(event.currentTarget);
>     const username = form.get('username');
>     const password = form.get('password');
>
>     const response = await fetch('/api/login', {
>       method: 'POST',
>       body: JSON.stringify({username, password}),
>     });
>
>     if (!response.ok) {
>       alert('Login failed');
>     }
>   };
>
>   return (
>     <form onSubmit={onSubmit}>
>       <input type="text" name="username" />
>       <input type="password" name="password" />
>       <button type="submit">Login</button>
>     </form>
>   );
> }
> ```
>
> **pages/api/login.js**
>
> ```jsx
> import {login} from '@/app/services';
> import {identify} from "@croct/plug-next/server";
>
> export default async function handler(req, res) {
>   const {username, password} = req.body;
>
>   // Your login logic
>   if (await login(username, password)) {
>     // Identify the user
>     await identify(username, {req, res});
>
>     return res.status(200).end();
>   }
>
>   return res.status(401).end();
> }
> ```
>
> **Page router — TypeScript**
>
> **pages/login.tsx**
>
> ```tsx
> import type {FormEvent, ReactElement} from 'react';
>
> export function LoginForm(): ReactElement {
>   const onSubmit = async (event: FormEvent<HTMLFormElement>) => {
>     event.preventDefault();
>
>     const form = new FormData(event.currentTarget);
>     const username = form.get('username');
>     const password = form.get('password');
>
>     const response = await fetch('/api/login', {
>       method: 'POST',
>       body: JSON.stringify({username, password}),
>     });
>
>     if (!response.ok) {
>       alert('Login failed');
>     }
>   };
>
>   return (
>     <form onSubmit={onSubmit}>
>       <input type="text" name="username" />
>       <input type="password" name="password" />
>       <button type="submit">Login</button>
>     </form>
>   );
> }
> ```
>
> **pages/api/login.ts**
>
> ```tsx
> import type {NextApiRequest, NextApiResponse} from 'next';
> import {login} from '@/app/services';
> import {identify} from "@croct/plug-next/server";
>
> export default async function handler(req: NextApiRequest, res: NextApiResponse): Promise<void> {
>   const {username, password} = req.body;
>
>     // Your login logic
>   if (await login(username, password)) {
>     // Identify the user
>     await identify(username, {req, res});
>
>     return res.status(200).end();
>   }
>
>   return res.status(401).end();
> }
> ```
>
> You can anonymize the user after logging out in a similar way:
>
> **App router — JavaScript**
>
> **components/LogoutButton.jsx**
>
> ```jsx
> 'use client';
>
> import {logout} from '@/app/services';
> import {anonymizeUser} from '@/app/actions';
>
> export function LogoutButton() {
>   const onClick = async () => {
>     // Your logout logic
>     await logout();
>     // Anonymize the user
>     await anonymizeUser();
>   };
>
>   return (<button onClick={onClick}>Logout</button>);
> }
> ```
>
> **app/actions.js**
>
> ```jsx
> 'use server';
>
> import {anonymize} from '@croct/plug-next/server';
>
> export async function anonymizeUser() {
>   await anonymize();
> }
> ```
>
> **App router — TypeScript**
>
> **components/LogoutButton.tsx**
>
> ```tsx
> 'use client';
>
> import type {ReactElement} from 'react';
> import {logout} from '@/app/services';
> import {anonymizeUser} from '@/app/actions';
>
> export function LogoutButton(): ReactElement {
>   const onClick = async () => {
>      // Your logout logic
>      await logout();
>      // Anonymize the user
>      await anonymizeUser();
>   };
>
>   return (<button onClick={onClick}>Logout</button>);
> };
> ```
>
> **app/actions.ts**
>
> ```tsx
> 'use server';
>
> import {anonymize} from '@croct/plug-next/server';
>
> export async function anonymizeUser(): Promise<void> {
>   await anonymize();
> }
> ```
>
> **Page router — JavaScript**
>
> **components/LogoutButton.jsx**
>
> ```jsx
> export function LogoutButton() {
>   const onClick = async () => {
>     await fetch('/api/logout');
>   };
>
>   return (<button onClick={onClick}>Logout</button>);
> }
> ```
>
> **pages/api/login.js**
>
> ```jsx
> import {logout} from '@/app/services';
> import {anonymize} from "@croct/plug-next/server";
>
> export default async function handler(req, res) {
>   // Your logout logic
>   if (await logout()) {
>     // Anonymize the user
>     await anonymize({req, res});
>   }
>
>   res.status(200).end();
> }
> ```
>
> **Page router — TypeScript**
>
> **components/LogoutButton.tsx**
>
> ```tsx
> import type {ReactElement} from 'react';
>
> export function LogoutButton(): ReactElement {
>   const onClick = async () => {
>     await fetch('/api/logout');
>   };
>
>   return (<button onClick={onClick}>Logout</button>);
> }
> ```
>
> **pages/api/logout.ts**
>
> ```tsx
> import type {NextApiRequest, NextApiResponse} from 'next';
> import {logout} from '@/app/services';
> import {anonymize} from "@croct/plug-next/server";
>
> export default async function handler(req: NextApiRequest, res: NextApiResponse): Promise<void> {
>   if (!await logout()) {
>     res.status(401).end();
>
>     return;
>   }
>
>   await anonymize({req, res});
>
>   res.status(200).end();
> }
> ```
>
> You can call these functions from your pages, API routes, or route handlers.

## Explore

- [Audiences](/explanation/audience): Understand how to delivery content to specific groups of users.
- [CQL](/reference/cql/syntax): Get the basics on how to write conditions to define audiences.
