npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

svelte-share-lite

v1.0.0

Published

Social media share buttons for Svelte and SvelteKit applications.

Readme

svelte-share-lite

Social media share buttons for Svelte 5 and SvelteKit.

21 networks, zero runtime dependencies, SSR-safe, fully typed.

npm version npm downloads Svelte 5 license


svelte-share-lite gives you one small, idiomatic component per social network. Each renders a real <button> with an inline SVG icon and opens the network's share dialog in a centred popup. No iframes, no third-party scripts, no tracking pixels, nothing loaded from the network's servers.

It is the Svelte port of react-share-lite, rebuilt from scratch on Svelte 5 runes — snippets instead of children, bind:element instead of refs, and plain CSS strings instead of style objects.

Table of contents

Features

  • 21 networks — X, Bluesky, Threads, Facebook, LinkedIn, WhatsApp, Telegram, Reddit, Pinterest, Tumblr, LINE, Viber, VK, Weibo, Messenger, Email, Mail.Ru, LiveJournal, Instapaper, Hatena, Gab.
  • Svelte 5 native — built with runes. Props are plain props, content is a snippet, and the button element is exposed through bind:element.
  • Zero runtime dependencies — icons are inline SVG paths compiled into the bundle.
  • SSR-safe — renders on the server without touching window. Works in SvelteKit out of the box.
  • Tree-shakeable — "sideEffects": false, so importing one button ships one button.
  • Fully typed — every component and every option type is exported.
  • Styleable — class, style, data-*, aria-* and every other button attribute is forwarded to the real <button>.

Requirements

| | | | :----- | :-------------------------------------------------------------------- | | Svelte | 5.0 or newer | | Node | 18 or newer (for the build tooling; the library itself runs anywhere) |

Svelte 4 is not supported — the components are written with runes.

Installation

npm install svelte-share-lite
pnpm add svelte-share-lite
yarn add svelte-share-lite
bun add svelte-share-lite

Quick start

<script>
  import { XShare, FacebookShare, WhatsappShare } from 'svelte-share-lite'

  const url = 'https://github.com/ahmadjoya/svelte-share-lite'
  const title = 'Social media share buttons for Svelte'
</script>

<XShare {url} {title} />
<FacebookShare {url} quote={title} />
<WhatsappShare {url} {title} />

That is the whole API: pass the url you want shared, plus whatever extra fields that particular network understands.

Sharing the current page in SvelteKit

<script>
  import { page } from '$app/state'
  import { XShare, LinkedinShare } from 'svelte-share-lite'
</script>

<XShare url={page.url.href} title={page.data.title} />
<LinkedinShare url={page.url.href} />

On SvelteKit older than 2.12, use import { page } from '$app/stores' and $page.url.href.

Supported networks

| Component | Network | Opens | | :----------------------- | :------------------------------ | :------------------------------------ | | XShare | X (formerly Twitter) | x.com/intent/post | | BlueskyShare | Bluesky | bsky.app/intent/compose | | ThreadsShare | Threads | threads.net/intent/post | | FacebookShare | Facebook | facebook.com/sharer/sharer.php | | FacebookMessengerShare | Facebook Messenger | facebook.com/dialog/send | | LinkedinShare | LinkedIn | linkedin.com/sharing/share-offsite/ | | WhatsappShare | WhatsApp | api.whatsapp.com/send | | TelegramShare | Telegram | t.me/share/url | | RedditShare | Reddit | reddit.com/submit | | PinterestShare | Pinterest | pinterest.com/pin/create/button/ | | TumblrShare | Tumblr | tumblr.com/widgets/share/tool | | LineShare | LINE | social-plugins.line.me/lineit/share | | ViberShare | Viber | viber://forward | | VKShare | VK | vk.com/share.php | | WeiboShare | Weibo | service.weibo.com/share/share.php | | MailruShare | Mail.Ru | connect.mail.ru/share | | LiveJournalShare | LiveJournal | livejournal.com/update.bml | | InstapaperShare | Instapaper | instapaper.com/hello2 | | HatenaShare | Hatena Bookmark | b.hatena.ne.jp/add | | GabShare | Gab | gab.com/compose | | EmailShare | Email | mailto: | | TwitterShare | ⚠️ deprecated alias of XShare | x.com/intent/post |

Props

Common props

Every button accepts these. On top of them, each network adds its own — see Network reference.

