# User attributes

Store and update custom fields on the signed-in user, with a full change history.

User attributes are custom key-value fields on a user account — onboarding
state, plan preferences, anything your app needs to remember about a person
rather than a workspace.

```tsx
import { useUserAttributes } from '@buildbase/sdk/react';

function OnboardingGate({ children }: { children: React.ReactNode }) {
  const { attributes, loading, updateAttribute } = useUserAttributes();

  if (loading) return ;
  if (attributes?.onboarded) return <>{children}</>;

  return <OnboardingFlow onDone={() => updateAttribute('onboarded', true)} />;
}
```

> **Note:**
  Define your attribute keys in the BuildBase console so they are available for
  audience segmentation and workflow conditions. Writing an undefined key still
  stores it, but it will not appear as a filterable field.


## What the hook returns

| Field                | Type                                          | Description                                  |
| -------------------- | --------------------------------------------- | -------------------------------------------- |
| `attributes`         | `Record<string, string \| number \| boolean>` | Current values                               |
| `loading`            | `boolean`                                     | True while the attributes fetch is in flight |
| `error`              | `string \| null`                              | Fetch error, if any                          |
| `refetch()`          | `() => Promise<void>`                         | Re-read attributes from the server           |
| `updateAttribute()`  | `(key, value) => Promise<IUser>`              | Write one attribute                          |
| `updateAttributes()` | `(updates) => Promise<IUser>`                 | Write several at once                        |

Values are limited to `string`, `number`, and `boolean`. Store an object by
serializing it yourself, and keep in mind that segmentation cannot filter inside
a serialized blob.

Both update methods resolve to the **full updated user**, not just the
attributes, so you can read other profile fields from the same response.

> **Note:**
  `useUserAttributes()` throws if it is called outside the provider tree, with
  "must be used within a UserProvider". If you see that error, the component is
  mounted above `SaaSOSProvider`.

Two returned fields are deprecated: `isLoading` combines the attributes and
features pipelines, so a features fetch flips it; and `refreshAttributes` is
the old name for `refetch`. Use `loading` and `refetch`.



## Writing one value versus several

`updateAttribute` is a convenience wrapper. Prefer `updateAttributes` when you
have more than one change — it is a single request, so there is no window where
half the changes are visible:

```tsx
const { updateAttributes } = useUserAttributes();

await updateAttributes({
  onboarded: true,
  plan_interest: 'scale',
  seats_estimate: 25,
});
```

## Change history

Every attribute write is recorded. The history is readable per audience contact
at `GET /api/audience/:id/attributes-history`, which is what the console renders
on a contact's timeline.

That makes attributes usable as an audit trail — "when did this user actually
finish onboarding" is answerable after the fact, not just "are they onboarded
now".

## Attributes versus feature flags

They look similar and are not interchangeable:

|            | User attributes                   | [User feature flags](/feature-flags/overview) |
| ---------- | --------------------------------- | --------------------------------------------- |
| Written by | Your app, at runtime              | The console                                   |
| Purpose    | Remember something about the user | Decide what the user may see                  |
| Read with  | `useUserAttributes()`             | `useUserFeatures()`, `WhenUserFeatureEnabled` |

If your app decides the value, it is an attribute. If you want to change
behavior without a deploy, it is a flag.

## Next Steps

- [Audience and lists](/users/audience-and-lists) — segment on these attributes.
- [Feature flags](/feature-flags/overview) — gate features per user.
