Embed reference
Two tags make up the embed: one loader <script> for the whole page, and one <tapback-bar> element per counter. This page is the full list of what each accepts.
Entities: how a count is scoped
A reaction count is scoped to an entity — the thing being reacted to, usually one page. Each entity has its own counter, its own live room, and its own row in your analytics.
<tapback-bar entity="blog/hello-world"></tapback-bar>
If you leave entity off, the bar infers one from the page's canonical URL — host plus path, lowercased, trailing slash stripped. On a normal page that's fine; set it explicitly when you want counts to survive a URL change, or when one page hosts several bars (an index of blog posts, say — the blog recipes walk through that). Use a path-like id such as blog/hello-world if you want to group entities by type.
The same entity id always lands on the same counter, wherever it appears — a bar on the index blurb and a bar on the post page share one tally as long as they share one entity.
The loader script
Set these as data-* attributes on the <script> tag. They apply to every bar on the page.
| Attribute | Default | Notes |
|---|---|---|
data-key | — | Your publishable key. Required. |
data-api | script origin | The Tapback API origin. Use https://api.tapback.party. |
data-theme | auto | auto | light | dark. |
data-live | visible | When to open the live socket: visible (on scroll into view), eager (immediately), or off (counts only, no live falls). |
data-layout | your config | Override the configured layout for this page: float or inline. |
The <tapback-bar> element
Per-bar overrides. On a single-bar page you rarely need more than entity; on a multi-bar page these are how each bar gets its own counter and placement.
| Attribute | Default | Notes |
|---|---|---|
entity | inferred from URL | Unique id for this page/item — see above. |
layout | script / config | float or inline for this bar. One float bar per page — a second one renders inline. |
live | script / config | Same values as data-live, per bar. live="off" on index blurbs keeps a long list cheap. |
muted | absent | Mutes while present — a host-driven override that doesn't touch the visitor's remembered choice. Toggle it from your own control; see the JS API. |
readonly | absent | Shows the bar without accepting taps: the reactions render as display-only chips and the counts keep updating live. It's for a copy of the bar inside your own editing UI, where an author's clicks would land in the post's real tally. Read when the bar connects, so put it in the template rather than toggling it later. |
reactions | your config | A comma-separated list of reaction keys to show on this page, in order — e.g. reactions="ghost,skull". The keys must be in the Tapbar's approved set (the default bar plus its palette). Sets this page's reactions the first time anyone loads it; a later change to the attribute doesn't move an already-counted page. Plus feature; free plans get the default bar. See different reactions per post. |
Everything else about the bar — the reaction set, icons, colors, corner, fall animation — comes from your Tapbar config in the console and updates on every embed without a code change. Theming covers the styling side, including the --tapback-* CSS custom properties the bar reads from your stylesheet.
The editor script (authoring)
A second, optional script for draft and preview templates only: it gives you a reaction picker in your CMS, so an author chooses a page's reactions while looking at it. It takes an authoring key (ak_live_…, minted in the console's API keys panel — Plus plan), a credential scoped to reading your approved reaction set and pointing a page at a subset of it.
Two tags again — the script, and a <tapback-editor> element that marks where the picker renders:
<script type="module" async src="https://api.tapback.party/editor.js"
data-key="ak_live_…" data-api="https://api.tapback.party"></script>
<tapback-editor></tapback-editor>
Put the element wherever it belongs in your editing UI — a sidebar, a settings drawer, under the post body. It's a block element in normal flow, so your own CSS positions and sizes it, and it reads the --tapback-* custom properties from your stylesheet like the bar does. Dropping it into a panel that already has its own frame? Set --tapback-bg and --tapback-border to transparent and the picker's card disappears into it. Without the element the script renders nothing and logs a note to the console saying so.
| Script attribute | Default | Notes |
|---|---|---|
data-key | — | An authoring key (ak_live_…). Required; the script refuses to boot on a publishable key. |
data-api | script origin | The Tapback API origin, same as the loader's. |
<tapback-editor> attribute | Default | Notes |
|---|---|---|
entity | every bar on the page | Edit this one page id. With it the picker needs no <tapback-bar> beside it, which is what lets it live in a sidebar or next to an iframe preview. Without it, the picker lists a section per bar it finds. |
float | absent | Pins a "✎ Reactions" button to the bottom-left of the viewport and opens the picker in a popover above it — the placement-free option when editing your template's layout is awkward. |
For an admin UI built in JavaScript, window.tapbackEditor.mount(target, opts) creates and appends the element for you and returns it. The element is the handle: open(), close(), refresh(), and remove() to tear it down.
const picker = window.tapbackEditor.mount("#sidebar", { entity: "blog/hello-world" });
// later
picker.remove();
The picker lists the default bar plus the palette; saving re-points the page immediately (earlier counts stay as read-only history) and refreshes the visible bar. Calls only work from the Tapbar's allowed sites. See pick reactions where you write for the workflow and per-CMS placement.