React Native Preview URL

<LinkPreview />

The drop-in component. Props, styling, accessibility, edge cases.

The LinkPreview component fetches metadata for a URL and renders a card with the title, description, image, and domain. It handles loading, errors, and tap-to-open out of the box.

Try it

Live · same component, every platform
iOSAndroidWeb
react-native-preview-urltap the card to fire onPress

URL

Hit Enter or tap Preview to fetch.

Quick picks

Props

titleLines2

Last event

callback log
Waiting for input. Tap the preview card to fire onPress.

Basic usage

import { LinkPreview } from 'react-native-preview-url';

<LinkPreview url="https://github.com" />;

When url is empty or not a valid http(s) URL the component renders nothing and reports the error via onError (if provided).

Props

The component takes 18 props. They group into four roles: identifying the target, hooking into lifecycle events, controlling layout, and styling.

Required

PropTypeDescription
urlstringThe URL to preview.

Behavior

PropTypeDefaultDescription
timeoutnumber3000Request timeout in ms. Clamped to [1000, 30000].
onSuccess(data: LinkPreviewResponse) => voidFires when metadata is fetched.
onError(error: string) => voidFires on any failure (invalid URL, network, server error, timeout).
onPress(data: LinkPreviewResponse) => voidOverride the default tap behavior. Without this prop, taps call Linking.openURL.
visiblebooleantrueWhen false, the resolved card is unmounted (the loader still shows during fetch).

Layout

PropTypeDefaultDescription
titleLinesnumber2numberOfLines for the title.
descriptionLinesnumber4numberOfLines for the description.
showUrlbooleantrueWhether to render the domain line below the description.
hideImagebooleanfalseSkip the image entirely. Useful in dense lists.
loaderComponentReact.ReactNodeskeletonElement shown while loading. Replaces the built-in shimmer.
fallbackImageImageSourcePropTypeImage used when the page has no image or the image fails to load.

Style overrides

PropTypeDescription
containerStyleViewStyleStyle merged onto the outer card.
imageStyleImageStyleStyle merged onto the image.
titleStyleTextStyleStyle merged onto the title.
descriptionStyleTextStyleStyle merged onto the description.
urlStyleTextStyleStyle merged onto the domain text.

Recipes

Real patterns for the common cases.

Custom press behavior

By default a tap calls Linking.openURL(data.url). Override it for analytics, in-app browsers, or routing:

<LinkPreview
  url="https://github.com"
  onPress={(data) => {
    track('link_preview_tap', { url: data.url, site: data.siteName });
    openInAppBrowser(data.url);
  }}
/>

Custom loader

Pass any element as loaderComponent to replace the built-in skeleton:

import { ActivityIndicator } from 'react-native';

<LinkPreview
  url="https://github.com"
  loaderComponent={<ActivityIndicator size="small" />}
/>;

The built-in loader is a shimmer skeleton sized to roughly match the resolved card. If you keep it, your layout won't jump when the data arrives.

Fallback image

Some pages have no Open Graph image, or return one that fails to decode on the client. Pass fallbackImage to keep the visual slot filled either way:

<LinkPreview
  url="https://example.com"
  fallbackImage={require('./assets/link-fallback.png')}
/>

If hideImage is true, the image slot is skipped entirely and fallbackImage is ignored.

Branded styling

The component is opinionated about layout (rounded card, 16:9 default image, shadow on iOS, elevation on Android, hairline border) but every visual is overridable.

<LinkPreview
  url="https://github.com"
  containerStyle={{
    margin: 16,
    borderRadius: 16,
    backgroundColor: '#fff',
    borderColor: '#e5e7eb',
  }}
  imageStyle={{ borderRadius: 12, height: 200 }}
  titleStyle={{ fontSize: 18, fontWeight: '700' }}
  descriptionStyle={{ fontSize: 14, color: '#4b5563' }}
  urlStyle={{ fontSize: 12, color: '#9ca3af' }}
/>

When the API returns image dimensions, the component uses the real aspect ratio instead of the 16:9 default. Pass imageStyle={{ height }} if you want a fixed height regardless of source dimensions.

Edge cases

Visibility

visible={false} unmounts the resolved card, but the loader still renders during the network fetch. If you want to gate the fetch itself, conditionally render the component:

{
  shouldShowPreview && <LinkPreview url={url} />;
}

Empty / invalid URL

When url is empty or fails the http(s) regex check, no network call is made — the component returns null and fires onError synchronously. This also means no cache entry is created for invalid input, so you can clear the URL state and retype without waiting out an error TTL.

Image failures

If the page's first image URL fails to decode, the component falls back to fallbackImage (if provided). If you want to retry the original, change the URL — the image error state resets every time url changes.

Accessibility

The card declares accessibilityRole="link", an accessibilityLabel built from the title and description, and an accessibilityHint that announces the target domain. The image slot gets its own descriptive label. The loader declares accessibilityRole="progressbar" with busy: true.

On this page