ARTICLE

Install the Feedback Embed: Script Tag, npm and Browser Support

The script tag, the JSON config block, npm, the UMD build, self-hosting, which browsers it runs in, and every option the widget takes.

Qaid Team
TL;DR

Paste one script tag and the widget loads a 17 KB core, then fetches targeting, the message box, screenshots and video only when they are used. It needs Chrome 123, Firefox 120 or Safari 17.5 to draw the message box properly. Every option, its default and its script attribute are in the table at the end.

This page covers every way to load the thumbs widget, what each one downloads, and every option it takes. For the first install from the dashboard, start with the Dashboard quick start.

Paste the script tag

<script
  src="https://unpkg.com/@qaiddev/thumbs-embed@1.9.0/dist/embed.js"
  defer
  data-api-key="YOUR_PROJECT_KEY"
  data-endpoint="https://qaid.dev/api/feedback"
></script>

Put it just before </body>. defer makes the browser run it after the page is parsed, so it never holds the page up and it finds a theme <style> block wherever that block sits. embed.js is 0.5 KB gzipped. It copies every data-* attribute onto a new tag that loads loader.js from the same folder, and the widget starts once the page has been parsed. The dashboard’s snippet leaves out the @1.8.0, which gets you the newest release on every load. Keep the version in to pin it: the other files load from the same versioned folder.

Keep the config out of the tag

Every option has a data-* attribute on the tag (thumbs-embed 1.9.0 or later). If a long list of attributes is hard to read, put the same settings in a JSON block instead:

<script type="application/json" data-feedback-config>
{
  "endpoint": "https://qaid.dev/api/feedback",
  "apiKey": "YOUR_PROJECT_KEY",
  "position": "bottom-left",
  "text": { "positiveLabel": "Envoyer un avis positif" }
}
</script>
<script src="https://unpkg.com/@qaiddev/thumbs-embed@1.9.0/dist/embed.js" defer></script>

When the block is there and is valid JSON, the widget uses it and ignores the tag’s data-* attributes, so it must carry endpoint and apiKey itself. If the JSON does not parse, the widget falls back to the attributes without a word. "cssSelector": "#qaid-theme" works here like data-css-selector.

Install from npm

npm install @qaiddev/thumbs-embed
import { QaidFeedback } from "@qaiddev/thumbs-embed";

const feedback = new QaidFeedback({
  endpoint: "https://qaid.dev/api/feedback",
  apiKey: "YOUR_PROJECT_KEY",
});
// feedback.destroy() removes it again.

Call new in the browser, after the page has a <body>: in a framework, that means in a mounted hook, not during server rendering. The main entry is one 32.7 KB file with every feature in it. Import from @qaiddev/thumbs-embed/loader instead to get the same class with the split, load-on-use build. Drop the script tag once you construct the widget in code, or the page gets two widgets.

Use it in a page with no build step

A module script can import the loader straight from the CDN:

<script type="module">
  import { QaidFeedback } from "https://unpkg.com/@qaiddev/thumbs-embed@1.9.0/dist/loader.js";

  new QaidFeedback({
    endpoint: "https://qaid.dev/api/feedback",
    apiKey: "YOUR_PROJECT_KEY",
  });
</script>

This is the form to use when the widget should only start on some pages. See Conditional display.

Use the UMD build

dist/qaid.umd.cjs is the whole widget in one classic script, 26.7 KB gzipped. It starts itself from the data-* attributes on its own tag, and it defines window.Qaid, so new Qaid.QaidFeedback({ … }) works after it loads.

It does not rescue old browsers. embed.js switches to it only in a browser without ES modules, but the file is written in modern JavaScript that such a browser cannot read. Use it when you want one file to host, not for reach. If your server sends .cjs files with a type other than JavaScript, rename it to .js.

Host it yourself

Copy node_modules/@qaiddev/thumbs-embed/dist/ to your server, keeping embed.js, loader.js and the chunks/ folder side by side, and point the tag’s src at your copy of embed.js.

Two parts still load from elsewhere:

  1. Screenshots on touch screens, or with screenshotMethod: "dom", load https://qaid.dev/lib/html2canvas.min.js.
  2. A button linked to a quest imports the quests widget from unpkg. Set quests.moduleUrl (data-quest-module-url) to your own copy.

Know what loads, and when

