Installation

How to install, initialize, and configure the Fruga Relay SDK for your platform. This reference covers the full installation steps via package managers, configuration options, and how to handle SDK lifecycle events.

💡
If you are setting up Relay for the first time, start with the Getting Started guide, which walks through the full integration end to end. This page is the SDK-specific reference for the frontend integration.

1. Install the SDK

The Fruga SDK is published on the public npm registry — no registry configuration or authentication needed. Install @fruga/sdk using your preferred package manager.

💡
Always use pnpm unless your project strictly enforces another package manager.
pnpm add @fruga/sdk

The SDK exposes two integration surfaces:

// React — component and hook
import { FrugaWidget, useFrugaWidget } from '@fruga/sdk/loader/react';

// Imperative API — any framework or vanilla JS
import { init, mount, unmount } from '@fruga/sdk/loader';

2. Adding the container (Optional)

The widget can render as a modal overlay or mount inline within a specific element. If you want it to render inline, add an empty container element to your page.

<div id="fruga-widget-root" style="width:100%; min-height:600px;"></div>

3. Initialising the widget

Call init() to boot up the widget. By default, the widget will mount immediately (autoMount: true).

⚠️
Security Reminder: Do not embed private secrets. The SDK only ever handles your public partnerKey and the access token your own backend returns - your PARTNER_SECRET never leaves your server. See the Authentication guide.

Every example below passes an onTokenRequired callback. This is how the widget authenticates: it calls your endpoint, which signs an assertion and exchanges it for a Fruga access token server-side, then returns that token. The callback is shared across all three integration styles:

const onTokenRequired = async () => {
  const res = await fetch('/fruga/token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ userRef: currentUser.id }),
  });
  if (!res.ok) throw new Error('Failed to fetch Fruga token');
  const { accessToken } = await res.json();
  return accessToken;
};

See Getting Started for the backend endpoint this calls.

The <FrugaWidget /> component renders its own container and handles init, mount, and cleanup through the React lifecycle:

import { FrugaWidget } from '@fruga/sdk/loader/react';

export const OffersPage = ({ partnerKey }) => (
  <div style={{ width: '100%', minHeight: '600px' }}>
    <FrugaWidget partnerKey={partnerKey} onTokenRequired={onTokenRequired} />
  </div>
);

React hook (manual control)

Use useFrugaWidget when you want to decide when the widget mounts — for example behind a button:

import { useFrugaWidget } from '@fruga/sdk/loader/react';

export const OffersPage = ({ partnerKey }) => {
  const { mount, unmount } = useFrugaWidget({
    partnerKey,
    onTokenRequired,
    autoMount: false,   // You control the mount timeline
  });

  return (
    <div className="flex flex-col gap-4">
      <div id="fruga-widget-root"></div>
      <button onClick={() => mount()}>View Offers</button>
    </div>
  );
};

Imperative API (vanilla JS / other frameworks)

Outside React, call init() directly:

import { init, unmount } from '@fruga/sdk/loader';

await init({
  partnerKey: process.env.NEXT_PUBLIC_PARTNER_ID,
  onTokenRequired,
});

// Later, when tearing the page down:
unmount();

Configuration Options

OptionTypeRequiredDescription
partnerKeystringYesYour public key identifying your partner environment - the NEXT_PUBLIC_PARTNER_ID value Fruga issued you. Separate values for development and production.
onTokenRequiredfunctionIn practice, yesAsync callback returning a fresh Fruga access token - not an assertion. The widget calls it on first load and whenever the token needs refreshing. Without it the widget cannot authenticate a user.
containerIdstringNoThe ID of the DOM element where the widget should mount (defaults to fruga-widget-root). If provided, it renders inline.
autoMountbooleanNoControls automatic DOM injection. Defaults to true. Set to false if you want to call mount() manually later.
userIdstringNoOptional identity context. The authoritative user identity comes from the userRef claim in the assertion your backend signs, not from this option.
theme'light' | 'dark'NoThe initial visual theme.
primaryColorstringNoHex code for the primary brand colour.

Architecture & styling

The SDK mounts an iframe shell to prevent your CSS from leaking into our widget and vice versa (Shadow DOM isolation).

⚠️
Branding is not set in init(). Colours, theme defaults, launcher copy, and related appearance are configured for your partner environment on Fruga’s side and applied when the widget loads. See Customising the Interface and Branding. Do not attempt CSS overrides on the internal iframe structure, as it will break isolation.

Troubleshooting

  • Widget fails to render: Verify your partnerKey is correct for the environment. Ensure your mount() logic aligns with your autoMount setting.
  • Widget mounts but shows no user data: onTokenRequired returned an empty string or an invalid token. Do not catch errors inside the callback and return '' - let them throw, so the failure is visible instead of surfacing as an empty widget. Check your token endpoint’s response in the Network tab.
  • Multiple widgets rendering: If using React 18+ strict mode, ensure the component isn’t unmounting/remounting excessively without proper cleanup (unmount()), or wrap the initialization carefully so it only executes once.