Skip to main content
Formo
The Formo Web SDK is open source and implements the standard Events API.

Installation

IMPORTANT: To enable accurate attribution, install Formo on both your website (example.com) and your app (app.example.com) using the same project/SDK write key.

Websites

Install this snippet at the <head> of your website:
<script
  src="https://cdn.formo.so/analytics@latest"
  defer onload="window.formofy('<YOUR_WRITE_KEY>');"
></script>
Enable Subresource Integrity (SRI) to improve site security.

React & Next.js

Install the JavaScript SDK via CDN or NPM.
npm install @formo/analytics --save
// App.tsx (or App.js)

import { FormoAnalyticsProvider } from '@formo/analytics';

const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);

root.render(
  <React.StrictMode>
    <FormoAnalyticsProvider writeKey="<YOUR_WRITE_KEY>">
      <App />
    </FormoAnalyticsProvider> 
  </React.StrictMode>
);
// Usage on a page component

import { useFormo } from '@formo/analytics';

const HomePage = () => {
  const analytics = useFormo();

  useEffect(() => {
    // Track a custom event
    analytics.track('Swap Completed', { points: 100 });
  }, [analytics]);

  return <div>Welcome to the Home Page!</div>;
};

export default HomePage;

Identify users

Call identify() after a user connects their wallet or signs in on your website or app:
analytics.identify({ address });

OR

window.formo.identify(...);
If no parameters are specified, the Formo SDK will attempt to auto-identify the wallet address.

Track events

The Web SDK automatically captures common events such as page views and wallet events (connect, disconnect, signature, transaction, etc) with full attribution (referrer, UTM, referrals.) You do not need to configure anything to track wallet events. To track custom events (in-app actions, key conversions) use the track function with send details about the action:
import { useFormo } from '@formo/analytics';

const analytics = useFormo();

analytics.track("Position Opened",  // custom event name
  {
    pool_id: "LINK/ETH", // custom event property
    amount: "1200", // custom event property
    revenue: 99.99,     // revenue (reserved property)
  }
)

OR 

window.formo.track(...)

Code examples

Configuration

Local testing

The SDK skips tracking in localhost by default. To enable tracking locally during development, set tracking to true:
<AnalyticsProvider
    writeKey={WRITE_KEY}
    options={{
        tracking: true, // enable tracking on localhost
    }}
>

Logging

Control the level of logs the SDK prints to the console with the following logLevel settings:
<AnalyticsProvider
    writeKey={WRITE_KEY}
    options={{
        logger: {
            enabled: true,
            levels: ["error", "warn", "info"],
        },
    }}
>
Log LevelDescription
traceShows the most detailed diagnostic information, useful for tracing program execution flow.
debugShows all messages, including function context information for each public method the SDK invokes.
infoShows informative messages about normal application operation.
warnDefault. Shows error and warning messages.
errorShows error messages only.

Batching

To support high-performance environments, the SDK sends events in batches.
<AnalyticsProvider
    writeKey={WRITE_KEY}
    options={{
        flushAt: 20, // defaults to batches of 20 events
        flushInterval: 1000 * 60, // defaults to 1 min
    }}
>
Customize this behavior with the flushAt and flushInterval configuration parameters.

Ready callback

The ready callback function executes once the Formo SDK is fully loaded and ready to use. This is useful for performing initialization tasks or calling SDK methods that require the SDK to be ready.
<AnalyticsProvider
    writeKey={WRITE_KEY}
    options={{
        ready: function(formo) {
            // SDK is ready
            console.log('Formo SDK is ready!');            
            formo.identify(); // Auto-identify user
        },
    }}
>
For Browser installations, you can use the ready callback in the onload attribute:
<script
  src="https://cdn.formo.so/analytics@latest"
  defer
  onload="
    window.formofy('<YOUR_WRITE_KEY>', {
      ready: function(formo) {
        formo.identify();
      }
    });
  "
></script>

Environments

You can control tracking behavior in different environments (test, staging) with the tracking option:
// Initialize with a boolean (simple on/off)
const analytics = await FormoAnalytics.init('your-write-key', {
  tracking: true // Enable tracking everywhere, including localhost
});

// Initialize only on production environment
const analytics = await FormoAnalytics.init('your-write-key', {
  tracking: ENV === 'production'
});

// Initialize with an object for exclusion rules
const analytics = await FormoAnalytics.init('your-write-key', {
  tracking: {
    excludeHosts: ['stage-v2.puri.fi'], // Exclude tracking based on window.location.hostname
    excludePaths: ['/test', '/debug'], // Exclude tracking based on window.location.pathname
    excludeChains: [5] // Exclude tracking on Goerli testnet (5)
  }
});
Specify exact hostnames and exact paths in exclusion lists. The Formo Web SDK includes simplified consent management functionality to help you comply with privacy regulations like GDPR, CCPA, and ePrivacy Directive. Control user tracking preferences with simple opt-out and opt-in methods:
import { useFormo } from '@formo/analytics';

