Documentation
Two packages. loupe-widget is what you install; loupe-core is the anchoring engine inside it, published separately for anyone who wants the primitives without the interface.
Start here
npm install loupe-widget
import { createFeedback } from 'loupe-widget'
createFeedback({
publishableKey: 'pk_live_…',
viewerToken: () => fetch('/api/loupe-token').then((r) => r.text()),
release: process.env.COMMIT_SHA,
})That is the whole installation. The widget guide covers the script-tag and tag-manager routes, what the options do, and how activation behaves on a production domain.
How it works
A comment is stored as a description of an element — its text, the names you gave it, what it sits between, the shape of the tree above it — rather than as a selector or a pair of coordinates. When the page is loaded again the description is resolved against the current DOM and scored, and the result lands in one of three bands.
| Band | Meaning |
|---|---|
| anchored | Found, confidently. The stored description is rewritten to match the page as it is now. |
| flagged | Found, probably. Shown with a dashed ring and a tooltip saying the element may have changed. |
| orphaned | Gone. The pin is never placed at stale coordinates — it is simply not drawn. |
The anchoring API documents the primitives, the signals and their weights, and how to tune the thresholds.
Keys
| Key | Where it goes |
|---|---|
| pk_live_… | Your client bundle. It identifies the project, can file a comment, and can read no one else's. |
| sk_live_… | Your server only. Mints the short-lived viewer tokens that let someone read the team's comments. |
| LP-XXXX-… | A join code, typed into the widget once. It enrols that browser; it is not a password. |
Mint all three on your project page. Secrets are shown once — only a hash is stored, so there is no second chance to read one.