| Prop | Type | Default | Description | | :----------------------- | :------------------------------------------ | :---------------- | :------------------------------------------------------------------------------ | | url | string | — | Required. The URL to share. | | size | number | 64 | Icon width and height, in pixels. | | round | boolean | false | Render a circular icon instead of a square one. | | borderRadius | number | 0 | Corner radius of the square icon, in pixels. Ignored when round. | | bgColor | string | brand colour | Icon background colour. | | iconFillColor | string | '#ffffff' | Colour of the glyph inside the icon. | | iconStyle | string | — | Inline CSS applied to the <svg>. | | buttonTitle | string | — | Render this text instead of the icon. | | children | Snippet | — | Render custom markup instead of the icon. | | class | string | — | Forwarded to the <button>. See Styling. | | style | string | — | Inline CSS applied to the <button>, after the reset style. | | resetButtonStyle | boolean | true | Strip the browser's default button chrome. | | disabled | boolean | false | Disable the button and apply disabledStyle. | | disabledStyle | string | 'opacity: 0.6;' | Inline CSS applied while disabled. | | openShareDialogOnClick | boolean | true | Set to false to handle the click yourself. | | blankTarget | boolean | false | Open a new tab (with noopener,noreferrer) instead of a popup. | | windowWidth | number | per network | Popup width, in pixels. | | windowHeight | number | per network | Popup height, in pixels. | | windowPosition | 'windowCenter' \| 'screenCenter' | 'windowCenter' | Centre the popup on the browser window or on the screen. | | beforeOnClick | () => Promise<void> \| void | — | Runs before the dialog opens. If it returns a promise, the dialog waits for it. | | onClick | (event: MouseEvent, link: string) => void | — | Runs after the dialog opens, with the generated share link. | | onShareWindowClose | () => void | — | Runs once the popup is closed. Not called when blankTarget is true. | | element | HTMLButtonElement \| null | null | bind:element to reach the underlying <button>. | | onclick | (event: MouseEvent) => void | — | Native click handler. Called alongside onClick. |

Any other <button> attribute (id, type, data-*, aria-*, onfocus, …) is forwarded as-is.

style is a string, not an object. This is the main difference from react-share-lite: Svelte's style attribute takes CSS text, so style="margin: 4px" — not style={{ margin: 4 }}. The same goes for iconStyle and disabledStyle.

title is network-specific. For networks that use a title in the shared text (X, WhatsApp, Telegram, …) title is consumed as a share option. For networks that don't (Facebook, Pinterest, Messenger, …) it falls through to the <button> and becomes a native tooltip.

Popup defaults per network

| Network | Popup | Position | | :---------------------------------------------------------------------- | :--------- | :------------------------------------------------ | | X · Twitter · Facebook · Telegram · WhatsApp | 550 × 400 | window centre | | Threads | 550 × 600 | window centre | | Bluesky · Tumblr · Reddit · VK · Viber · Mail.Ru · LiveJournal · Hatena | 660 × 460 | window centre | | Weibo | 660 × 550 | screen centre | | Gab | 660 × 640 | window centre | | LinkedIn | 750 × 600 | window centre | | LINE · Instapaper | 500 × 500 | window centre | | Pinterest | 1000 × 730 | window centre | | Facebook Messenger | 1000 × 820 | window centre | | Email | — | navigates to mailto: instead of opening a popup |

Styling

Icon appearance

<!-- Square, 64px, brand colour — the default -->
<FacebookShare {url} />

<!-- Circular, smaller -->
<FacebookShare {url} round size={40} />

<!-- Rounded corners -->
<FacebookShare {url} size={48} borderRadius={12} />

<!-- Monochrome: black tile, white glyph -->
<FacebookShare {url} bgColor="#111827" iconFillColor="#ffffff" />

<!-- Outline look: transparent tile, coloured glyph -->
<FacebookShare {url} bgColor="transparent" iconFillColor="#3b5998" />

Styling the button with CSS

class is forwarded to the <button>, but that button lives inside svelte-share-lite, not inside your component — so Svelte's scoped CSS will not match it. Wrap the rule in :global():

<script>
  import { FacebookShare } from 'svelte-share-lite'
</script>

<FacebookShare url="https://example.com" class="share-button" size={40} round />

<style>
  :global(.share-button) {
    transition: transform 150ms ease;
  }

  :global(.share-button:hover) {
    transform: translateY(-2px);
  }

  :global(.share-button:focus-visible) {
    outline: 2px solid #ff3e00;
    outline-offset: 3px;
  }
</style>

If you prefer not to reach for :global(), put the buttons inside a wrapper you own and style by descendant selector, or use a global stylesheet (app.css in SvelteKit).

For one-off tweaks, style is simpler:

<FacebookShare {url} style="margin-right: 8px;" />

Custom content instead of the icon

Pass a string with buttonTitle:

<FacebookShare {url} buttonTitle="Share on Facebook" />

