Script tag

Two lines in the head. No build step and no package.

Add the tag

every page, in the head
<script>window.fr=window.fr||function(){(fr.q=fr.q||[]).push(arguments)}</script>
<script async
        src="https://app.firstrun.app/t.js"
        data-key="fr_xxxxxxxxxxxxxxxx"></script>

The first line queues calls made before the tag has loaded. The tag posts to wherever it was served from, so a first-party CNAME is one setting rather than two: data-host overrides it.

Ask for consent

Nothing is stored and nothing is sent until consent is granted.

JavaScript
fr("consent", true);
fr("consent", false);  // withdraw

Send your own events

Then write events.

JavaScript
fr("event", "download_clicked", { platform: "windows" });
fr("error", err);
fr("log", { name: "checkout_stalled", severity: 13, attributes: { step: 3 } });
fr("identify", "u_42");

event writes at INFO and error at ERROR, both filling in the conventional attributes. log takes any event you like: any name, any severity, any attributes, and your own time if you are recording something after the fact. Attribute values are JSON, so a number stays a number. Any element carrying data-fr-event="name" fires that event when clicked, and keeps its own behaviour whether or not the tag loaded.

With consent granted the tag also writes page_view (SPA navigations included), session_start, page_leave, outbound_click, file_download, form_submit and web_vital on its own. Each group is off when its attribute is false: data-auto-page, data-auto-outbound, data-auto-vitals, data-auto-forms, data-track-leave.