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

# React Client SDK Reference

> Complete reference for the utilsio React Client SDK. Learn about UtilsioProvider, useUtilsio hook, and how to manage user authentication and subscriptions.

The `@utilsio/react` package provides a `UtilsioProvider` component and `useUtilsio` hook to integrate utilsio into your React application. This is the main interface for building subscription experiences.

## UtilsioProvider

The `UtilsioProvider` is the root component that manages the SDK state and provides context to your application. It should wrap your app's root or the entire section that needs access to utilsio.

<Note>
  Always place UtilsioProvider as high in your component tree as possible. If you only wrap a part of your app, only the wrapped components will have access to `useUtilsio()`.
</Note>

### Props

<ParamField path="appId" type="string" required>
  Your unique utilsio Application ID. This identifies your app to utilsio's servers. You can find this in your utilsio dashboard.

  Example: `"550e8400-e29b-41d4-a716-446655440000"`
</ParamField>

<ParamField path="getAuthHeadersAction" type="function" required="false">
  An async function that returns authentication headers for signed requests. This function is called automatically whenever the SDK needs to make a request to utilsio (fetching subscriptions, canceling, creating new ones).

  <AccordionGroup>
    <Accordion title="Parameters">
      <ParamField path="deviceId" type="string" required>
        The unique device identifier from the SDK
      </ParamField>

      <ParamField path="additionalData" type="string">
        Optional additional data to include in the signature (used for subscription operations)
      </ParamField>
    </Accordion>

    <Accordion title="Return Values">
      <ResponseField name="signature" type="object">
        The returned signature for the data
      </ResponseField>

      <ResponseField name="timestamp" type="object">
        System timestamp to prevent replay attack and tampering
      </ResponseField>
    </Accordion>
  </AccordionGroup>

  <Warning>
    **Why it's important:** This ensures your `UTILSIO_APP_SECRET` never leaves your server. The backend computes HMAC signatures using the secret, which prevents tampering with requests.
  </Warning>

  See [Server SDK Reference](/sdks/react/server) for implementation details of the signing endpoint.
</ParamField>

<ParamField path="children" type="ReactNode" required>
  Your application components. Everything wrapped by the provider can access `useUtilsio()`.
</ParamField>

<ParamField path="utilsioBaseUrl" default="https://utilsio.dev" type="string">
  The base URL for the utilsio API and embed page. This will be useful later as utilsio rollout sandbox environment.

  <Note>
    For now, keep the default value for this prop
  </Note>

  Example: `"https://utilsio.dev"`
</ParamField>

<ParamField path="parentOrigin" default="window.location.origin" type="string">
  Your app URL. This is used to establish secure connection with utilsio.

  Example: `"https://myapp.com"`
</ParamField>

### Complete Usage Example (Next.js 16 - App Router)

First, create a server action for signing:

```typescript src/app/actions.ts theme={null}
"use server";

import { deriveAppHashHex, signRequest } from "@utilsio/react/server";

// Derive the HMAC key once at module load (expensive operation)
const appHashHex = deriveAppHashHex({
  appSecret: process.env.UTILSIO_APP_SECRET!,
  salt: process.env.UTILSIO_APP_SALT!,
});

export async function getAuthHeadersAction(input: {
  deviceId: string;
  additionalData?: string;
}) {
  const timestamp = Math.floor(Date.now() / 1000);

  const signature = signRequest({
    appHashHex,
    deviceId: input.deviceId,
    appId: process.env.NEXT_PUBLIC_UTILSIO_APP_ID!,
    timestamp,
    additionalData: input.additionalData,
  });

  return { signature, timestamp: String(timestamp) };
}
```

Then use it in your layout:

```typescript src/app/layout.tsx theme={null}
import { UtilsioProvider } from "@utilsio/react/client";
import { getAuthHeadersAction } from "./actions";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html>
      <body>
        <UtilsioProvider
          utilsioBaseUrl={process.env.NEXT_PUBLIC_UTILSIO_APP_URL!}
          appId={process.env.NEXT_PUBLIC_UTILSIO_APP_ID!}
          getAuthHeadersAction={getAuthHeadersAction}
        >
          {children}
        </UtilsioProvider>
      </body>
    </html>
  );
}
```

## useUtilsio Hook

The `useUtilsio` hook provides access to the current state and actions of the SDK. This is how you interact with utilsio from your components.

<Warning>
  This hook can only be used in client components (marked with `"use client"`). If you need to use it in a Server Component, create a separate client component and pass data through props.
</Warning>

### Basic Usage

```typescript theme={null}
"use client";

import { useUtilsio } from "@utilsio/react/client";

export function MyComponent() {
  const {
    user,
    deviceId,
    currentSubscription,
    loading,
    error,
    refresh,
    cancelSubscription,
    redirectToConfirm,
  } = useUtilsio();

  // Use these values in your component
}
```

### State Props

