Skip to main content

Link menus

A long press on a link can do three things in EnrichedMarkdownText, and which one happens depends on what you set and where the text runs:

What the reader getsWhenWhere
A menu with your itemsThe link's URL matches a linkContextMenuItems pattern with at least one visible itemiOS 17 and later
Your onLinkLongPress handlerNo menu applies and the handler is setiOS, Android, macOS; contextmenu on web
The system link previewNeither of the above, and enableLinkPreview is oniOS

This page is about the first row. The other two are plain props and are covered in the reference.

Defining menus​

linkContextMenuItems is a map from a URL pattern to the items shown for links that match it. Patterns are regexes tested against the link URL and matched like markdownStyle.linkVariants: the longest pattern that matches supplies the menu, so one entry covers every link of a kind, and a pattern anchored to a single URL gives that one link its own menu.

import { EnrichedMarkdownText } from 'react-native-enriched-markdown';
import { View, Text, StyleSheet, useColorScheme } from 'react-native';
import { useState, useMemo } from 'react';
import { defaultMarkdownStyle } from './theme';

const markdown =
'Open [README.md](https://example.com/files/README.md) or ask [@gregory](user:gregory).';

export default function App() {
const isDark = useColorScheme() === 'dark';
const markdownStyle = useMemo(() => defaultMarkdownStyle(isDark), [isDark]);
const [status, setStatus] = useState('Long-press a link.');

return (
<View style={styles.container}>
<EnrichedMarkdownText
markdown={markdown}
markdownStyle={markdownStyle}
// One entry per kind of link. Keys are URL patterns, like `linkVariants`.
linkContextMenuItems={{
'^https://example\\.com/files/': [
{
text: 'Copy path',
icon: 'doc.on.doc',
onPress: ({ url }) => setStatus(`Copied ${url}`),
},
{
text: 'Delete',
icon: 'trash',
destructive: true,
onPress: ({ url }) => setStatus(`Deleted ${url}`),
},
],
'^user:': [
{
text: 'Mention',
icon: 'at',
onPress: ({ url }) => setStatus(`Mentioning ${url}`),
},
],
}}
// Android, macOS and iOS 16 ignore the menus and fall back to this.
onLinkLongPress={({ url }) => setStatus(`Long-pressed ${url}`)}
/>
<Text style={styles.status}>{status}</Text>
</View>
);
}

const styles = StyleSheet.create({
container: { gap: 12 },
status: { fontSize: 14, fontStyle: 'italic', color: '#8a90a6' },
});

Each item has:

  • text - the label. It identifies the item, so it must be unique within one pattern.
  • onPress({ url }) - called with the link's original URL, including relative paths. The library does not navigate, copy or share for you; do that here.
  • icon - an SF Symbol name, as in contextMenuItems.
  • visible, disabled, destructive - optional, all default to the obvious value. A hidden or disabled item is still part of the pattern's list, so toggling it does not change which pattern wins.

The menu holds only your items. Its title is the link's text, or the pill's label when the link is a link pill.

Fallbacks​

A link that matches no pattern, or whose pattern has no visible items, keeps its ordinary long-press behavior: onLinkLongPress if set, otherwise the system link preview on iOS.

Android, macOS and iOS below 17 ignore the prop entirely. Keep an onLinkLongPress handler next to your menus for those - as the example does - so the gesture still does something there.

Changing menus while one is open​

The prop can change while a menu is on screen, for example when a list of files refreshes. A press on an item that the current value no longer offers - removed, hidden or disabled - is ignored rather than acting on stale data.

Relation to the other menus​

linkContextMenuItems only touches the long press on a link. The rest of the menu surface is unchanged:

Menus work in CommonMark text and in GitHub-flavor text segments, including blockquotes and table cells.

Reference​