> ## Documentation Index
> Fetch the complete documentation index at: https://base-a060aa97-danc-alpha-docs.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# useAddFrame (coming soon)

> Allow users to save your Mini App to their collection (Coming Soon)

Defined in [@coinbase/onchainkit](https://github.com/coinbase/onchainkit)

<Warning>
  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.
</Warning>

<Info>
  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.
</Info>

## Returns

<ResponseField name="addFrame" type="() => Promise<AddFrameResult | null>">
  Function that triggers the add frame flow. Returns a promise that resolves when the user completes the action.

  <Expandable title="AddFrameResult properties">
    <ResponseField name="url" type="string">
      The URL that was saved to the user's collection. This should match your Mini App's manifest URL.
    </ResponseField>

    <ResponseField name="token" type="string">
      Notification token for this user and Mini App combination. Save this to your database to send push notifications later.
    </ResponseField>
  </Expandable>
</ResponseField>

<RequestExample>
  ```tsx components/SaveButton.tsx
  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 })
    });
  }
  ```

  ```tsx components/SmartSaveButton.tsx
  import { useAddFrame, useMiniKit } from '@coinbase/onchainkit/minikit';

  export default function SmartSaveButton() {
    const addFrame = useAddFrame();
    const { context } = useMiniKit();
    
    // Don't show save button if already saved
    if (context.client.added) {
      return <div>✅ Already saved to your collection</div>;
    }

    const handleSave = async () => {
      const result = await addFrame();
      if (result) {
        // Save with user context for analytics
        await fetch('/api/analytics', {
          method: 'POST',
          body: JSON.stringify({
            event: 'frame_saved',
            userFid: context.user.fid,
            url: result.url,
            token: result.token
          })
        });
      }
    };

    return (
      <button onClick={handleSave}>
        Save to Collection
      </button>
    );
  }
  ```

  ```tsx components/GameCompletion.tsx
  import { useAddFrame } from '@coinbase/onchainkit/minikit';
  import { useEffect, useState } from 'react';

  export default function GameCompletion() {
    const addFrame = useAddFrame();
    const [showSavePrompt, setShowSavePrompt] = useState(false);
    
    // Show save prompt after user achieves something
    const handleGameWin = () => {
      setShowSavePrompt(true);
    };

    const handleSave = async () => {
      const result = await addFrame();
      if (result) {
        // User saved after achievement - high engagement signal
        analytics.track('post_achievement_save', {
          achievement: 'game_completed',
          token: result.token
        });
      }
      setShowSavePrompt(false);
    };

    return (
      <div>
        {showSavePrompt && (
          <div className="save-prompt">
            <h3>🎉 Congratulations!</h3>
            <p>Save this game to play again anytime</p>
            <button onClick={handleSave}>Save Game</button>
            <button onClick={() => setShowSavePrompt(false)}>
              Maybe Later
            </button>
          </div>
        )}
      </div>
    );
  }
  ```
</RequestExample>

## Usage Patterns

### Database Storage

Always save the notification token to your database for future use:

```typescript 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:

```tsx
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.');
  }
};
```

<Warning>
  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.
</Warning>

<Info>
  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.
</Info>
