Share buttons that stay out of the way.no account required
AddToSite

← All customization

What gets shared

Short links through your own shortener

Give the widget a function and every popup share goes through it first. The window is opened the instant the reader presses, on a blank page, and pointed at the short link when your promise settles — which is the only order a browser allows.

The code

<div class="ats_kit ats_kit_size_32">
  <a class="ats_btn_x"></a>
  <a class="ats_btn_whatsapp"></a>
  <a class="ats_btn_telegram"></a>
  <a class="ats_btn_email"></a>
  <a class="ats_dd"></a>
</div>

<p id="ats-demo-status" role="status" aria-live="polite">Try X or WhatsApp. This example resolves to the AddToSite home URL.</p>

The hook

It is handed the page address — already carrying your campaign parameters if you set any — and it returns a promise, or a plain string if it already knows the answer. What comes back goes into the service’s template in place of the link, untagged, because a short link is a finished address that the shortener issued.

window.ats_config = {
  shorten: function (url) {
    return fetch('/shorten?u=' + encodeURIComponent(url))
      .then(function (res) { return res.text(); });
  }
};

Why the window opens before the link is known

A window opened after an await has lost the user gesture that justified it, and every browser blocks it. So the popup is opened synchronously on the click, at about:blank, and re-pointed with location.replace once your promise settles — replace, so the blank page is not a history entry the reader has to walk back through. Write it the other way round and it works on your machine with the popup blocker off, and nowhere else.

/* This is the version to avoid: the window is opened after the wait,
   so the click is long over and the browser refuses it. */
async function share(url) {
  var short = await shorten(url);
  window.open(short);
}

Three ways it gives up, and all of them still share

A promise that rejects, a shortener that never answers within three seconds, or a result that is not an http address — HTML from a failing endpoint, a null, a javascript: string — each lands the window on the full URL instead. The three seconds are not configurable: the number that matters to a reader is how long they stare at a blank tab, and three is already at the edge of it. An answer that arrives after the fallback is ignored rather than moving them a second time.

What the shortener never reaches

Copy link, Print, and any template with a mailto: or sms: scheme: none of those open a window, so there is nothing to re-point and the full address is used. The count is unaffected either way — it is keyed to the page, never to the short link, so shortening cannot split one article into two numbers.

Live demo

An article worth passing on

Try X or WhatsApp. This example resolves to the AddToSite home URL.

This working asynchronous hook uses the home URL as its demonstration result. Replace the Promise with your own shortener. Email keeps the original URL.

Services (4 selected)

Button size

Shape

Behaviour

<div class="ats_kit ats_kit_size_32">
  <a class="ats_btn_x"></a>
  <a class="ats_btn_whatsapp"></a>
  <a class="ats_btn_telegram"></a>
  <a class="ats_btn_email"></a>
  <a class="ats_dd"></a>
</div>

<p id="ats-demo-status" role="status" aria-live="polite">Try X or WhatsApp. This example resolves to the AddToSite home URL.</p>

<script>
window.ats_config = window.ats_config || {};
window.ats_config.shorten = function (url) {
  document.getElementById('ats-demo-status').textContent = 'Resolving a short link for ' + url;
  // Demo destination. Replace this Promise with your own shortener request.
  return Promise.resolve('https://addtosite.com/').then(function (shortUrl) {
    document.getElementById('ats-demo-status').textContent = 'Resolved: ' + shortUrl;
    return shortUrl;
  });
};
</script>

<script async src="//static.addtosite.com/widget.js"></script>

Related

More in What gets shared