> For the complete documentation index, see [llms.txt](https://docs.limecall.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.limecall.com/callback/widget-recipes.md).

# Widget recipes

Copy-paste patterns for common widget customisations.

Working patterns you can paste. All of them assume the widget is installed — see [Install with JavaScript](/callback/install-with-javascript.md).

Each wraps its work in the `ready` event, which is the safe place for API calls.

## Your own button, no floating launcher

```html
<button type="button" id="cta">Talk to us</button>

<script>
window.LimeCall && window.LimeCall.on("ready", function () {
  window.LimeCall.hideLauncher();
  document.getElementById("cta").addEventListener("click", function () {
    window.LimeCall.open();
  });
});
</script>
```

## Open straight onto a specific tab

```js
document.getElementById("msg-us").addEventListener("click", function () {
  window.LimeCall.openTab("message");
});
```

## Open from a link

Link to `#limecall` — no JavaScript at all:

```html
<a href="#limecall">Request a callback</a>
```

`#lc-open` and `#lc-widget` work the same way, and the anchor survives a cross-page link (`/pricing#limecall`), which makes it the one to use in emails and ads.

For a link the visitor may click more than once, handle the click instead — a repeated identical anchor fires no event:

```html
<a href="#" onclick="window.LimeCall&&window.LimeCall.open();return false;">Request a callback</a>
```

## Show the widget only on high-intent pages

Display rules in the dashboard are the better tool for this — see [Display rules](/callback/display-rules.md). When you need logic they cannot express:

```js
window.LimeCall.on("ready", function () {
  var highIntent = /\/(pricing|demo|contact)/.test(location.pathname);
  if (highIntent) window.LimeCall.showLauncher();
  else window.LimeCall.hideLauncher();
});
```

## Open automatically for an engaged visitor

The widget scores engagement and reports it. Rather than a fixed timer, wait until someone is actually engaged:

```js
window.LimeCall.on("score", function (e) {
  if (e.total >= e.threshold) window.LimeCall.open();
});
```

## Send conversions to Google Analytics

```js
window.LimeCall.on("callback:requested", function (e) {
  gtag("event", "generate_lead", {
    method: "limecall_widget",
    mode: e.mode,
    callback_id: e.id
  });
});
```

## Send conversions to Meta

```js
window.LimeCall.on("callback:requested", function () {
  if (window.fbq) fbq("track", "Lead");
});
```

## Log failures you would otherwise never see

```js
window.LimeCall.on("callback:failed", function (e) {
  // "rejected" = closed / blocked / unsupported country. "network" = connectivity.
  console.warn("LimeCall:", e.reason, e.message);
  if (window.gtag) gtag("event", "callback_failed", { reason: e.reason });
});
```

Worth doing early. Failed requests are invisible otherwise, and a run of `rejected` usually means your business hours are wrong.

## React to a browser call starting and ending

```js
window.LimeCall.on("webcall:started", function () {
  document.body.classList.add("in-call");   // e.g. pause a background video
});
window.LimeCall.on("webcall:ended", function () {
  document.body.classList.remove("in-call");
});
```

## Do not offer a call when the widget is hidden

```js
window.LimeCall.on("ready", function (state) {
  if (!state.visible) document.getElementById("cta").hidden = true;
});
```

## Only promise a call someone can take

`state.visible` tells you the widget is on the page. `hasAvailableAgents()` tells you somebody would actually answer — closed hours, an empty team, or exhausted AI minutes all come back `false`.

```js
window.LimeCall.on("ready", async function () {
  var cta = document.getElementById("cta");
  if (await window.LimeCall.hasAvailableAgents()) {
    cta.textContent = "Talk to us now";
  } else {
    cta.textContent = "Leave your number";     // still captures the lead
  }
});
```

Swapping the wording beats hiding the button. The visitor who wanted to talk still gets a way through.

## Route by page, without asking the visitor

```js
window.LimeCall.on("ready", function () {
  var byPath = { "/pricing": "Sales", "/docs": "Support", "/billing": "Billing" };
  var dept = byPath[location.pathname];
  if (dept && window.LimeCall.getDepartments().indexOf(dept) !== -1) {
    window.LimeCall.setDepartment(dept);
  }
});
```

Checking against `getDepartments()` first means renaming a department in the dashboard degrades to your default routing instead of sending a mismatched label.

## Submit from your own form and keep the result

```js
var r = await window.LimeCall.requestCallback(phoneInput.value, { name: nameInput.value });
status.textContent = r.ok ? "Calling you now — keep your phone nearby." : r.message;
```

Full version, including keeping your own backend in the loop: [Connect your own form](/callback/connect-your-own-form.md).

## Clean up in a single-page app

Handlers persist across client-side navigation, so remove the ones tied to a page:

```js
function onRequested(e) { /* … */ }

window.LimeCall.on("callback:requested", onRequested);
// later, when the view unmounts
window.LimeCall.off("callback:requested", onRequested);
```

Registering the same handler twice is safe — it is only added once — but a handler belonging to a page the visitor has left will still fire.