…or any markup at all as the component's content, which becomes the children snippet:

<script>
  import { XShare } from 'svelte-share-lite'
</script>

<XShare url="https://example.com" title="Look at this" class="pill">
  <svg width="16" height="16" viewBox="0 0 64 64" aria-hidden="true">
    <path
      d="M 41.116 18.375 h 4.962 l -10.8405 12.39 l 12.753 16.86 H 38.005 l -7.821 -10.2255 L 21.235 47.625 H 16.27 l 11.595 -13.2525 L 15.631 18.375 H 25.87 l 7.0695 9.3465 z m -1.7415 26.28 h 2.7495 L 24.376 21.189 H 21.4255 z"
      fill="currentColor"
    />
  </svg>
  <span>Post</span>
</XShare>

<style>
  :global(.pill) {
    display: inline-flex;
    gap: 6px;
    align-items: center;
    padding: 8px 14px;
    border-radius: 999px;
    background: #000;
    color: #fff;
    font-size: 14px;
  }
</style>

buttonTitle wins over children, and children wins over the icon.

Recipes

A reusable share bar

<!-- src/lib/ShareBar.svelte -->
<script lang="ts">
  import {
    BlueskyShare,
    EmailShare,
    FacebookShare,
    LinkedinShare,
    WhatsappShare,
    XShare,
  } from 'svelte-share-lite'

  let {
    url,
    title = '',
    size = 36,
  }: { url: string; title?: string; size?: number } = $props()
</script>

<div class="share-bar">
  <XShare {url} {title} {size} round />
  <BlueskyShare {url} {title} {size} round />
  <FacebookShare {url} quote={title} {size} round />
  <LinkedinShare {url} {title} {size} round />
  <WhatsappShare {url} {title} {size} round />
  <EmailShare {url} subject={title} {size} round />
</div>

<style>
  .share-bar {
    display: flex;
    gap: 8px;
    align-items: center;
  }
</style>

Track shares with analytics

onClick receives the click event and the exact link that was opened:

<script>
  import { XShare } from 'svelte-share-lite'

  function trackShare(event, link) {
    analytics.track('share', { network: 'x', link })
  }
</script>

<XShare url="https://example.com" title="Hello" onClick={trackShare} />

Gate sharing behind a condition

beforeOnClick runs before the dialog opens; if it returns a promise, the dialog waits for it. Use it for work that must finish first — saving a draft, minting a short link, logging an intent:

<script>
  import { LinkedinShare } from 'svelte-share-lite'

  async function saveDraft() {
    await fetch('/api/draft', { method: 'POST' })
  }
</script>

<LinkedinShare url="https://example.com" beforeOnClick={saveDraft} />

beforeOnClick cannot cancel the share. For a hard stop, drive openShareDialogOnClick from state:

<script>
  import { RedditShare } from 'svelte-share-lite'

  let accepted = $state(false)
</script>

<label>
  <input type="checkbox" bind:checked={accepted} />
  I agree to the sharing terms
</label>

<RedditShare
  url="https://example.com"
  title="Hello"
  disabled={!accepted}
  openShareDialogOnClick={accepted}
/>

Keep beforeOnClick quick. Browsers only allow window.open shortly after a user gesture, so a slow await can get the popup blocked.

Do something after the popup closes

<script>
  import { FacebookShare } from 'svelte-share-lite'

  let shared = $state(false)
</script>

<FacebookShare
  url="https://example.com"
  onShareWindowClose={() => (shared = true)}
/>