Part Size, gzipped Loaded
embed.js 0.5 KB With the page
Core: buttons, styles, submit 16.9 KB With the page
Element targeting 3.8 KB When the pointer rests on a thumb, or on the first click
Message box 2.5 KB On the first thumb click
Screenshot, then the markup editor 0.7 to 1.1 KB, then 5.1 KB Soon after the page settles if captureScreenshot is on, otherwise on the first capture
Video recording 6.7 KB Soon after the page settles if captureVideo is on, and when the pointer rests on the record button

To watch it, filter the Network panel to JS and reload. You should see embed.js, loader.js and a loader- file, then a targeting- file when you hover a thumb. If nothing loads, check the tag and look for a blocked request; see Troubleshooting.

Loading the film…

Check which browsers it runs in

thumbs-embed 1.8.0 or later runs on plain http:// pages as well as https://. Screen recording still needs https:// or localhost.

The widget uses For Chrome and Edge Firefox Safari
light-dark() Background and text of the message box, phone sheet and video review 123 120 17.5
color-mix() Targeting outline, crosshairs and focus glow 111 113 16.2
:has() Sizing emoji icons 105 121 15.4
Screen capture The record button, and desktop screenshots Desktop only Desktop only Desktop only

In a browser older than the first row, the buttons still work, but the message box has no background. Where screen capture is missing, the record button does not appear.

A strict Content Security Policy needs unpkg.com (and qaid.dev, for phone screenshots) in script-src, qaid.dev in connect-src, and 'unsafe-inline' in style-src, because the widget writes its own <style> elements.

Every option

JavaScript and the JSON block take the option names. The script tag takes the attributes. For a true/false attribute, write "true"; anything else, or a bare attribute, leaves it off.

Option Type Default Script attribute
endpoint URL, required none data-endpoint
apiKey string none data-api-key
position "bottom-right", "bottom-left", "top-right", "top-left" "bottom-right" data-position
offset { x, y } in pixels { x: 16, y: 16 } data-offset-x, data-offset-y
direction "horizontal", "vertical" "horizontal" data-direction
container CSS selector none: fixed to the window data-container
zIndex number 50 data-zindex
buttonSize "small", "medium", "large" "medium" data-button-size
buttonClass class names none data-button-class
colors.positive CSS colour rgb(0, 200, 83) data-positive-color
colors.negative CSS colour rgb(255, 0, 0) data-negative-color
colors.marker CSS colour #6365f1 data-marker-color
positiveIcon, negativeIcon, feedbackIcon SVG or text built-in icons data-positive-icon, data-negative-icon, data-feedback-icon
recordIcon SVG or text built-in icon none
singleButton true/false false data-single-button
hideThumbs true/false false data-hide-thumbs
hideDismiss true/false false, and always on in a container data-hide-dismiss
incognito true/false false data-incognito
skipTargeting true/false false data-skip-targeting
feedbackMode "target", "annotate" "target" data-feedback-mode
text.* strings see Text customization same page
text.errorTitle, text.errorMessage strings “Message not sent”, “Something went wrong, so we did not get your message. Please try again later.” data-error-title, data-error-message
hideConfirmation true/false false data-hide-confirmation
modalWidth pixels 400 data-modal-width
backdropOpacity 0 to 1 0.3 data-backdrop-opacity
fontFamily CSS font list system-ui, -apple-system, sans-serif data-font-family
fontSize pixels 16 data-font-size
css CSS text none data-css-selector, naming a <style> element
captureScreenshot true/false false data-capture-screenshot
screenshotMethod "permission", "dom" "permission", and "dom" on touch screens data-screenshot-method
screenshotOptions.quality 0 to 1 0.8 data-screenshot-quality
screenshotOptions.maxWidth, .maxHeight pixels 1280, 800 data-screenshot-max-width, data-screenshot-max-height
annotate true/false true data-annotate="false" turns it off
annotationColor CSS colour colors.marker data-annotation-color
annotationPalette list of hex colours six colours none
captureVideo true/false false data-capture-video
videoOptions.maxDuration seconds 15 data-video-max-duration
videoOptions.redaction true/false false data-video-redaction
quests.base URL none data-quest-base
quests.up, .down, .video quest ids none data-quest-up, data-quest-down, data-quest-video
quests.apiKey string apiKey data-quest-api-key
quests.moduleUrl URL the quests widget on unpkg data-quest-module-url
Pro

Screenshots, console errors and video recordings are kept on Pro projects only. On Free the feedback still arrives without them. See Free vs Pro.

Back to all articles