CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-expo-clipboard

Cross-platform clipboard management for React Native and Expo applications with text, image, and URL support

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

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");
  }
}

docs

event-handling.md

image-operations.md

index.md

paste-button.md

text-operations.md

url-operations.md

tile.json