Skip to main content

Triggers Integration Guide

Overview

Trigger Kit is a powerful library for integrating blockchain widgets into your React applications. It provides a seamless way to import and execute blockchain actions through URLs or metadata objects, with full support for wagmi and various wallet connections. You can integrate widgets in multiple ways, from simple URL-based integration to advanced API-based customization. This guide walks you through each method, increasing in complexity.

Installation

Install the required packages:
npm install @sherrylinks/slinks @sherrylinks/slinks-core

Peer Dependencies

Make sure you have these peer dependencies installed:
npm install wagmi viem @tanstack/react-query framer-motion react react-dom

Quick Start

1. Import Required Styles

Add the Relayer CSS to your application:
import '@sherrylinks/slinks/index.css';

2. Create a Wagmi Adapter

import { createWagmiAdapter } from '@sherrylinks/slinks';
import { useConfig } from 'wagmi';

function MyApp() {
  const config = useConfig(); // Your wagmi config
  const adapter = createWagmiAdapter(config);

  return <div>{/* Your app content */}</div>;
}

3. Render Your First Widget

import { Trigger } from '@sherrylinks/slinks';

function MyTriggerComponent() {
  return <Trigger url="https://api.relayer.fi/v1/widget/example/metadata" adapter={adapter} enableAnalytics={true} />;
}
URL Requirements: Ensure that the provided URL returns valid metadata. The Trigger component will fetch the metadata from the URL and validate it. If the metadata is valid, a widget will be rendered. Invalid metadata will result in an error state.

Integration Methods

This section covers different ways to integrate widgets, from the simplest to the most advanced.

Method 1: Simple URL Integration

The simplest way to integrate a widget is by providing a URL that returns valid metadata. Use case: When you have a direct URL to widget metadata.
import { Trigger } from '@sherrylinks/slinks';

function URLTrigger({ adapter }) {
  return (
    <Trigger
      url="https://api.relayer.fi/v1/widget/example/metadata"
      adapter={adapter}
      enableAnalytics={true}
    />
  );
}
Pros:
  • Simplest integration method
  • No additional setup required
  • Widget is automatically validated
Cons:
  • Limited customization options
  • Requires a valid metadata endpoint

Method 2: Directory API Integration

Fetch available widgets from the Relayer directory and display them dynamically. Use case: When you want to show a curated list of available widgets to users.
import { Trigger } from '@sherrylinks/slinks';
import { useState, useEffect } from 'react';

function DirectoryTriggers({ adapter }) {
  const [widgets, setWidgets] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const fetchWidgets = async () => {
      try {
        const response = await fetch('https://api.relayer.fi/v1/directory/v1');
        const data = await response.json();
        setWidgets(data.widgets);
      } catch (error) {
        console.error('Error fetching widgets:', error);
      } finally {
        setLoading(false);
      }
    };

    fetchWidgets();
  }, []);

  if (loading) return <div>Loading widgets...</div>;

  return (
    <div
      style={{
        display: 'grid',
        gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))',
        gap: '20px',
      }}
    >
      {widgets
        .filter(widget => widget.state === 'trusted') // Only show trusted widgets
        .map(widget => (
          <Trigger
            key={widget.id}
            url={`${widget.host}${widget.path}`}
            adapter={adapter}
            enableAnalytics={true}
          />
        ))}
    </div>
  );
}

Directory API Response Structure

The directory API returns a structured response with available widgets:
{
  "lastUpdated": "2025-07-11T17:39:19.431Z",
  "version": "1.0.0",
  "widgets": [
    {
      "id": "e2349bf1-0893-44cd-a5aa-0f8104b7094d",
      "host": "https://api.relayer.fi",
      "state": "trusted",
      "category": "Relayer KOL",
      "subcategory": "Swap",
      "verifiedAt": "2025-07-11T15:51:49.610392",
      "protocol": "Protocol",
      "path": "/v1/widget/e2349bf1-0893-44cd-a5aa-0f8104b7094d/metadata",
      "source": "chat"
    }
  ],
  "templates": [...],
  "maliciousDomains": [...]
}
Pros:
  • Access to curated widget list
  • Can filter by category, protocol, or state
  • Automatic updates when new widgets are added
Cons:
  • Requires network request
  • Limited to widgets in the directory

Method 3: Custom Metadata Objects

Define widgets using metadata objects directly in your code. Use case: When you want full control over widget configuration and don’t need to fetch from an API.

Basic Setup

First, install the Relayer SDK for metadata validation:
npm install @sherrylinks/sherry-sdk

Simple Custom Metadata

import { Trigger } from '@sherrylinks/slinks';
import { createMetadata } from '@sherrylinks/sherry-sdk';
import type { ValidatedMetadata } from '@sherrylinks/sherry-sdk';

