or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

event-handling.mdimage-operations.mdindex.mdpaste-button.mdtext-operations.mdurl-operations.md
tile.json

event-handling.mddocs/

Event Handling

Clipboard change event listeners for detecting and responding to clipboard content changes. Event handling is supported on iOS and Android platforms but is a no-op on Web.

Capabilities

Add Clipboard Listener

Adds a listener that fires whenever the content of the user's clipboard changes.

/**
 * Adds a listener that will fire whenever the content of the user's clipboard changes.
 * This method is a no-op on Web.
 * @param listener - Callback to execute when listener is triggered. The callback receives 
 *                   a single argument containing information about clipboard contents.
 * @returns EventSubscription object to remove the listener
 */
function addClipboardListener(listener: (event: ClipboardEvent) => void): EventSubscription;

interface ClipboardEvent {
  /**
   * @deprecated Returns empty string. Use getStringAsync() instead to retrieve clipboard content.
   */  
  content: string;
  /** An array of content types that are available on the clipboard */
  contentTypes: ContentType[];
}

enum ContentType {
  PLAIN_TEXT = 'plain-text',
  HTML = 'html', 
  IMAGE = 'image',
  URL = 'url' // iOS only
}

interface EventSubscription {
  remove(): void;
}

Usage Examples:

import * as Clipboard from "expo-clipboard";

// Basic clipboard change detection
const subscription = Clipboard.addClipboardListener(({ contentTypes }) => {
  console.log("Clipboard changed, content types:", contentTypes);
  
  if (contentTypes.includes(Clipboard.ContentType.PLAIN_TEXT)) {
    console.log("Clipboard contains text");
  }
  
  if (contentTypes.includes(Clipboard.ContentType.IMAGE)) {
    console.log("Clipboard contains image");
  }
});

// Comprehensive clipboard monitoring
const subscription = Clipboard.addClipboardListener(async ({ contentTypes }) => {
  if (contentTypes.includes(Clipboard.ContentType.PLAIN_TEXT)) {
    const text = await Clipboard.getStringAsync();
    console.log("Text copied:", text.substring(0, 50) + "...");
  } 
  
  if (contentTypes.includes(Clipboard.ContentType.IMAGE)) {
    const image = await Clipboard.getImageAsync({ format: 'png' });
    if (image) {
      console.log(`Image copied: ${image.size.width}x${image.size.height}`);
    }
  }
  
  if (contentTypes.includes(Clipboard.ContentType.URL)) {
    // iOS only
    const url = await Clipboard.getUrlAsync();
    console.log("URL copied:", url);
  }
});

// React component integration
const ClipboardMonitor = () => {
  const [clipboardContent, setClipboardContent] = useState<string>("");
  
  useEffect(() => {
    const subscription = Clipboard.addClipboardListener(async ({ contentTypes }) => {
      if (contentTypes.includes(Clipboard.ContentType.PLAIN_TEXT)) {
        const text = await Clipboard.getStringAsync();
        setClipboardContent(text);
      }
    });
    
    return () => subscription.remove();
  }, []);
  
  return <Text>Last copied: {clipboardContent}</Text>;
};

Remove Clipboard Listener

Removes a previously added clipboard listener using its subscription.

/**
 * Removes the listener added by addClipboardListener. This method is a no-op on Web.
 * @param subscription - The subscription to remove (created by addClipboardListener)
 */
function removeClipboardListener(subscription: EventSubscription): void;

Usage Examples:

import * as Clipboard from "expo-clipboard";

// Manual listener management
const subscription = Clipboard.addClipboardListener(() => {
  console.log("Clipboard changed!");
});

// Remove listener when done
Clipboard.removeClipboardListener(subscription);

// Alternative using subscription.remove() directly
const subscription = Clipboard.addClipboardListener(() => {
  console.log("Clipboard changed!");
});

subscription.remove(); // Equivalent to removeClipboardListener(subscription)

// Component cleanup pattern
const useClipboardListener = (callback: (event: ClipboardEvent) => void) => {
  useEffect(() => {
    const subscription = Clipboard.addClipboardListener(callback);
    
    return () => {
      // Either method works for cleanup
      Clipboard.removeClipboardListener(subscription);
      // or: subscription.remove();
    };
  }, [callback]);
};

Event Object Details

Content Types Array

The contentTypes array indicates what types of content are currently in the clipboard:

// Example event object
{
  content: "", // Always empty string (deprecated)
  contentTypes: [
    Clipboard.ContentType.PLAIN_TEXT, 
    Clipboard.ContentType.HTML
  ]
}

Content Type Detection

const handleClipboardChange = ({ contentTypes }: ClipboardEvent) => {
  // Check for specific content types
  const hasText = contentTypes.includes(Clipboard.ContentType.PLAIN_TEXT);
  const hasHtml = contentTypes.includes(Clipboard.ContentType.HTML);
  const hasImage = contentTypes.includes(Clipboard.ContentType.IMAGE);
  const hasUrl = contentTypes.includes(Clipboard.ContentType.URL); // iOS only
  
  // Multiple content types possible
  if (hasText && hasHtml) {
    console.log("Clipboard contains rich text with HTML formatting");
  }
  
  if (hasText && hasImage) {
    console.log("Clipboard contains both text and image");
  }
};

Platform Considerations

Native Platforms (iOS/Android)

  • Real-time monitoring: Events fire immediately when clipboard changes
  • System-wide detection: Detects changes from any app, not just your app
  • Content type detection: Provides accurate content type information
  • Performance: Minimal performance impact

Web Platform

  • No-op behavior: addClipboardListener and removeClipboardListener do nothing
  • No events: Clipboard change events are not supported in web browsers
  • Alternative approach: Use manual polling or user-triggered checks

Cross-Platform Patterns

import * as Clipboard from "expo-clipboard";
import { Platform } from 'react-native';

const useClipboardMonitoring = () => {
  const [clipboardState, setClipboardState] = useState({
    hasText: false,
    hasImage: false,
    lastUpdate: Date.now()
  });
  
  useEffect(() => {
    if (Platform.OS === 'web') {
      // Web: Use polling or manual checks
      const checkClipboard = async () => {
        const hasText = await Clipboard.hasStringAsync();
        const hasImage = await Clipboard.hasImageAsync();
        setClipboardState({
          hasText,
          hasImage, 
          lastUpdate: Date.now()
        });
      };
      
      // Manual check on component focus
      checkClipboard();
      return () => {}; // No cleanup needed
      
    } else {
      // Native: Use event listeners
      const subscription = Clipboard.addClipboardListener(async ({ contentTypes }) => {
        setClipboardState({
          hasText: contentTypes.includes(Clipboard.ContentType.PLAIN_TEXT),
          hasImage: contentTypes.includes(Clipboard.ContentType.IMAGE),
          lastUpdate: Date.now()
        });
      });
      
      return () => subscription.remove();
    }
  }, []);
  
  return clipboardState;
};

Error Handling

Event listener operations may throw UnavailabilityError if clipboard functionality is not available.

import * as Clipboard from "expo-clipboard";
import { UnavailabilityError } from 'expo-modules-core';

try {
  const subscription = Clipboard.addClipboardListener(() => {
    console.log("Clipboard changed");
  });
} catch (error) {
  if (error instanceof UnavailabilityError) {
    console.log("Clipboard event listeners not available");
  }
}