import { useAddFrame } from '@coinbase/onchainkit/minikit';
import { useState } from 'react';

export default function SaveButton() {
  const addFrame = useAddFrame();
  const [isAdding, setIsAdding] = useState(false);

  const handleAddFrame = async () => {
    setIsAdding(true);
    try {
      const result = await addFrame();
      if (result) {
        console.log('Frame saved:', result.url);
        console.log('Notification token:', result.token);
        
        // Save to your database for future notifications
        await saveNotificationToken(result.token, result.url);
        
        alert('Mini App saved successfully! 🎉');
      } else {
        console.log('User cancelled or frame already saved');
      }
    } catch (error) {
      console.error('Failed to save frame:', error);
    } finally {
      setIsAdding(false);
    }
  };

  return (
    <button 
      onClick={handleAddFrame}
      disabled={isAdding}
    >
      {isAdding ? 'Saving...' : 'Save Mini App'}
    </button>
  );
}

async function saveNotificationToken(token: string, url: string) {
  await fetch('/api/notification-tokens', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ token, url })
  });
}
Defined in @coinbase/onchainkit
The Add Frame feature is not yet available in Base App but is coming soon. This documentation describes the upcoming API that will be available when the feature is fully deployed.
Enables users to save your Mini App to their personal collection for quick access. Returns notification credentials when successful, enabling future push notifications to re-engage the user.

Returns

addFrame
() => Promise<AddFrameResult | null>
Function that triggers the add frame flow. Returns a promise that resolves when the user completes the action.
import { useAddFrame } from '@coinbase/onchainkit/minikit';
import { useState } from 'react';

export default function SaveButton() {
  const addFrame = useAddFrame();
  const [isAdding, setIsAdding] = useState(false);

  const handleAddFrame = async () => {
    setIsAdding(true);
    try {
      const result = await addFrame();
      if (result) {
        console.log('Frame saved:', result.url);
        console.log('Notification token:', result.token);
        
        // Save to your database for future notifications
        await saveNotificationToken(result.token, result.url);
        
        alert('Mini App saved successfully! 🎉');
      } else {
        console.log('User cancelled or frame already saved');
      }
    } catch (error) {
      console.error('Failed to save frame:', error);
    } finally {
      setIsAdding(false);
    }
  };

  return (
    <button 
      onClick={handleAddFrame}
      disabled={isAdding}
    >
      {isAdding ? 'Saving...' : 'Save Mini App'}
    </button>
  );
}

async function saveNotificationToken(token: string, url: string) {
  await fetch('/api/notification-tokens', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ token, url })
  });
}

Usage Patterns

Database Storage

Always save the notification token to your database for future use:
pages/api/notification-tokens.ts
// Example API route for storing tokens
// pages/api/notification-tokens.ts
export default async function handler(req, res) {
  if (req.method === 'POST') {
    const { token, url, userFid } = req.body;
    
    await db.notificationTokens.create({
      data: {
        token,
        url,
        userFid,
        createdAt: new Date()
      }
    });
    
    res.status(200).json({ success: true });
  }
}

Strategic Timing

Prompt users to save at high-value moments:
  • After achievements: Completing a game, reaching a milestone
  • After successful transactions: Minting an NFT, making a purchase
  • During onboarding: After showing app value
  • Immediately on load: Before demonstrating value
  • Multiple times: Respect user’s previous decision

Error Handling

Handle various outcomes gracefully:
const handleAddFrame = async () => {
  try {
    const result = await addFrame();
    
    if (result === null) {
      // User cancelled or already saved
      console.log('No action taken');
    } else {
      // Successfully saved
      console.log('Saved with token:', result.token);
    }
  } catch (error) {
    // Network error or other issue
    console.error('Save failed:', error);
    showErrorMessage('Failed to save. Please try again.');
  }
};
The notification token is unique per user and Mini App combination. Store it securely in your database and never expose it in client-side code. Tokens are required for sending push notifications when that feature becomes available.
Users can only save each Mini App once. Subsequent calls to addFrame() for the same user and app will return null. Use context.client.added from useMiniKit to check if the user has already saved your app.