Aannot8

Installation

The script tag, CSP directives, framework placement, and how to verify the widget is live.

Annot8 loads from a single script tag. There is no npm package to install and nothing to vendor — loading from the hosted URL is what keeps the widget up to date.

The snippet

Copy the generated snippet from Project → Settings → Installation. It already contains your project key, backend URL, and dashboard origin:

<script
  src="https://www.annot8.app/widget.js"
  data-project-id="pj_xxxxxxxxxxxx"
  data-convex-url="https://your-deployment.convex.cloud"
  data-dashboard-url="https://www.annot8.app"
  async
></script>

Use the exact values from your dashboard. Copying another workspace's project key or backend URL fails the origin check and the widget silently does nothing.

Script attributes

AttributeRequiredDescription
data-project-idYesYour project key (pj_...)
data-convex-urlYesBackend URL for your deployment
data-dashboard-urlNoDashboard origin. Inferred from the script src when omitted
data-public-slugNoPublic link slug for guest feedback. Usually auto-discovered — see Feedback links
data-themeNolight, dark, or auto (default)
data-labelNoLauncher label, overriding the dashboard setting
data-analytics-consentNotrue only after your consent manager has collected consent
data-configNoA JSON object carrying any of the above keys

Values are validated before use. A malformed project key, a non-HTTPS backend URL, or an unrecognised theme is discarded rather than passed through.

Alternatives to data-convex-url

If your CMS or template strips data-* attributes, the loader also reads:

<meta name="annot8-convex-url" content="https://your-deployment.convex.cloud" />

or a global set before the script runs:

<script>
  window.__ANNOT8_CONVEX_URL__ = "https://your-deployment.convex.cloud";
</script>

Where to put it

It has to load on every route, not inside a single page component.

app/layout.tsx
import Script from "next/script";

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en">
      <body>
        {children}
        <Script
          src="https://www.annot8.app/widget.js"
          data-project-id={process.env.NEXT_PUBLIC_ANNOT8_PROJECT_ID}
          data-convex-url={process.env.NEXT_PUBLIC_ANNOT8_CONVEX_URL}
          strategy="afterInteractive"
        />
      </body>
    </html>
  );
}

Content Security Policy

If your site sends a CSP, the widget needs four allowances:

script-src  https://www.annot8.app;
connect-src https://*.convex.cloud https://*.convex.site https://www.annot8.app;
frame-src   https://www.annot8.app;
img-src     data: blob:;

Why each one:

  • script-src — the loader itself.
  • connect-src — the widget talks to your Convex deployment over HTTPS and WebSocket, and calls …convex.site/widget/session to mint an origin-validated session token. Analytics, if enabled, posts a session-end beacon to the dashboard origin.
  • frame-src — all widget UI renders inside a sandboxed iframe served from the dashboard origin. Without this the launcher never appears.
  • img-src data: blob: — screenshots are rendered to a canvas in the page before upload.

Verify it worked

You should see a launcher button in the corner within a second or two of load. If it isn't there, work through Troubleshooting — the overwhelmingly common cause is a hostname that matches no environment.

To confirm from the console:

typeof window.Annot8 === "function"; // loader present

The loader being present but no launcher appearing means the project or environment check rejected the page, not that the script failed to load.

Core feedback collection does not require Annot8 Analytics, and Analytics is off for new projects. It only starts in the browser when both are true:

  1. An admin enabled Analytics in Settings → Features.
  2. Your page passes data-analytics-consent="true".

Wire that attribute to your consent-management platform rather than hard-coding it, and describe Annot8 in your own privacy and cookie notices. See Privacy and data handling.

Next

On this page