const analytics = useFormo();

// Check if user has opted out of tracking
console.log(analytics.hasOptedOutTracking())

// If user has not opted out, tracking is enabled
analytics.track('page_view');

// Opt out of tracking (stops all tracking for the user)
analytics.optOutTracking();

// Opt back into tracking (re-enables tracking for the user)
analytics.optInTracking();
For browser installations:
// Manage consent
window.formo.optOutTracking();
window.formo.optInTracking();

Autocapture

You can configure which wallet events are automatically captured by the SDK:
// Enable full autocapture (default)
const analytics = await FormoAnalytics.init('YOUR_API_KEY', {
  autocapture: true
});

// Disable autocapture for signature and transaction events
const analytics = await FormoAnalytics.init('YOUR_API_KEY', {
  autocapture: {
    connect: true,
    disconnect: true,
    signature: false,    // Disable signature tracking
    transaction: false,  // Disable transaction tracking
    chain: true
  }
});

// Disable all autocapture
const analytics = await FormoAnalytics.init('YOUR_API_KEY', {
  autocapture: false
});

Proxy

To avoid ad-blockers from blocking your requests, we recommend setting up a reverse proxy.
Reverse proxy data flow

What happens under the hood when you forward the data to Formo via a reverse proxy

Here are the domains used by Formo to load the SDK and send data:
DomainUse case
cdn.formo.soLoads the SDK (only for Website installation) e.g. https://cdn.formo.so/analytics@1.23.0
events.formo.soReceives data from the SDK (http://events.formo.so/v0/raw_events)

CDN

Some ad blockers block specific SDK-related downloads and API calls based on the domain name and URL. To circumvent this issue you can download and serve the Formo SDK from cdn.formo.so to your own domain e.g. yourdomain.com/formo.js.

Next.js rewrites

If you are using Next.js, you can take advantage of rewrites to behave like a reverse proxy. To do so, add a rewrites() function to your next.config.js file:
// next.config.js
module.exports = {
  async rewrites() {
    return [
      {
        source: "/formo.js",
        destination: "https://cdn.formo.so/analytics@1.23.0", // If using website install script
      },
      {
        source: "/api/ingest",
        destination: "https://events.formo.so/v0/raw_events",
      },
    ];
  },
};
Then, replace the website install script to:
<script
    src="https://yourdomain.com/formo.js"
  ...
></script>
Then, configure the Formo SDK to send requests via your rewrite. Update the apiHost parameter in the Formo SDK:
<AnalyticsProvider
  writeKey={WRITE_KEY}
  options={{
    apiHost: "/api/ingest",
  }}
>
See an example Next.js app here.

Next.js middleware

If you are using Next.js and rewrites aren’t working for you, you can write custom middleware to proxy requests to Formo. Create a file named middleware.js/ts in your base directory (same level as the app folder).
import { NextResponse } from "next/server";

export function middleware(request: any) {
  const hostname = "events.formo.so";

  const requestHeaders = new Headers(request.headers);
  requestHeaders.set("host", hostname);

  let url = request.nextUrl.clone();
  url.protocol = "https";
  url.hostname = hostname;
  url.port = 443;
  url.pathname = url.pathname.replace(/^\/ingest/, "");

  return NextResponse.rewrite(url, {
    headers: requestHeaders,
  });
}

export const config = {
  matcher: "/ingest/:path*",
};
In this file, set up code to match requests to a custom route, set a new host header, change the URL to point to Formo, and rewrite the response. Once done, configure the Formo SDK to send requests via your rewrite:
<AnalyticsProvider
  writeKey={WRITE_KEY}
  options={{
    apiHost: "https://your-host-url.com/ingest",
  }}
>

Others

Alternatively, you can set up your own proxy (with Cloudfront, Cloudflare, etc) and pass the URL as the SDK apiHost:
<script
  src="https://cdn.formo.so/analytics@latest"
  defer
  onload="
    window.formofy('<YOUR_WRITE_KEY>', {
      apiHost: 'https://your-host-url.com/ingest',
      ready: function(formo) {
        formo.identify();
      }
    });
  "
></script>

Verification

To verify the proxy is working:
  • Visit your website / app
  • Open the network tab in your browser’s developer tools
  • Check that analytics requests are going through your domain instead of events.formo.so
  • Check that events show up in the Activity page on the Formo dashboard