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

# React Components

> Use OzElements, hooks, and pre-built React components to embed secure payment fields.

`@ozura/elements/react` ships a context provider, hooks, and pre-built field components for both card and bank payments.

***

## Installation

```bash theme={null}
npm install @ozura/elements
```

Import from the React-specific entrypoint:

```ts theme={null}
import {
  OzElements,
  useOzElements,
  OzCard,
  OzCardNumber,
  OzExpiry,
  OzCvv,
  OzBankCard,
  OzBankAccountNumber,
  OzBankRoutingNumber,
} from '@ozura/elements/react';
```

***

## OzElements Provider

Wrap your checkout UI with `<OzElements>`. It creates the `OzVault` instance and makes it available to all children via React context. A session option is required. `pubKey` is required when using a production vault API key; omit it when using a test/sandbox vault API key.

```tsx theme={null}
import { OzElements } from '@ozura/elements/react';

export default function App() {
  return (
    // Omit `pubKey` when using a test vault key from a Test project at ozuravault.com.
    <OzElements
      pubKey="YOUR_PUB_KEY"
      sessionUrl="/api/oz-session"
    >
      <CheckoutForm />
    </OzElements>
  );
}
```

`sessionUrl` is the simplest option — just pass your session endpoint path. See [Server SDK — Session route](/sdks/elements/server#session-route) for the matching backend route.

For custom headers or auth tokens, use `getSessionKey` instead:

```tsx theme={null}
<OzElements
  pubKey="YOUR_PUB_KEY"
  getSessionKey={async (sessionId) => {
    const res = await fetch('/api/oz-session', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` },
      body: JSON.stringify({ sessionId }),
    });
    if (!res.ok) throw new Error(`Session endpoint returned HTTP ${res.status}`);
    const { sessionKey } = await res.json();
    return sessionKey;
  }}
>
  <CheckoutForm />