<ResponseField name="user" type="UtilsioUser | null">
  The currently authenticated user (happy path), or `null` if not logged in to utilsio.dev yet (normal path).

  <Warning>
    **Safari & Privacy Extensions:** The `user` object may be `null` even when the user is logged into utilsio.dev. This happens in browsers with strict third-party cookie blocking (Safari, Brave) or privacy extensions. This is **expected behavior** and you should handle it gracefully.

    **Solution:** Don't block your UI if `user` is null. Show your subscribe button anyway - when users click it, they'll be redirected to utilsio.dev which will handle authentication automatically. The `user` object is provided for convenience (e.g., displaying user info), not as a gate to functionality.
  </Warning>

  <Expandable title="UtilsioUser">
    <ResponseField name="id" type="string">
      The unique ID for user
    </ResponseField>

    <ResponseField name="email" type="string | undefined">
      User's email (optional — may not be set for all accounts)
    </ResponseField>

    <ResponseField name="phone" type="string | undefined">
      User's phone number (optional)
    </ResponseField>

    <ResponseField name="user_metadata" type="Record<string, unknown>">
      Free-form metadata object. We welcome all suggestions to what should appear here.
    </ResponseField>

    <ResponseField name="created_at" type="string">
      Timestamp showing when the user account was created
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="deviceId" type="string | null">
  A unique identifier for the current device/browser. This is generated automatically by the SDK and persisted in a cookie on the utilsio origin.

  <Note>
    This ID is just for reference, DO NOT rely on this for critical authentication flows as it might change across browser sessions and data clearance.
  </Note>
</ResponseField>

<ResponseField name="currentSubscription" type="UtilsioSubscription | null">
  The active subscription for the current user/device combination. Returns `null` if there's no active subscription.

  <Expandable title="UtilsioSubscription">
    <ResponseField name="id" type="string">
      The unique ID for the subscription
    </ResponseField>

    <ResponseField name="amountPerDay" type="string">
      The subscription's value in unit of USD/day (although billing is per-second)
    </ResponseField>

    <ResponseField name="isActive" type="boolean">
      Whether the subscription is currently active.
    </ResponseField>

    <ResponseField name="createdAt" type="string">
      Timestamp showing when the subscription was created
    </ResponseField>

    <ResponseField name="cancelledAt" type="string | null">
      Timestamp showing when the subscription was cancelled (null if it is still active). Note that you won't see this often, as cancelled subscriptions won't be returned as `currentSubscription`.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="loading" type="boolean">
  Whether the SDK is currently fetching initial state from the server. Starts as `true` while the SDK initializes, becomes `false` once ready.

  <Tip>
    It is advised to wait for loading to be `false` before rendering your page / component, although it is ultimately up to you. Note: don't block the subscribe button while loading — `user` may be `null` in Safari/Brave even after loading completes, and that's expected.
  </Tip>
</ResponseField>

<ResponseField name="error" type="string | null">
  An error message if something went wrong during initialization or while performing actions.

  **Common errors:**

  * `"Failed to authenticate"` - Signing endpoint is down or unreachable
  * `"Network error"` - Connection issues with utilsio servers
  * `"Invalid credentials"` - App ID or signing is incorrect
  * `"User must be authenticated to cancel subscription"` - User is not logged in
  * `"Either deviceId or appUrl is required to cancel subscription"` - Safari users must provide appUrl
</ResponseField>

### Actions

<ResponseField name="refresh" type="function">
  Manually refresh the user state and subscription info from the server. Useful after making changes or periodically polling for updates.

  <AccordionGroup>
    <Accordion title="Parameters">
      This function does not accept any parameters
    </Accordion>

    <Accordion title="Return Values">
      This function does not return any values
    </Accordion>
  </AccordionGroup>

  **When to use:**

  * After subscribing to / cancelling a subscription
  * Periodically in long-running apps
  * When returning from external authentication flows
  * When you suspect subscription state is stale
  * Or just simply every time page reload - it's a simple function

  <Note>
    This function:

    * Sets `loading` to `true` while operating
    * Returns early if deviceId is null (no user authenticated)
    * Updates `currentSubscription` with the response
  </Note>
</ResponseField>

<ResponseField name="cancelSubscription" type="function">
  Cancel one or more subscriptions. For Safari compatibility, provide `appUrl` to enable server-side signature generation.

  <AccordionGroup>
    <Accordion title="Parameters">
      <ParamField path="subscriptionIds" type="string[]" required>
        Array of subscription IDs to cancel (typically just one)
      </ParamField>

      <ParamField path="appUrl" type="string">
        Your app's URL (e.g., `"https://yourdomain.com"`). Required for Safari users where deviceId is not available due to cookie blocking. When provided, the SDK will use a server-side callback flow to generate signatures.
      </ParamField>
    </Accordion>

    <Accordion title="Return Values">
      This function does not return any values
    </Accordion>
  </AccordionGroup>

  **When to use:**

  * After subscribing to / cancelling a subscription
  * Periodically in long-running apps
  * When returning from external authentication flows
  * When you suspect subscription state is stale
  * Or just simply every time page reload - it's a simple function

  <Note>
    After cancellation completes, `currentSubscription` becomes `null`.
  </Note>
</ResponseField>

