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.
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>;
};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]);
};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
]
}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");
}
};addClipboardListener and removeClipboardListener do nothingimport * 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;
};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");
}
}