</OzElements>
```

### OzElements props

| Prop               | Type                                     | Required | Default                      | Description                                                                                                                                                                                              |
| ------------------ | ---------------------------------------- | -------- | ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `pubKey`           | `string`                                 | ²        | —                            | Vault pub key (client-safe)                                                                                                                                                                              |
| `sessionUrl`       | `string`                                 | ✅ ¹      | —                            | URL of your session endpoint — simplest option                                                                                                                                                           |
| `getSessionKey`    | `(sessionId: string) => Promise<string>` | ✅ ¹      | —                            | Custom callback for session key retrieval (custom headers, auth, etc.)                                                                                                                                   |
| `fetchWaxKey`      | `(sessionId: string) => Promise<string>` | ✅ ¹      | —                            | **Deprecated** — use `sessionUrl` or `getSessionKey`                                                                                                                                                     |
| `frameBaseUrl`     | `string`                                 | —        | `https://elements.ozura.com` | Override iframe asset URL                                                                                                                                                                                |
| `fonts`            | `FontSource[]`                           | —        | `[]`                         | Custom fonts to load in iframes                                                                                                                                                                          |
| `appearance`       | `Appearance`                             | —        | —                            | Global theme and variable overrides. See [Styling](/sdks/elements/styling).                                                                                                                              |
| `onLoadError`      | `() => void`                             | —        | —                            | Called if vault fails to load                                                                                                                                                                            |
| `loadTimeoutMs`    | `number`                                 | —        | `10000`                      | Only relevant when `onLoadError` is set: ms to wait before calling `onLoadError`                                                                                                                         |
| `onSessionRefresh` | `() => void`                             | —        | —                            | Called when the SDK silently refreshes the session during a tokenization attempt                                                                                                                         |
| `onReady`          | `() => void`                             | —        | —                            | Called once when the vault is ready                                                                                                                                                                      |
| `sessionLimit`     | `number \| null`                         | —        | `3`                          | Max successful tokenize calls per session before auto-refresh. Pass `null` to remove the cap. Must match `sessionLimit` in your server-side `createSession` call.                                        |
| `debug`            | `boolean`                                | —        | `false`                      | Enables `[OzVault]`-prefixed `console.log` output at every lifecycle event. Safe in production — no sensitive data is logged. See [API Reference — Debug mode](/sdks/elements/api-reference#debug-mode). |
| `children`         | `ReactNode`                              | ✅        | —                            | Your checkout UI                                                                                                                                                                                         |

¹ Exactly one of `sessionUrl`, `getSessionKey`, or `fetchWaxKey` is required.

² `pubKey` is required when using a **production** vault API key. If your vault API key was created for the test/sandbox environment, you can omit `pubKey` entirely.

<Warning>
  Mount **one card form at a time** inside a single `<OzElements>` provider. If you render a second `<OzCard>` (or a second set of individual field components) without first unmounting the first, the older element iframes will be silently replaced. For multi-step flows, unmount one form before mounting the next.
</Warning>

***

## useOzElements Hook

Use `useOzElements()` inside any component wrapped by `<OzElements>` to tokenize and track readiness.

```tsx theme={null}
import { useOzElements } from '@ozura/elements/react';

function CheckoutForm() {
  const { createToken, ready, initError } = useOzElements();

  if (initError) return <p>Payment unavailable — please refresh.</p>;

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    const { token, cvcSession, billing } = await createToken({
      billing: { firstName: 'Jane', lastName: 'Smith' },
    });
    // send token + cvcSession + billing to backend
  };

  return (
    <form onSubmit={handleSubmit}>
      <OzCardNumber />
      <OzExpiry />
      <OzCvv />
      <button type="submit" disabled={!ready}>Pay now</button>
    </form>
  );
}
```

### useOzElements return values

| Value             | Type                                                        | Description                                                                                                                                                                                                                                               |
| ----------------- | ----------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `createToken`     | `(opts?: TokenizeOptions) => Promise<TokenResponse>`        | Tokenizes all mounted card elements                                                                                                                                                                                                                       |
| `createBankToken` | `(opts: BankTokenizeOptions) => Promise<BankTokenResponse>` | Tokenizes mounted bank account elements                                                                                                                                                                                                                   |
| `reset`           | `() => void`                                                | Clears all mounted card and bank element fields without destroying the vault or refreshing the session. Call this after a declined payment so the customer can re-enter their details. The session and its remaining budget are preserved.                |
| `ready`           | `boolean`                                                   | `true` when the vault and all mounted fields are ready. Gate your submit button on this.                                                                                                                                                                  |
| `initError`       | `Error \| null`                                             | Non-null when vault initialization fails — e.g. `pubKey` is missing, session endpoint unreachable, or backend error. Render a fallback UI when set.                                                                                                       |
| `tokenizeCount`   | `number`                                                    | Count of successful `createToken`/`createBankToken` calls in the current session. Resets to `0` on every session refresh. Use this to display "X attempts remaining" feedback or disable the pay button while a background session refresh is completing. |

<Note>
  `ready` is a **composite** flag. It is `true` only when the vault is ready AND every field component that has been mounted has also finished loading. Do not use `vault.isReady` directly — that only reflects vault readiness, not field readiness.
</Note>

***

## OzCard — Combined Card Component

`OzCard` renders card number, expiry, and CVV in a single component with built-in layout, loading skeletons, and inline error display. For maximum layout control, use the individual components instead.

```tsx theme={null}
import { OzElements, OzCard, useOzElements } from '@ozura/elements/react';

function CheckoutForm() {
  const { createToken, ready } = useOzElements();

  return (
    <form onSubmit={async (e) => {
      e.preventDefault();
      const { token, cvcSession } = await createToken({
        billing: { firstName: 'Jane', lastName: 'Doe' },
      });
      // send to backend
    }}>
      <OzCard layout="default" />
      <button type="submit" disabled={!ready}>Pay</button>
    </form>
  );
}

export default function App() {
  return (
    // Omit `pubKey` when using a test vault key from a Test project at ozuravault.com.
    <OzElements pubKey="YOUR_PUB_KEY" sessionUrl="/api/oz-session">
      <CheckoutForm />
    </OzElements>
  );
}
```

### OzCard props

| Prop             | Type                                                 | Default     | Description                                                                                         |
| ---------------- | ---------------------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------- |
| `layout`         | `'default' \| 'rows'`                                | `'default'` | `'default'`: card number on top, expiry + CVV side by side. `'rows'`: all three stacked vertically. |
| `style`          | `ElementStyleConfig`                                 | —           | Shared style applied to all three fields                                                            |
| `styles`         | `{ cardNumber?, expiry?, cvv? }`                     | —           | Per-field style overrides merged on top of `style`                                                  |
| `classNames`     | `{ cardNumber?, expiry?, cvv?, row? }`               | —           | CSS class names for field wrappers and the expiry+CVV row container                                 |
| `labels`         | `{ cardNumber?, expiry?, cvv? }`                     | —           | Labels rendered above each field                                                                    |
| `labelStyle`     | `React.CSSProperties`                                | —           | Inline style applied to all label elements                                                          |
| `labelClassName` | `string`                                             | —           | CSS class applied to all label elements                                                             |
| `placeholders`   | `{ cardNumber?, expiry?, cvv? }`                     | —           | Placeholder overrides per field                                                                     |
| `gap`            | `number \| string`                                   | `8`         | Gap between fields (px number or any CSS value string)                                              |
| `hideErrors`     | `boolean`                                            | `false`     | Suppress the built-in error display                                                                 |
| `errorStyle`     | `React.CSSProperties`                                | —           | Inline style for the error message container                                                        |
| `errorClassName` | `string`                                             | —           | CSS class for the error message container                                                           |
| `renderError`    | `(err: string) => ReactNode`                         | —           | Custom error renderer                                                                               |
| `onChange`       | `(state: OzCardState) => void`                       | —           | Fires on any field change with aggregate state                                                      |
| `onReady`        | `() => void`                                         | —           | Fires once all three field iframes are loaded                                                       |
| `onFocus`        | `(field: 'cardNumber' \| 'expiry' \| 'cvv') => void` | —           | Fires when a field gains focus                                                                      |
| `onBlur`         | `(field: 'cardNumber' \| 'expiry' \| 'cvv') => void` | —           | Fires when a field loses focus                                                                      |
| `disabled`       | `boolean`                                            | —           | Disables all inputs                                                                                 |
| `className`      | `string`                                             | —           | CSS class for the outer wrapper div                                                                 |

### OzCardState

```ts theme={null}
type OzCardState = {
  complete:   boolean;                      // all three fields complete and valid
  cardBrand?: string;                       // detected brand from card number field
  error?:     string;                       // first error from any field
  fields: {
    cardNumber: ElementChangeEvent | null;
    expiry:     ElementChangeEvent | null;
    cvv:        ElementChangeEvent | null;
  };
};
```

***

## Individual Card Components

Use `OzCardNumber`, `OzExpiry`, and `OzCvv` when you need full layout control.

<Note>
  The expiry component is exported as `OzExpiry`, not `OzExpirationDate`.
</Note>

```tsx theme={null}
import { OzCardNumber, OzExpiry, OzCvv } from '@ozura/elements/react';

function CardFields() {
  return (
    <div className="card-form">
      <div className="field">
        <label>Card number</label>
        <OzCardNumber />
      </div>
      <div className="row">
        <div className="field">
          <label>Expiry</label>
          <OzExpiry />
        </div>
        <div className="field">
          <label>CVV</label>
          <OzCvv />
        </div>
      </div>
    </div>
  );
}
```

### OzFieldProps (all individual field components)

| Prop            | Type                                  | Description                                     |
| --------------- | ------------------------------------- | ----------------------------------------------- |
| `style`         | `ElementStyleConfig`                  | Style overrides for this field                  |
| `placeholder`   | `string`                              | Placeholder text                                |
| `disabled`      | `boolean`                             | Disables the input                              |
| `loadTimeoutMs` | `number`                              | Override the iframe load timeout for this field |
| `className`     | `string`                              | CSS class applied to the wrapper `<div>`        |
| `onChange`      | `(event: ElementChangeEvent) => void` | Fired on value or state change                  |
| `onFocus`       | `() => void`                          | Fired when the field receives focus             |
| `onBlur`        | `() => void`                          | Fired when the field loses focus                |
| `onReady`       | `() => void`                          | Fired when the iframe is loaded and interactive |
| `onLoadError`   | `(error: string) => void`             | Fired if the iframe fails to load               |

***

## OzBankCard — Combined Bank Component

`OzBankCard` renders account number and routing number in a single component with built-in layout, loading skeletons, and inline error display.

```tsx theme={null}
import { OzElements, OzBankCard, useOzElements } from '@ozura/elements/react';

function BankForm() {
  const { createBankToken, ready } = useOzElements();
  const [firstName, setFirstName] = React.useState('');
  const [lastName, setLastName]   = React.useState('');

  return (
    <form onSubmit={async (e) => {
      e.preventDefault();
      const { token, bank } = await createBankToken({ firstName, lastName });
      // send token to your ACH processor backend
    }}>
      <input value={firstName} onChange={e => setFirstName(e.target.value)} placeholder="First name" />
      <input value={lastName}  onChange={e => setLastName(e.target.value)}  placeholder="Last name" />
      <OzBankCard />
      <button type="submit" disabled={!ready}>Submit</button>
    </form>
  );
}

export default function App() {
  return (
    // Omit `pubKey` when using a test vault key from a Test project at ozuravault.com.
    <OzElements pubKey="YOUR_PUB_KEY" sessionUrl="/api/oz-session">
      <BankForm />
    </OzElements>
  );
}
```

### OzBankCard props

| Prop             | Type                                                  | Default | Description                                       |
| ---------------- | ----------------------------------------------------- | ------- | ------------------------------------------------- |
| `style`          | `ElementStyleConfig`                                  | —       | Shared style applied to both fields               |
| `styles`         | `{ accountNumber?, routingNumber? }`                  | —       | Per-field style overrides                         |
| `classNames`     | `{ accountNumber?, routingNumber? }`                  | —       | CSS class names for field wrappers                |
| `labels`         | `{ accountNumber?, routingNumber? }`                  | —       | Labels rendered above each field                  |
| `labelStyle`     | `React.CSSProperties`                                 | —       | Inline style applied to all label elements        |
| `labelClassName` | `string`                                              | —       | CSS class applied to all label elements           |
| `placeholders`   | `{ accountNumber?, routingNumber? }`                  | —       | Placeholder overrides per field                   |
| `gap`            | `number \| string`                                    | `8`     | Gap between fields                                |
| `hideErrors`     | `boolean`                                             | `false` | Suppress the built-in error display               |
| `errorStyle`     | `React.CSSProperties`                                 | —       | Inline style for the error message container      |
| `errorClassName` | `string`                                              | —       | CSS class for the error message container         |
| `renderError`    | `(err: string) => ReactNode`                          | —       | Custom error renderer                             |
| `onChange`       | `(state: OzBankCardState) => void`                    | —       | Fires on either field change with aggregate state |
| `onReady`        | `() => void`                                          | —       | Fires once both field iframes are loaded          |
| `onFocus`        | `(field: 'accountNumber' \| 'routingNumber') => void` | —       | Fires when a field gains focus                    |
| `onBlur`         | `(field: 'accountNumber' \| 'routingNumber') => void` | —       | Fires when a field loses focus                    |
| `disabled`       | `boolean`                                             | —       | Disables all inputs                               |
| `className`      | `string`                                              | —       | CSS class for the outer wrapper div               |

### OzBankCardState

```ts theme={null}
type OzBankCardState = {
  complete: boolean;                          // both fields complete and valid
  error?:   string;                           // first error from either field
  fields: {
    accountNumber: ElementChangeEvent | null;
    routingNumber: ElementChangeEvent | null;
  };
};
```

***

## Individual Bank Components

Use `OzBankAccountNumber` and `OzBankRoutingNumber` for full layout control. They accept the same `OzFieldProps` as card fields.

```tsx theme={null}
import { OzBankAccountNumber, OzBankRoutingNumber } from '@ozura/elements/react';

function BankFields() {
  return (
    <>
      <div>
        <label>Account number</label>
        <OzBankAccountNumber placeholder="Account number" />
      </div>
      <div>
        <label>Routing number</label>
        <OzBankRoutingNumber placeholder="Routing number" />
      </div>
    </>
  );
}
```

***

## Full Card Checkout Example

```tsx theme={null}
import { useState } from 'react';
import { OzElements, useOzElements, OzCard } from '@ozura/elements/react';
import { OzError } from '@ozura/elements';

function CheckoutForm() {
  const { createToken, reset, ready, initError } = useOzElements();
  const [error, setError]   = useState<string | null>(null);
  const [loading, setLoading] = useState(false);

  if (initError) return <p>Payment fields unavailable — please refresh.</p>;

  const handleSubmit = async (e: React.FormEvent) => {
    e.preventDefault();
    setError(null);
    setLoading(true);

    try {
      const { token, cvcSession, billing } = await createToken({
        billing: {
          firstName: 'Jane',
          lastName:  'Smith',
          address: {
            line1:   '123 Main St',
            city:    'San Francisco',
            state:   'CA',
            zip:     '94102',
            country: 'US',
          },
        },
      });

      const res = await fetch('/api/charge', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ token, cvcSession, billing }),
      });

      if (!res.ok) throw new Error('Payment failed');
      window.location.href = '/success';
    } catch (err) {
      reset();                           // clear fields so customer can re-enter
      setError(err instanceof OzError ? err.message : 'Something went wrong.');
    } finally {
      setLoading(false);
    }
  };

  return (
    <form onSubmit={handleSubmit} className="checkout-form">
      <OzCard layout="default" />
      {error && <p className="error">{error}</p>}
      <button type="submit" disabled={!ready || loading}>
        {loading ? 'Processing…' : 'Pay'}
      </button>
    </form>
  );
}

export default function App() {
  return (
    <OzElements
      pubKey="YOUR_PUB_KEY"
      sessionUrl="/api/oz-session"
      appearance={{ theme: 'flat', variables: { colorPrimary: '#6366f1' } }}
      onLoadError={() => console.error('Payment fields failed to load')}
    >
      <CheckoutForm />
    </OzElements>
  );
}
```

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Styling" icon="palette" href="/sdks/elements/styling">
    Customize field appearance with style config and global appearance.
  </Card>

  <Card title="Error Handling" icon="triangle-alert" href="/sdks/elements/error-handling">
    Handle OzError codes and surface them to users.
  </Card>

  <Card title="Card Elements Reference" icon="credit-card" href="/sdks/elements/card-elements">
    All card options, events, and methods.
  </Card>

  <Card title="Bank Elements Reference" icon="landmark" href="/sdks/elements/bank-elements">
    All bank options, events, and methods.
  </Card>
</CardGroup>
