@share-kit/svelte
v0.1.1
Published
A primitive, headless-friendly share component for Svelte 5 — clipboard copy, native Web Share, file download, and custom providers, with a zero-dependency built-in icon set.
Maintainers
Readme
@share-kit/svelte
A primitive, headless-friendly share component for Svelte 5 + daisyUI.
Provides clipboard copy, native Web Share API, file download, and custom provider support through a flexible multi-mode UI. No icon library is required — the package ships its own small, zero-dependency icon set, fully overridable per provider.
Install
npm install @share-kit/svelte
# peer dependency
npm install svelte@^5Quick start
<script>
import { Share } from '@share-kit/svelte';
</script>
<Share data={{ url: 'https://example.com', title: 'My page' }} />Display modes
| Mode | Behaviour |
| -------- | ------------------------------------------------------------------------ |
| menu | Button + floating dropdown (default). Promotes to sheet on mobile. |
| button | Single button — executes the first available provider directly. |
| inline | Always-visible provider list, no trigger button. |
| sheet | Mobile bottom-sheet triggered by a button. |
<Share data={…} mode="menu" />
<Share data={…} mode="button" />
<Share data={…} mode="inline" />
<Share data={…} mode="sheet" />ShareData
interface ShareData {
title?: string; // Page/resource title
text?: string; // Description or body text
url?: string; // URL to copy or share
files?: File[]; // Files for native share
downloadUrl?: string; // Pre-built download URL (blob: or https:)
downloadFilename?: string; // Suggested download filename
}Built-in providers
| ID | Kind | Behaviour |
| ---------- | ---------- | ---------------------------------------- |
| copy | copy | Copies url (or text) to clipboard |
| native | native | Opens the device's Web Share API sheet |
| download | download | Downloads downloadUrl or files[0] |
| open | open | Opens url in a new tab |
Availability is detected automatically:
native— only shown whennavigator.shareis availabledownload— only shown whendownloadUrlorfilesare provided
Custom providers
import { buildDefaultProviders, ShareController } from '@share-kit/svelte';
const providers = [
...buildDefaultProviders(data),
{
id: 'slack',
kind: 'custom',
label: 'Slack',
icon: SlackIcon, // any icon library or custom component
tooltip: 'Send to Slack',
available: true,
action: async (data, ctrl) => {
await postToSlack(data.url);
return 'success'; // or 'error'
}
}
];
const ctrl = new ShareController({ data, providers });External controller
Share state with a sibling component (e.g. a trigger button that lives
outside the <Share> tree) by creating and owning the controller yourself:
<script>
import { Share, ShareController } from '@share-kit/svelte';
const ctrl = new ShareController({
data: { url: '…', title: '…' },
onCopy: (e) => console.log('Copied!', e),
onError: (e) => console.error('Failed', e)
});
</script>
<!-- Trigger from anywhere in the page -->
<button onclick={() => ctrl.toggle()}>Share</button>
<!-- The share UI (menu/sheet/inline) -->
<Share controller={ctrl} />A controller you create yourself is yours to keep alive across remounts —
<Share> only disposes controllers it created internally.
Events
All callbacks receive a ShareEvent object:
interface ShareEvent {
provider: ShareProvider; // which provider ran
data: ShareData; // the data that was shared
result: ShareResult; // 'success' | 'error' | 'dismissed' | 'unsupported'
error?: unknown; // populated when result='error'
}| Callback | Fires when |
| ------------------- | -------------------------------------------- |
| onShare | Any share succeeds |
| onCopy | Clipboard copy succeeds |
| onDownload | File download starts |
| onProviderSelect | User clicks a provider (before action runs) |
| onError | Any share operation fails |
Snippet overrides
<Share data={…}>
<!-- Replace the trigger button -->
{#snippet trigger(ctrl)}
<button onclick={() => ctrl.toggle()}>My custom button</button>
{/snippet}
<!-- Replace the floating content entirely -->
{#snippet children(ctrl)}
<div class="my-custom-menu">
<!-- your custom share UI -->
</div>
{/snippet}
</Share>Icons
This package has no icon library dependency. Built-in providers use a small bundled SVG icon set, exported for reuse:
import { LinkIcon, ShareIcon, DownloadIcon, ExternalLinkIcon, SparkIcon, CheckIcon, XIcon, LoaderIcon } from '@share-kit/svelte';Override any provider's icon with a component from any icon library (or your
own) via ShareProvider.icon — see Custom providers.
Props reference
<Share>
| Prop | Type | Default | Description |
| ------------------------ | ----------------- | -------- | -------------------------------- |
| controller | ShareController | — | External controller (optional) |
| data | ShareData | {} | Content to share |
| providers | ShareProvider[] | auto | Override provider list |
| mode | ShareMode | 'menu' | Display mode |
| autoDetectNativeShare | boolean | true | Auto-enable native share |
| responsive | boolean | true | Promote menu→sheet on mobile |
| showLabels | boolean | true | Show text labels on icons |
| showTooltips | boolean | true | Show tooltips on hover |
Also exported as standalone primitives for custom compositions:
ShareTrigger, ShareOption, ShareMenu, ShareSheet, ShareInline.
Accessibility, dark mode, RTL
- Menu/sheet/inline lists use
role="menu"/role="dialog"witharia-label,aria-modal,aria-expanded, andaria-haspopupas appropriate; the sheet moves focus into itself on open. - All colors come from daisyUI theme tokens (
bg-base-200,text-success, etc.), so the component follows whatever daisyUI theme (including dark themes) is active on the page — no separate dark-mode prop. - Layout uses logical, direction-agnostic Tailwind utilities, so the
component follows the page's
dir="rtl"automatically.
Architecture
src/
├── constants.ts — timing, z-index, layout constants
├── controller.svelte.ts — ShareController ($state runes, no DOM)
├── elements/
│ ├── Share.svelte — shell: mode routing, responsive, keyboard
│ ├── ShareTrigger.svelte — the share button (state-reflective)
│ ├── ShareOption.svelte — single provider button (icon + label)
│ ├── ShareMenu.svelte — floating dropdown
│ ├── ShareSheet.svelte — mobile bottom-sheet
│ ├── ShareInline.svelte — always-visible list
│ ├── provider-icons.ts — maps provider id/kind → built-in icon
│ └── icons/ — zero-dependency built-in icon set
├── utils/
│ ├── share.ts — clipboard, native share, download, provider factory
│ └── index.ts — barrel
├── types/
│ ├── enums.ts — shareMode, providerKind, shareResult, shareState
│ ├── types.d.ts — all interfaces
│ └── index.ts — barrel
├── docs/Share.md — extended internal reference
└── index.ts — public APIBrowser compatibility
| Feature | Support | | ------------------------ | --------------------------------------------------------- | | Clipboard API | Chrome 66+, Firefox 63+, Safari 13.1+ | | Web Share API | Chrome 89+, Safari 12.1+ (mobile), Firefox 71+ (Android) | | Popover API (tooltips) | Chrome 114+, Firefox 125+, Safari 17+ | | File download | All modern browsers |
The component degrades gracefully: unavailable providers are shown
greyed-out, and the Clipboard API falls back to execCommand('copy') on
older browsers.
Example
A runnable browser demo lives in examples/svelte
in the monorepo (not published with this package). See its own README for
how to run it.
License
MIT
