Skip to main content
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.
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().

Props

appId
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"
getAuthHeadersAction
function
required
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).
deviceId
string
required
The unique device identifier from the SDK
additionalData
string
Optional additional data to include in the signature (used for subscription operations)
signature
object
The returned signature for the data
timestamp
object
System timestamp to prevent replay attack and tampering
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.
See Server SDK Reference for implementation details of the signing endpoint.
children
ReactNode
required
Your application components. Everything wrapped by the provider can access useUtilsio().
utilsioBaseUrl
string
default:"https://utilsio.dev"
The base URL for the utilsio API and embed page. This will be useful later as utilsio rollout sandbox environment.
For now, keep the default value for this prop
Example: "https://utilsio.dev"
parentOrigin
string
default:"window.location.origin"
Your app URL. This is used to establish secure connection with utilsio.Example: "https://myapp.com"

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

First, create a server action for signing:
src/app/actions.ts
"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:
src/app/layout.tsx
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.
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.

Basic Usage

"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

user
UtilsioUser | null
The currently authenticated user (happy path), or null if not logged in to utilsio.dev yet (normal path).
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.
deviceId
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.
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.
currentSubscription
UtilsioSubscription | null
The active subscription for the current user/device combination. Returns null if thereโ€™s no active subscription.
loading
boolean | null
Whether the SDK is currently fetching initial state from the server. This is true while the SDK initializes and false once itโ€™s ready.
It is advised to wait for loading to be true before rendering your page / component, although it is ultimately up to you.
error
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

Actions

refresh
function
Manually refresh the user state and subscription info from the server. Useful after making changes or periodically polling for updates.
This function does not accept any parameters
This function does not return any values
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
This function:
  • Sets loading to true while operating
  • Returns early if deviceId is null (no user authenticated)
  • Updates currentSubscription with the response
cancelSubscription
function
Cancel one or more subscriptions. For Safari compatibility, provide appUrl to enable server-side signature generation.
subscriptionsId
string[]
required
Array of subscription IDs to cancel (typically just one)
appUrl
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.
This function does not return any values
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
After cancellation completes, currentSubscription becomes null.
redirectToConfirm
function
Cancel one or more subscriptions.
appId
string
required
Your utilsio app ID (same as in UtilsioProvider).
appName
string
required
Display name of your app shown during the subscription flow.Example: "My Premium App"
amountPerDay
string
required
The daily subscription amount as a string. This is the billing amount per day that will be charged to the user.
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
URL to your appโ€™s logo. Displayed during the subscription flow.
appUrl
string
Your appโ€™s URL. Used for redirects and context.
nextSuccess
string
required
URL to redirect the user to after successful subscription. Typically your success page or home page.Example: "https://myapp.com/success"
nextCancelled
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"
This function does not return any values

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

Hereโ€™s a fully functional subscription component using all the pieces:
src/components/SubscriptionWidget.tsx
"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>
        <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:
NEXT_PUBLIC_UTILSIO_APP_ID=your-app-id

NEXT_PUBLIC_APP_URL=https://yourapp.com
NEXT_PUBLIC_UTILSIO_APP_URL=https://utilsio.dev
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.
Last modified on February 17, 2026