Actions

ShareButton

A button that shares a link: through the system's share sheet where there is one (phones, Safari), and by copying it where there is not, when it says "Link copied" for two seconds and screen readers hear it once. It shares the page's own address unless given url.

Trail notes

In Spoonjoy

Spoonjoy, a recipe app, is built with Ouro HX (how).

See it live, no sign-in needed: A recipe

In its source: app/routes/cookbooks.$id.tsx · app/routes/recipes.$id.tsx

Install

Once per app, with Tailwind CSS v4 and an @/* path alias (Ouro HX is built on Base UI, so --base base; skip -t in an existing app):

npx shadcn@latest init --base base --preset nova --yes --no-monorepo
npx shadcn@latest add ourostack/ouro-ui/ouro-base#v1.2.13

Then the component. From v0.8.0 it installs only the helpers it imports and never touches your stylesheet (Installing):

npx shadcn@latest add ourostack/ouro-ui/share-button#v1.2.13

Or, with the @ouro namespace set up:

npx shadcn@latest add @ouro/ouro-base @ouro/share-button

Its tests, stories and Playwright spec are optional. They need Vitest 4.1.11 or later, and from v0.8.0 never change a version your app has (Testing):

npx shadcn@latest add ourostack/ouro-ui/share-button#v1.2.13 ourostack/ouro-ui/share-button-tests#v1.2.13
import { ShareButton } from "@/components/ouro/share-button/share-button";

Paths use shadcn's default @/ alias; the CLI rewrites them to yours.

Example

<ShareButton url={`https://example.com/notes/${note.id}`} title={note.title} />

Use instead when

  • Use CopyButton instead to copy any text without a share sheet, and the share() helper for a share action inside a menu.

Props

Type: ShareButtonProps.

className
Type: string
Layout only: margins, grid placement (ms-auto). Appearance classes warn in development.
copiedLabel
Type: string
Default: "Link copied"
What it says, and screen readers hear, when there was no share sheet and the link was copied instead.
failedLabel
Type: string
Default: "Couldn't share"
What it says, and screen readers hear, when neither sharing nor copying worked.
hideLabel
Type: boolean
Shows only the icon; the label stays the button's name for screen readers.
label
Type: string
Default: "Share"
The button's label, and its name for screen readers with hideLabel.
onShare
Type: ((result: ShareResult) => void)
Called with how the share ended: shared, copied, cancelled or failed.
ref
Type: Ref<HTMLButtonElement>
Reaches the rendered element.
size
Type: "sm" | "md" | "lg"
Default: "md"
Control size, as Button's.
text
Type: string
A line about the link, where the receiving app takes one.
title
Type: string
A title for the link, where the receiving app takes one.
url
Type: string
Default: the page's own address
The address to share.
variant
Type: "solid" | "soft" | "outline" | "plain"
Default: "outline"
Visual weight, as Button's.

Native attributes

Also accepts the attributes in React's ButtonHTMLAttributes and HTMLAttributes (id, aria-*, event handlers), passed to the rendered element, except those named on this page.

Don't use

These are not props. Passing one fails the type check with the fix in the error, and in development a console message names the fix.

  • children: Not a prop. The label is label: <ShareButton label="Share recipe" />.
  • href: Not a prop. The address is url: <ShareButton url={link} />.
  • onClick: Not a prop. ShareButton shares on click itself; to react to it, pass onShare.

className is for layout only

Pass margins, width and grid placement (mt-4, w-full, col-span-2). Your classes always win over Ouro UI's: ShareButton merges className last with cn (tailwind-merge), so a class that conflicts with its own replaces it.layout-classes

Appearance comes from props, not classes. In development, a class that sets appearance logs one warning per component instance, naming the prop to use instead. A class sets appearance when, after its variants (hover:, sm:) and any ! are removed, it starts with bg-, text-, border-, rounded-, shadow-, font-, ring-, from-, via-, to-, fill- or stroke-, or is border, rounded, shadow or ring, or has a dark: variant. Text wrapping, overflow and table layout classes (text-wrap, text-ellipsis, border-collapse) are layout and do not warn.

Alignment classes (text-center) warn too: set alignment on a parent element.

canShare()

Whether the browser has a share sheet for this data: most phones and Safari do, most desktop browsers do not.

import { canShare } from "@/lib/ouro/share";

Example

const label = canShare({ url }) ? "Share" : "Copy link";

Parameters

data
Type: ShareContent

Returns

boolean

copyText()

Puts text on the clipboard: through the Clipboard API, or a text selection where the page may not use it (plain http, an older browser). Resolves whether it worked; never throws.

import { copyText } from "@/lib/ouro/share";

Example

if (await copyText(url)) toast("Link copied");

Parameters

text
Type: string

Returns

Promise<boolean>

share()

Shares a link through the system's share sheet, or copies it where there is none, and says which happened. Call it from a click: browsers open the share sheet only for one. It never throws.

import { share } from "@/lib/ouro/share";

Example

const result = await share({ url: location.href, title: document.title });
if (result === "copied") toast("Link copied");

Use instead when

  • Use ShareButton instead for the whole button, with its "Link copied" confirmation.

Parameters

data
Type: ShareContent

Returns

Promise<ShareResult>

Claim sources