alouette
alouette-external-links
Send the user to a URL outside the app. ExternalLink is the primitive over expo-web-browser and Linking; ExternalLinkButton is the call-to-action shaped call site and ExternalLinkText the inline one inside a sentence. How the link opens is decided per platform — an in-app browser or the system one on native, a new tab or the same one on web — and the two wrappers already pick the usual answer. Load when linking out to external URLs from alouette UI.
This skill builds on alouette-actions. Read it first for button props.
alouette + expo-web-browser — External links
ExternalLink centralizes outbound-URL behavior per platform: an in-app browser sheet (expo-web-browser) or Linking on native, and a new tab or same tab on web. Two components apply it for you — ExternalLinkButton for a call to action, ExternalLinkText for a link inside a text flow.
ExternalOpenLinkBehavior (exported from alouette): { native: "linking" | "webBrowser"; web: "targetBlank" | "targetSelf" }. Both wrappers default it to { native: "webBrowser", web: "targetBlank" }.
Setup
import { ExternalLinkButton, ExternalLinkText } from "alouette";
<ExternalLinkButton href="https://example.com" text="Open docs" />
<ExternalLinkText href="https://example.com" text="the documentation" />;Core Patterns
Inline link vs link button
ExternalLinkButton takes every Button prop (variant, size, accent, icon, state, …) and reads as an action. ExternalLinkText is the lightweight one: bold underlined text preceded by an affordance icon (the external-link arrow, overridable via icon, dropped with icon={null}), size "sm" | "md", an accent, and a disabled state — on web the text and icon tint through hover/active from the interactive tokens, and the row carries the focus-visible outline.
<Paragraph>
Read <ExternalLinkText size="sm" href={docsUrl} text="the guide" /> first.
</Paragraph>Inside a Text or Paragraph it renders as a nested Text, so it wraps with the sentence and keeps its baseline on native as on web.
A disabled link drops its href on both platforms, because a disabled Pressable never sees the press that would cancel the navigation. On web both wrappers go through ExternalLink, which renders a real anchor with target="_blank" + rel="noopener noreferrer" (unless web: "targetSelf"), so middle-click and copy-link work.
Wrap any component with explicit per-platform behavior
ExternalLink takes the target component via as and forwards remaining props.
import { ExternalLink, Button } from "alouette";
<ExternalLink
as={Button}
href="https://example.com"
openLinkBehavior={{ native: "webBrowser", web: "targetBlank" }}
text="Open"
/>;- native: "webBrowser" opens an in-app browser sheet themed with alouette colors.
- native: "linking" hands off to the OS browser via Linking.openURL.
- web: "targetBlank" opens a new tab; "targetSelf" navigates in place.
Common Mistakes
MEDIUM Calling Linking.openURL directly
Wrong:
import { Linking } from "react-native";
<Pressable onPress={() => Linking.openURL(href)} />;Correct:
<ExternalLink
as={Button}
href={href}
openLinkBehavior={{ native: "webBrowser", web: "targetBlank" }}
text="Open"
/>ExternalLink handles the in-app themed browser sheet on native and the correct target behavior on web; calling Linking directly loses both.
Source: packages/alouette/src/expo/ExternalLink.tsx
MEDIUM Omitting openLinkBehavior
Wrong:
<ExternalLink as={Button} href={href} text="Open" />Correct:
<ExternalLink
as={Button}
href={href}
openLinkBehavior={{ native: "linking", web: "targetSelf" }}
text="Open"
/>openLinkBehavior is required on ExternalLink itself and selects the native and web strategies; without it the link can't decide how to open and throws on an unsupported branch. (ExternalLinkButton and ExternalLinkText default it for you — use them for the simple case.)
Source: packages/alouette/src/expo/ExternalLink.tsx; expo/ExternalLink.shared.ts
MEDIUM A pressable Text instead of ExternalLinkText
Wrong:
<Pressable onPress={() => openUrl(href)}>
<Text className="underline text-accent">the documentation</Text>
</Pressable>Correct:
<ExternalLinkText href={href} text="the documentation" />The hand-rolled version has no role="link", no affordance icon, no hover/active/focus tint from the interactive tokens, and on web no real anchor — so the link cannot be middle-clicked or copied.
Source: packages/alouette/src/ui/actions/ExternalLinkText.tsx