Builder Icebreakers vs. Custom-JS Icebreakers (and when to use each)

Smarter Prompts: Builder Icebreakers vs. Custom-JS Icebreakers (and when to use each)

Hi all!!

This post should tie together/wrap up a few different posts mentioning or asking questions on how to either beef up, or gain some clarity on using custom JS and embed types.

Pickaxe already includes built-in icebreakers in the chat UI that are great defaults and zero code. Occasionally users will want page-aware prompts, A/B tests, or chips that live outside the chat panel (hero, header, sticky footer). That’s where a tiny bit of custom JavaScript on top of a script embed shines.

TL;DR

  • Simplest and works with an iframe? → Use Builder icebreakers.
  • Personalized chips (URL/cookie/plan-aware), extra analytics, or custom placement? → Add custom JS with a script embed. When you programmatically prefill, fire input/change so the embed registers the text.
  • Combine both: keep Builder chips as defaults; layer page-aware chips with JS (see FAB/UI augmentation examples).

What’s different (quick table)

Dimension Builder Icebreakers (inside chat) Custom-JS Icebreakers (page-side)
Where they render Inside the Pickaxe embed Anywhere on your page
Works with iframe? Yes No — needs script embed so parent JS can reach the DOM.
Events Wired internally You must fire input/change when prefilling; otherwise messages can register blank.
Personalization Limited to bot context Full page/URL/cookie context (e.g., window.PickaxeConfig, UTM params)
Analytics Pickaxe metrics Your GA/Segment + Pickaxe (if you auto-send)
A/B testing Harder Easy in your site code
Maintenance Lowest You own a tiny snippet
Host quirks Fewer (self-contained) Must handle site keybindings (e.g., Genially spacebar capture)

Adapter A — Add page-aware chips that prefill the chat (script embed)

Use this when: you want chips anywhere on the page that insert a prompt (and optionally auto-send).
Requires: script embed (Inline / Flex), not iframe.

<!-- Your Pickaxe script embed -->
<script src="https://studio.pickaxe.co/api/embed/bundle.js" defer></script>

<!-- Your chips anywhere on the page –>
<ul class=“icebreakers”>
<li><button data-icebreaker=“tour”>Give me a 2-minute tour of features</button></li>
<li><button data-icebreaker=“pricing”>Which plan should I choose?</button></li>
<li><button data-icebreaker=“integrations”>Show me Zapier/Make options</button></li>
</ul>

<!-- Prefill logic (namespaced + proper events) –>
<script>
(() => {
if (window.PICKAXE_ICEBREAKER) return;
window.PICKAXE_ICEBREAKER = true;

const MESSAGES = {
tour: ‘Give me a 2-minute tour of features.’,
pricing: ‘Based on my usage, which plan should I choose and why?’,
integrations: ‘Show me how to connect this to Zapier or Make.’
};

function findTextarea() {
const root = document.querySelector(‘[data-pickaxe-embed]’);
return root && root.querySelector(‘textarea’);
}

function setPrompt(text) {
const area = findTextarea();
if (!area) return false;
area.focus();
area.value = text;
// Fire the events the embed listens for (critical)
area.dispatchEvent(new Event(‘input’, { bubbles: true }));
area.dispatchEvent(new Event(‘change’, { bubbles: true }));
try { area.setSelectionRange(area.value.length, area.value.length); } catch {}
return true;
}

function clickSend() {
const root = document.querySelector(‘[data-pickaxe-embed]’);
root?.querySelector(‘button[type=“submit”], button[aria-label=“Send”], [data-role=“send”]’)?.click();
}

function bind() {
document.querySelectorAll(‘[data-icebreaker]’).forEach(el => {
if (el.dataset.bound === ‘1’) return;
el.dataset.bound = ‘1’;
el.addEventListener(‘click’, (e) => {
e.preventDefault();
const key = el.getAttribute(‘data-icebreaker’);
const text = MESSAGES[key] || key;
if (setPrompt(text)) {
// Optional: auto-send the message
// clickSend();
}
}, { passive: false });
});
}

bind();
new MutationObserver(bind).observe(document.body, { childList: true, subtree: true });
})();
</script>

Why the events? Community reports show that setting textarea.value alone can produce a blank message; dispatch input/change so the app recognizes the text.

Customize: change the MESSAGES; uncomment clickSend(); to auto-send; style your chips as cards/links/etc. For positioning ideas (e.g., text above a FAB), see the community walkthrough.

Adapter B — Track Builder icebreaker clicks with your analytics (no DOM writes)

Use this when: you keep Builder chips but want GA/Segment events on your site.
Requires: script embed (the parent page needs to observe the embed’s DOM). Iframe isolates inner DOM.

<script>
(() => {
  const ROOT = '[data-pickaxe-embed]';
  const mo = new MutationObserver(() => {
    document.querySelectorAll(`${ROOT} [role="button"], ${ROOT} button`).forEach(btn => {
      if (btn.dataset._tracked) return;
      btn.dataset._tracked = '1';
      btn.addEventListener('click', () => {
        const text = btn.textContent?.trim() || 'icebreaker';
        // GA4 example
        window.gtag?.('event', 'pickaxe_icebreaker_click', { text });
        // Segment example
        window.analytics?.track?.('Pickaxe Icebreaker Click', { text });
      });
    });
  });
  mo.observe(document.documentElement, { subtree: true, childList: true });
})();
</script>

References

  1. What’s the difference between embed types? (script vs iframe)
  2. Embed shows blank when text is injected (input/change events)
  3. Genially spacebar / keybinding capture (scope & isolate)
  4. Add custom text above a FAB (UI augmentation example)
  5. Pass URL parameters with window.PickaxeConfig