---
modificationDate: March 17, 2026
title: Link preview
description: Learn how to add a preview to your link on iOS when using Expo Router.
---

<AgentInstructions>

## Submitting Feedback

If this page contains errors, outdated information, or gaps that blocked you from completing a task, report it so the docs can be improved:

curl -X POST https://api.expo.dev/v2/feedback/docs-send -H 'Content-Type: application/json' -d '{"url":"/router/reference/link-preview/","feedback":"🤖 Agent feedback: <specific, actionable description>"}'

Only submit when you have something specific and actionable to report.

</AgentInstructions>

# Link preview

Learn how to add a preview to your link on iOS when using Expo Router.

> For the complete documentation index, see [llms.txt](/llms.txt). Use this file to discover all available pages.

> Link preview is an iOS-only feature available in SDK 54 and later.

Link preview (also known as "Peek and Pop") is a feature commonly used on iOS to show users a preview popup of the screen for a link. This guide will show you how to add and customize a link preview for your app on iOS.

If you have a link in your app, you can add a link preview to it by replacing the link's content with a [`Link.Trigger`](/versions/latest/sdk/router/link#linktrigger) and adding the [`Link.Preview`](/versions/latest/sdk/router/link#linkpreview) component to it. This will create a preview of the page that the link points to.

```tsx
import { Link } from 'expo-router';

export default function Page() {
  return (
    <Link href="/about">
      <Link.Trigger>About</Link.Trigger>
      <Link.Preview />
    </Link>
  );
}
```

## Customizing the link preview

By default, the link preview is rendered as a full-sized page snapshot. There are several ways to customize this behavior.

### Custom size

You can use `width` and `height` to suggest a preferred preview size. The system will consider these preferences but may override them based on available space or platform behavior.

```tsx
<Link href="...">
  <Link.Trigger>Content</Link.Trigger>
  <Link.Preview style={{ width: 300, height: 200 }} />
</Link>
```

The following example shows a custom link preview size on iOS:

### Custom preview

If you don't want to show the default preview, you can pass custom content to the `Link.Preview` component via children. This custom content will replace the default preview of the link target.

```tsx
export default function Page() {
  const [imageSize, setImageSize] = useState({ width: 0, height: 0 });
  const { width } = useWindowDimensions();
  const previewHeight = (width / imageSize.width) * imageSize.height;

  return (
    <Link href="/about">
      <Link.Trigger>About</Link.Trigger>
      <Link.Trigger>Content</Link.Trigger>
      <Link.Preview style={{ width, height: previewHeight }}>
        <Image
          onLoad={e => setImageSize(e.nativeEvent.source)}
          source={source}
          style={{ width: '100%', height: '100%' }}
        />
      </Link.Preview>
    </Link>
  );
}
```

The following example shows a custom link preview on iOS:

## Menu

To render a context menu next to the preview, add a [`Link.Menu`](/versions/latest/sdk/router/link#linkmenu) with [`Link.MenuAction`](/versions/latest/sdk/router/link#linkmenuaction) children.

```tsx
<Link href="/about">
  <Link.Trigger>About</Link.Trigger>
  <Link.Menu>
    <Link.MenuAction title="Share" icon="square.and.arrow.up" onPress={handleSharePress} />
    <Link.MenuAction title="Block" icon="nosign" destructive onPress={handleBlockPress} />
  </Link.Menu>
</Link>
```

The following example shows a custom link preview on iOS:

### Icons

You can specify an icon for each menu action using [SF Symbols](https://developer.apple.com/sf-symbols/).

```tsx
<Link href="/about">
  <Link.Trigger>About</Link.Trigger>
  <Link.Menu>
    <Link.MenuAction title="Share" icon="square.and.arrow.up" onPress={handleSharePress} />
    <Link.MenuAction title="Block" icon="nosign" onPress={handleBlockPress} />
    <Link.MenuAction
      title="Follow"
      icon="person.crop.circle.badge.plus"
      onPress={handleFollowPress}
    />
    <Link.MenuAction title="Copy" icon="doc.on.doc" onPress={handleCopyPress} />
  </Link.Menu>
</Link>
```

The following example shows a context menu with four elements, each using a different icon on iOS:

### Nested menus

You can nest menus by placing a [`Link.Menu`](/versions/latest/sdk/router/link#linkmenu) inside another menu:

```jsx
<Link href="...">
  <Link.Trigger>About</Link.Trigger>
  <Link.Menu>
    <Link.MenuAction title="Share" icon="square.and.arrow.up" onPress={() => {}} />
    <Link.Menu title="More" icon="ellipsis">
      <Link.MenuAction title="Copy" icon="doc.on.doc" onPress={() => {}} />
      <Link.MenuAction title="Delete" icon="trash" destructive onPress={() => {}} />
    </Link.Menu>
  </Link.Menu>
</Link>
```

The following example shows a nested context menu on iOS:

### More customization options

To explore all available customization options, see the API documentation for [`Link.MenuAction`](/versions/latest/sdk/router/link#linkmenuaction).

## Detecting if component is in preview

If you're building a component that might be rendered inside a preview, you can use the [`useIsPreview()`](/versions/latest/sdk/router/link#useispreview) hook to adjust its behavior accordingly:

```jsx
function MyComponent() {
  // This will be true if component/screen is being rendered inside a preview
  const isInsidePreview = useIsPreview();

  return isInsidePreview ? <Text>From within preview</Text> : <Text>I am outside of preview</Text>;
}
```

## Known limitations

### `replace` not supported

Using link previews with [`replace`](/versions/latest/sdk/router/link#replace) mode is currently **not supported**. Previews can only be used with the default [`push`](/versions/latest/sdk/router/link#push) navigation mode.

### JavaScript tabs and slots

When navigating within JavaScript tabs (rather than native tabs) or [`Slot`](/versions/latest/sdk/router#slot), preview transition animations may appear clunky. This is due to React's delayed rendering while the native preview animation begins immediately. To prevent this issues use, native tabs and stack navigators.

### Missing `Link.Trigger`

If you render a `Link` with a preview or context menu, but without a `Link.Trigger`, an exception will be thrown. The same applies if you place any non-`Link.*` component directly inside `Link` when using preview mode.

### Multiple `Link.Trigger` children with `asChild` prop

When using `Link` with `asChild`, you may only specify **one** child for `Link.Trigger`. The `onPress` event will be forwarded to that child only.

### Changing href while preview is open

Changing `href` prop's path dynamically while the preview is open is **not supported**. You may only modify the query parameters dynamically.