<ResponseField name="redirectToConfirm" type="function">
  Cancel one or more subscriptions.

  <AccordionGroup>
    <Accordion title="Parameters">
      <ParamField path="appId" type="string" required>
        Your utilsio app ID (same as in UtilsioProvider).
      </ParamField>

      <ParamField path="appName" type="string" required>
        Display name of your app shown during the subscription flow.

        Example: `"My Premium App"`
      </ParamField>

      <ParamField path="amountPerDay" type="string" required>
        The daily subscription amount as a string. This is the billing amount per day that will be charged to the user.

        <Note>
          The amount is in USD/day unit, so passing `1` would mean that the user will be charged 1 USD/day or 30 USD/month
        </Note>
      </ParamField>

      <ParamField path="appLogo" type="string">
        URL to your app's logo. Displayed during the subscription flow.
      </ParamField>

      <ParamField path="appUrl" type="string">
        Your app's URL. Used for redirects and context.
      </ParamField>

      <ParamField path="nextSuccess" type="string" required>
        URL to redirect the user to after successful subscription. Typically your success page or home page.

        Example: `"https://myapp.com/success"`
      </ParamField>

      <ParamField path="nextCancelled" type="string" required>
        URL to redirect the user to if they cancel the subscription flow. Typically back to your home page.

        Example: `"https://myapp.com/cancelled"`
      </ParamField>
    </Accordion>

    <Accordion title="Return Values">
      This function does not return any values
    </Accordion>
  </AccordionGroup>
</ResponseField>

## Complete Usage Example (Next.js 16 - App Router)

Here's a fully functional subscription component using all the pieces:

```typescript src/components/SubscriptionWidget.tsx theme={null}
"use client";

import { useUtilsio } from "@utilsio/react/client";
import { useCallback, useState } from "react";

export function SubscriptionWidget() {
  const {
    user,
    currentSubscription,
    loading,
    error,
    redirectToConfirm,
    cancelSubscription,
  } = useUtilsio();

  const [isCancelling, setIsCancelling] = useState(false);
  const [cancelError, setCancelError] = useState<string | null>(null);

  const handleSubscribe = useCallback(() => {
    const appUrl = process.env.NEXT_PUBLIC_APP_URL!;

    redirectToConfirm({
      appId: process.env.NEXT_PUBLIC_UTILSIO_APP_ID!,
      appName: "Premium App",
      amountPerDay: (10 / 30).toFixed(6), // $10/month
      appUrl,
      nextSuccess: `${appUrl}/success`,
      nextCancelled: `${appUrl}/`,
    });
  }, [redirectToConfirm]);

  const handleCancel = useCallback(async () => {
    if (!currentSubscription) return;
    if (!confirm("Cancel your subscription?")) return;

    setIsCancelling(true);
    setCancelError(null);

    try {
      const appUrl = process.env.NEXT_PUBLIC_APP_URL!;
      await cancelSubscription([currentSubscription.id], appUrl);
    } catch (err) {
      setCancelError(err instanceof Error ? err.message : "Unknown error");
    } finally {
      setIsCancelling(false);
    }
  }, [currentSubscription, cancelSubscription]);

  // Has active subscription
  if (currentSubscription) {
    const dailyAmount = parseFloat(currentSubscription.amountPerDay);
    // Using 30-day month approximation for display
    const monthlyAmount = (dailyAmount * 30).toFixed(2);

    return (
      <div className="p-4 border rounded">
        <h2>Your Subscription</h2>
        {user && <p>Email: {user.email}</p>}
        <p>Amount: ~${monthlyAmount}/month</p>
        <p>Started: {new Date(currentSubscription.createdAt).toLocaleDateString()}</p>

        <button
          onClick={handleCancel}
          disabled={isCancelling}
          className="mt-4 px-4 py-2 bg-red-500 text-white rounded"
        >
          {isCancelling ? "Cancelling..." : "Cancel Subscription"}
        </button>

        {cancelError && <p className="text-red-600 mt-2">{cancelError}</p>}
      </div>
    );
  }

  // No subscription - show subscribe button
  // Note: user might be null (Safari/privacy extensions), but that's okay!
  // The redirect will handle authentication automatically
  return (
    <div className="p-4 border rounded">
      <h2>Subscribe</h2>
      {user && <p>Email: {user.email}</p>}
      <p>~$10/month for premium features</p>

      <button
        onClick={handleSubscribe}
        className="mt-4 px-4 py-2 bg-blue-500 text-white rounded"
      >
        Subscribe Now
      </button>
    </div>
  );
}
```

## Environment Variables

When setting up UtilsioProvider, you'll typically use these environment variables:

```env theme={null}
NEXT_PUBLIC_UTILSIO_APP_ID=your-app-id

NEXT_PUBLIC_APP_URL=https://yourapp.com
NEXT_PUBLIC_UTILSIO_APP_URL=https://utilsio.dev
```

<Warning>
  The `NEXT_PUBLIC_*` prefix means these are safe to expose in the browser. Your `UTILSIO_APP_SECRET` should NOT be exposed and should only be used on the backend in your signing endpoint.
</Warning>