function CustomWidget({ adapter }) {
  const customMetadata = {
    url: 'https://example.com',
    icon: 'https://example.com/icon.png',
    title: 'My Custom Widget',
    description: 'A custom blockchain widget',
    baseUrl: 'https://api.example.com',
    actions: [
      {
        type: 'transfer',
        label: 'Send 0.01 ETH',
        to: '0x742d35Cc6634C0532925a3b8D000b7AA5b7eA48C',
        amount: 0.01,
        chains: {
          source: 1, // Ethereum mainnet
        },
      },
    ],
  };

  // Validate and create metadata
  const validatedMetadata: ValidatedMetadata = createMetadata(customMetadata);

  return (
    <Trigger
      metadata={validatedMetadata}
      adapter={adapter}
      securityState="unknown" // Custom metadata widgets appear as unverified
      enableAnalytics={true}
    />
  );
}
Pros:
  • Full control over widget configuration
  • No network requests needed
  • Can be statically defined
Cons:
  • Widgets appear as unverified
  • Requires manual metadata creation
  • Need to understand metadata structure
Security State: Widgets created with custom metadata will always appear as unverified to users, as they haven’t gone through the official verification process. Always validate your metadata using createMetadata before rendering.

Method 4: API-Based Customization (Advanced)

With your API key, you can fetch metadata from Relayer’s widget endpoints, customize it, and render personalized widgets. Use case: When you need to personalize widgets based on user preferences, modify parameters dynamically, or create white-label experiences.

Basic API Customization

import { Trigger, createMetadata } from '@sherrylinks/slinks';
import { useState, useEffect } from 'react';

function CustomizedWidget({ adapter, apiKey, widgetId }) {
  const [metadata, setMetadata] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const fetchAndCustomizeMetadata = async () => {
      try {
        // Fetch metadata from Relayer API using your API key
        const response = await fetch(
          `https://api.relayer.fi/v1/widget/${widgetId}/metadata`,
          {
            headers: {
              'Authorization': `ApiKey ${apiKey}`,
              'Content-Type': 'application/json',
            },
          }
        );

        if (!response.ok) {
          throw new Error('Failed to fetch metadata');
        }

        const fetchedMetadata = await response.json();

        // Customize the metadata
        const customizedMetadata = {
          ...fetchedMetadata,
          title: 'My Customized Widget', // Override title
          description: 'This widget has been customized for my use case',
          actions: fetchedMetadata.actions.map(action => ({
            ...action,
            label: `Custom ${action.label}`,
            // Modify amounts, addresses, or other parameters as needed
          })),
        };

        // Validate the customized metadata
        const validatedMetadata = createMetadata(customizedMetadata);
        setMetadata(validatedMetadata);
      } catch (error) {
        console.error('Error fetching or customizing metadata:', error);
      } finally {
        setLoading(false);
      }
    };

    fetchAndCustomizeMetadata();
  }, [apiKey, widgetId]);

  if (loading) return <div>Loading widget...</div>;
  if (!metadata) return <div>Failed to load widget</div>;

  return (
    <Trigger
      metadata={metadata}
      adapter={adapter}
      securityState="verified" // Metadata from API is verified
      enableAnalytics={true}
    />
  );
}

Advanced Customization Example

Apply complex customizations based on user preferences or business logic:
import { Trigger, createMetadata } from '@sherrylinks/slinks';
import { useState, useEffect } from 'react';

function AdvancedCustomizedWidget({ adapter, apiKey, widgetId, userPreferences }) {
  const [metadata, setMetadata] = useState(null);

  useEffect(() => {
    const customizeWidget = async () => {
      try {
        // Fetch original metadata
        const response = await fetch(
          `https://api.relayer.fi/v1/widget/${widgetId}/metadata`,
          {
            headers: {
              'Authorization': `ApiKey ${apiKey}`,
            },
          }
        );

        const originalMetadata = await response.json();

        // Apply customizations based on user preferences
        const customizedMetadata = {
          ...originalMetadata,
          actions: originalMetadata.actions.map(action => {
            // Example: Adjust amounts based on user tier
            if (action.type === 'transfer' && userPreferences.tier === 'premium') {
              return {
                ...action,
                amount: action.amount * 1.5, // Premium users get 50% more
              };
            }

            // Example: Change default recipient for transfers
            if (action.type === 'transfer' && userPreferences.defaultRecipient) {
              return {
                ...action,
                to: userPreferences.defaultRecipient,
              };
            }

            return action;
          }),
        };

        const validatedMetadata = createMetadata(customizedMetadata);
        setMetadata(validatedMetadata);
      } catch (error) {
        console.error('Error customizing widget:', error);
      }
    };

    customizeWidget();
  }, [apiKey, widgetId, userPreferences]);

  if (!metadata) return null;

  return (
    <Trigger
      metadata={metadata}
      adapter={adapter}
      securityState="verified"
      enableAnalytics={true}
    />
  );
}
API Key Required: To fetch metadata from Relayer’s widget endpoints, you need a valid API key. Include it in the Authorization header using the format ApiKey {your_api_key}. The fetched metadata can be customized before validation and rendering, allowing you to create personalized widget experiences for your users.
Pros:
  • Access to verified widgets from Relayer API
  • Full customization capabilities
  • Can personalize based on user data
  • Widgets maintain verified status