{#if shared}
  <p>Thanks for sharing! 🎉</p>
{/if}

onShareWindowClose polls the popup once a second, so it cannot be used together with blankTarget — a tab opened with noopener is not observable.

Reach the underlying button element

<script>
  import { TelegramShare } from 'svelte-share-lite'

  let element = $state(null)

  $effect(() => {
    element?.focus()
  })
</script>

<TelegramShare url="https://example.com" bind:element />

Network reference

Every example below also accepts all of the common props.

X

<script>
  import { XShare } from 'svelte-share-lite'
</script>

<XShare
  url="https://github.com/ahmadjoya/svelte-share-lite"
  title="Social media share buttons for Svelte"
  via="ahmadjoya"
  hashtags={['svelte', 'sveltekit']}
  related={['sveltejs']}
/>

| Prop | Type | Description | | :--------- | :--------- | :--------------------------------- | | title | string | Text of the post. | | via | string | Username to attribute the post to. | | hashtags | string[] | Hashtags appended to the post. | | related | string[] | Accounts suggested after posting. |

Twitter (deprecated)

⚠️ Twitter is now X. TwitterShare is kept as an alias: it renders the X icon and opens the same x.com/intent/post endpoint. Use XShare in new code.

<TwitterShare url="https://example.com" title="Hello" />

Props: identical to XShare.

Bluesky

<BlueskyShare url="https://example.com" title="Hello" separator=" — " />

| Prop | Type | Default | Description | | :---------- | :------- | :------ | :-------------------------------------- | | title | string | — | Text placed before the URL. | | separator | string | ' ' | Inserted between the title and the URL. |

Threads

<ThreadsShare url="https://example.com" title="Hello" />

| Prop | Type | Description | | :------ | :------- | :---------------- | | title | string | Text of the post. |

Facebook

<FacebookShare url="https://example.com" quote="Hello" hashtag="#svelte" />

| Prop | Type | Description | | :-------- | :------- | :-------------------------------------- | | quote | string | Quoted text shown with the shared link. | | hashtag | string | A single hashtag, including the #. |

Facebook Messenger

<FacebookMessengerShare
  url="https://example.com"
  appId="123456789012345"
  redirectUri="https://example.com/thanks"
/>

| Prop | Type | Required | Description | | :------------ | :------- | :------- | :---------------------------------------------------- | | appId | string | yes | Your Facebook app id. | | redirectUri | string | no | Where to send the user afterwards. Defaults to url. | | to | string | no | User id of a recipient. |

LinkedIn

<LinkedinShare
  url="https://example.com"
  title="Hello"
  summary="A short description"
  source="example.com"
/>

| Prop | Type | Description | | :-------- | :------- | :--------------------------- | | title | string | Title of the shared article. | | summary | string | Short description. | | source | string | Publisher name. |

LinkedIn now builds previews almost entirely from the shared page's Open Graph tags and may ignore title, summary and source.

WhatsApp

<WhatsappShare url="https://example.com" title="Hello" separator=" — " />

| Prop | Type | Default | Description | | :---------- | :------- | :------ | :-------------------------------------- | | title | string | — | Text placed before the URL. | | separator | string | ' ' | Inserted between the title and the URL. |

Telegram

<TelegramShare url="https://example.com" title="Hello" />

| Prop | Type | Description | | :------ | :------- | :-------------------------------- | | title | string | Message text shown with the link. |

Reddit

<RedditShare url="https://example.com" title="Hello" />

| Prop | Type | Description | | :------ | :------- | :----------------------- | | title | string | Title of the submission. |

Pinterest

<PinterestShare
  url="https://example.com"
  media="https://example.com/cover.png"
  description="Hello"
/>

| Prop | Type | Required | Description | | :------------ | :------- | :------- | :-------------------------------- | | media | string | yes | Absolute URL of the image to pin. | | description | string | no | Description of the pin. |

Tumblr

<TumblrShare
  url="https://example.com"
  title="Hello"
  caption="A short description"
  tags={['svelte', 'share']}
/>

| Prop | Type | Default | Description | | :--------- | :--------- | :------- | :-------------------------------- | | title | string | — | Title of the post. | | caption | string | — | Description of the post. | | tags | string[] | [] | Tags, joined with commas for you. | | posttype | string | 'link' | Tumblr post type. |

LINE

<LineShare url="https://example.com" title="Hello" />

| Prop | Type | Description | | :------ | :------- | :------------------------ | | title | string | Text shown with the link. |

Viber

<ViberShare url="https://example.com" title="Hello" separator=" — " />

| Prop | Type | Default | Description | | :---------- | :------- | :------ | :-------------------------------------- | | title | string | — | Text placed before the URL. | | separator | string | ' ' | Inserted between the title and the URL. |

Viber uses the viber:// scheme, so it only works where the Viber app is installed.

VK

<VKShare
  url="https://example.com"
  title="Hello"
  image="https://example.com/cover.png"
  noParse
/>

| Prop | Type | Description | | :---------- | :-------- | :---------------------------------------------- | | title | string | Title of the shared post. | | image | string | Absolute URL of the image to share. | | noParse | boolean | Stop VK from fetching page metadata. | | noVkLinks | boolean | Hide profile links in the dialog (mobile only). |

Weibo

<WeiboShare
  url="https://example.com"
  title="Hello"
  image="https://example.com/cover.png"
/>

| Prop | Type | Description | | :------ | :------- | :---------------------------------- | | title | string | Text of the post. | | image | string | Absolute URL of the image to share. |

Email

<EmailShare
  url="https://example.com"
  subject="Something for you"
  body="Take a look at this:"
  separator=" — "
/>

| Prop | Type | Default | Description | | :---------- | :------- | :------ | :--------------------------------------- | | subject | string | — | Subject line. | | body | string | — | Body text; the URL is appended after it. | | separator | string | ' ' | Inserted between the body and the URL. |

Email navigates to a mailto: link instead of opening a popup, so windowWidth, windowHeight and onShareWindowClose do not apply.

Mail.Ru

<MailruShare
  url="https://example.com"
  title="Hello"
  description="A short description"
  imageUrl="https://example.com/cover.png"
/>

| Prop | Type | Description | | :------------ | :------- | :---------------------------------- | | title | string | Title of the shared page. | | description | string | Description of the shared page. | | imageUrl | string | Absolute URL of the image to share. |

LiveJournal

<LiveJournalShare
  url="https://example.com"
  title="Hello"
  description="https://example.com"
/>

| Prop | Type | Description | | :------------ | :------- | :------------------------ | | title | string | Subject of the new entry. | | description | string | Body of the new entry. |

LiveJournal's endpoint takes only a subject and a body, so url is not sent. Put the link in description if you want it in the post.

Instapaper

<InstapaperShare
  url="https://example.com"
  title="Hello"
  description="A short description"
/>

| Prop | Type | Description | | :------------ | :------- | :----------------------------- | | title | string | Title of the saved page. | | description | string | Description of the saved page. |

Hatena

<HatenaShare url="https://example.com" title="Hello" />

| Prop | Type | Description | | :------ | :------- | :--------------------- | | title | string | Title of the bookmark. |

Gab

<GabShare url="https://example.com" title="Hello" />

| Prop | Type | Description | | :------ | :------- | :---------------- | | title | string | Text of the post. |

TypeScript

The package ships its own types — no @types/… package needed. Every component's props type is exported, which is handy when you wrap a button in your own component:

<script lang="ts">
  import { XShare, type XShareProps } from 'svelte-share-lite'

  let { url, ...rest }: XShareProps = $props()
</script>

<XShare {url} {...rest} round size={32} />

Also exported: CommonShareProps, NetworkLink, WindowPosition, ShareIconProps, SocialIcon, SocialIcons, and one …LinkParams type per network (XLinkParams, FacebookLinkParams, …).

Server-side rendering

The components never touch window, document or navigator during render — the browser APIs are only reached inside the click handler. You can import them directly into a SvelteKit page, layout or server-rendered component with no onMount guard and no browser check.

Accessibility

  • Every button renders as <button type="button">, so it is keyboard focusable and never submits a surrounding form by accident.

  • The icon <svg> is aria-hidden, and the button carries an aria-label set to the network key (facebook, x, …). Override it with something a screen reader user would want to hear:

    <FacebookShare {url} aria-label="Share on Facebook" />
  • The default styles keep the browser's focus ring intact. If you replace them with resetButtonStyle={false} or a custom class, make sure :focus-visible stays visible.

FAQ

Why doesn't my shared link show an image preview?

Previews are built by the social network from the Open Graph / Twitter Card meta tags of the shared page, not by this library. If a preview is missing or stale, add the right og:* / twitter:* tags to the page you are sharing, and use the network's own debugger to re-scrape it.

Can I get share counts?

No. Most networks removed their public count endpoints years ago, and this library deliberately makes no network requests.

Does it work with Svelte 4?

No — the components use runes, which need Svelte 5. Stay on Svelte 5 or use a different library.

Does it work outside SvelteKit?

Yes. Anything that compiles Svelte 5 components works: plain Vite, Astro with the Svelte integration, Storybook, and so on.

The popup is blocked. Why?

Browsers only allow window.open during a user gesture. If your beforeOnClick awaits something slow, the browser may no longer consider the click "recent" and will block the popup. Keep beforeOnClick fast, or use blankTarget and let the browser handle the navigation.

Why is my class rule not applied?

Svelte scopes styles per component, and the <button> belongs to this library's component. Wrap the selector in :global() — see Styling the button with CSS.

Contributing

Issues and pull requests are welcome at github.com/ahmadjoya/svelte-share-lite.

git clone https://github.com/ahmadjoya/svelte-share-lite.git
cd svelte-share-lite
npm install

npm run check    # svelte-check
npm test         # vitest
npm run lint     # eslint
npm run format   # prettier
npm run build    # svelte-package + publint

Adding a network means: an entry in src/lib/constant/icons.ts, a link builder in src/lib/utils/button.ts, its option type in src/lib/types.ts, a thin component in src/lib/components/buttons/, an export from src/lib/index.ts, a test, and a README section.

License

MIT © Ahmad Joya

Looking for the React version? See react-share-lite, or react-share-kit if you also need share counts.