Cons:
  • Requires API key
  • More complex implementation
  • Additional network requests

Advanced Configuration

Security States

Control how security warnings are displayed to users:
<Trigger
  metadata={validatedMetadata}
  adapter={adapter}
  securityState="verified" // Options: "unknown", "malicious", "verified"
/>
Security States:
  • "verified": Widget has been verified by Relayer (from API endpoints)
  • "unknown": Widget source is not verified (custom metadata)
  • "malicious": Widget has been flagged as potentially unsafe

Analytics

Analytics are automatically collected for all integrations. The enableAnalytics option will be deprecated in future versions as analytics collection will always be active.
<Trigger
  url="https://example.com/api/widget"
  adapter={adapter}
  enableAnalytics={true} // Always collected - this option will be deprecated
/>

Complete Examples

Example 1: Simple Widget Grid

Display multiple widgets in a grid layout:
import React from 'react';
import { Trigger, createWagmiAdapter } from '@sherrylinks/slinks';
import { useConfig } from 'wagmi';
import '@sherrylinks/slinks/index.css';

function WidgetContainer() {
  const config = useConfig();
  const adapter = createWagmiAdapter(config);

  const widgets = [
    'https://api.relayer.fi/v1/widget/example-1/metadata',
    'https://api.relayer.fi/v1/widget/example-2/metadata',
  ];

  return (
    <div
      style={{
        display: 'grid',
        gridTemplateColumns: 'repeat(auto-fit, minmax(300px, 1fr))',
        gap: '20px',
        padding: '20px',
      }}
    >
      {widgets.map((url, index) => (
        <Trigger
          key={index}
          url={url}
          adapter={adapter}
          enableAnalytics={true}
        />
      ))}
    </div>
  );
}

export default WidgetContainer;

Example 2: Filtered Directory Widgets

Fetch and filter widgets from the directory:
// Fetch all trusted widgets
const response = await fetch('https://api.relayer.fi/v1/directory/v1');
const directory = await response.json();

// Filter by category
const swapWidgets = directory.widgets.filter(
  widget => widget.state === 'trusted' && widget.subcategory === 'Swap',
);

// Filter by protocol
const protocolWidgets = directory.widgets.filter(
  widget => widget.protocol === 'Protocol'
);

Best Practices

  1. Always validate metadata before passing to Trigger using createMetadata
  2. Use proper error boundaries around Trigger components
  3. Test with different networks and wallet states
  4. Implement proper loading states for better UX
  5. Cache metadata when possible to improve performance
  6. Handle errors gracefully - provide fallback UI for failed widgets
  7. Use API keys securely - never expose them in client-side code if possible

Error Handling

The Trigger component handles errors gracefully:
  • Invalid URLs show error states
  • Malicious widgets are blocked automatically
  • Network errors are displayed to users
  • Failed transactions can be retried
  • Validation errors prevent rendering invalid widgets
Always wrap Trigger components in error boundaries:
import { ErrorBoundary } from 'react-error-boundary';

function WidgetWithErrorBoundary({ adapter, url }) {
  return (
    <ErrorBoundary fallback={<div>Widget failed to load</div>}>
      <Trigger url={url} adapter={adapter} />
    </ErrorBoundary>
  );
}

API Reference

Trigger Props

export type TriggerProps = {
  adapter: RelayerAdapter;
  enableAnalytics?: boolean; // 📊 Always collected - will be deprecated
  player?: boolean;
} & (
  | {
      url: string;
      metadata?: never;
      securityState?: never;
    }
  | {
      metadata: ValidatedMetadata;
      securityState: TriggerSecurityState;
      url?: string;
    }
);

RelayerAdapter Interface

The adapter interface provides wallet connectivity:
interface RelayerAdapter {
  connect(): Promise<string>;
  sendTransaction(params: TransactionParams): Promise<{ hash: string }>;
  signMessage(message: string): Promise<string>;
  switchChain(chainId: number): Promise<void>;
  getAddress(): Promise<string>;
  getChainId(): Promise<number>;
  isConnected(): Promise<boolean>;
}

Troubleshooting

Common Issues

  1. Wallet not connected: Ensure your wagmi config is properly set up
  2. Metadata validation errors: Check your metadata format using the Metadata Validation guide
  3. Network issues: Verify your RPC endpoints are correctly configured
  4. Style conflicts: Import the CSS file correctly: import '@sherrylinks/slinks/index.css'
  5. API key errors: Verify your API key is valid and included in the Authorization header

Getting Help