Skip to content

JITSU-135 feat(console): add Microsoft Clarity and Hotjar device destinations - #1424

Merged
vklimontovich merged 8 commits into
newjitsufrom
jitsu-135-clarity-hotjar-destinations
Jul 28, 2026
Merged

JITSU-135 feat(console): add Microsoft Clarity and Hotjar device destinations#1424
vklimontovich merged 8 commits into
newjitsufrom
jitsu-135-clarity-hotjar-destinations

Conversation

@vklimontovich

Copy link
Copy Markdown
Contributor

Adds two client-side device (tag) destinationsMicrosoft Clarity and Hotjar. Both are session-analytics tools (heatmaps, recordings, surveys) whose value comes from running a vendor JS snippet in the visitor's browser, so they run through the @jitsu/js SDK as internal device plugins, following the existing logrocket / ga4-tag / gtm pattern.

JITSU-135

Event mapping

Microsoft Clarity (tag from clarity.ms/tag/<projectId>)

  • identifyclarity('identify', userId) + traits forwarded as filterable custom tags (clarity('set', k, v)); name/email used as friendly name.
  • trackclarity('event', name); event properties forwarded as custom tags when enabled (default on — Clarity events carry only a name).
  • page → no-op (Clarity auto-records page views).
  • Options: projectId, cookieConsent, trackProperties (tags | ignore).

Hotjar (tag from static.hotjar.com/c/hotjar-<siteId>.js?sv=6)

  • identifyhj('identify', userId, attributes) (null userId allowed).
  • trackhj('event', name) (name sanitized: ≤250 chars, spaces → _); Hotjar has no per-event property API, so properties can be merged into the user record when enabled (default off).
  • pagehj('stateChange', path) behind an opt-in SPA toggle (default off).
  • Options: siteId, spaPageViews, trackProperties (ignore | attributes).

Reference: RudderStack Hotjar / MicrosoftClarity integrations.

Changes

  • libs/jitsu-js/src/destination-plugins/{clarity,hotjar}.ts — browser plugins.
  • libs/jitsu-js/src/destination-plugins/index.ts — register in internalDestinationPlugins.
  • webapps/console/lib/schema/destinations.tsx + icons/{clarity,hotjar}.tsx — catalog entries + icons.
  • bulker/ingest/destination_types.goDeviceOptions map entries (classifies each as client-side and tells the SDK which plugin to run).

The id (clarity, hotjar) matches across catalog, deviceOptions.name, plugin, and Go map.

Verification

  • bulker/ingest compiles; jitsu-js tsc --noEmit passes; console pnpm codegen + tsc --noEmit passes.
  • Manual (todo): create each destination in the console, connect to a site stream, confirm window.clarity(...) / window.hj(...) fire.

Docs (pages, catalog cards, logos) are in a separate PR against jitsucom/websites.

Note: the Clarity icon is a hand-authored approximation of the layered-panels mark (no official SVG is CDN-hosted). Swap in the official asset if available.

…inations

Both are client-side session-analytics tools, so they run in the browser via
the @jitsu/js SDK as internal device plugins, following the logrocket/ga4-tag/gtm
pattern.

- clarity: identify -> clarity('identify') + traits as custom tags; track ->
  clarity('event') + optional properties as tags; cookie-consent option.
- hotjar: identify -> hj('identify', userId, attrs); track -> hj('event')
  (name sanitized); opt-in page -> hj('stateChange') for SPAs.

Wires the id across the console catalog, jitsu-js plugin registry, and the
ingest DeviceOptions map (which classifies the destination as client-side and
tells the SDK which plugin to run).
@vklimontovich
vklimontovich force-pushed the jitsu-135-clarity-hotjar-destinations branch from d87a8a5 to 1784ba7 Compare July 26, 2026 16:26
vklimontovich and others added 4 commits July 28, 2026 09:01
Mirrors GTM's loadGtm. When off, Jitsu skips injecting the vendor tag (and, for
Hotjar, skips setting _hjSettings) and only forwards events to the already-loaded
window.clarity/hj. The self-queuing stub is still installed so early events aren't
lost. Avoids double-loading Clarity/Hotjar when the page already includes them.
The projectId/siteId is only used when Jitsu loads the tag.
Replaces the hand-drawn placeholder with Clarity's official mark, sourced from
Clarity's own static assets (claritystatic.azureedge.net/images/logo.svg).
Device destinations must also be registered as no-op builtin functions
so rotor's function-chain builder resolves them; without the entry
getBuiltinFunction returns undefined and rotor throws "no functions
assigned to it's destination type" for clarity/hotjar connections.
Mirrors the existing tag/gtm/logrocket/ga4-tag no-op entries.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LVmXNhA3FRyKRc4cdK1k4i
@vklimontovich
vklimontovich marked this pull request as ready for review July 28, 2026 13:40
jitsu-code-review[bot]
jitsu-code-review Bot previously approved these changes Jul 28, 2026

@jitsu-code-review jitsu-code-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the Clarity/Hotjar device-destination additions across ingest mappings, destination registration, plugin runtime logic, and console schema wiring. I found a few correctness risks worth addressing: global singleton state can mix multiple destination configs, and failed script loads can leave unbounded in-memory queues.

Comment thread libs/jitsu-js/src/destination-plugins/clarity.ts Outdated
Comment thread libs/jitsu-js/src/destination-plugins/clarity.ts
Comment thread libs/jitsu-js/src/destination-plugins/hotjar.ts Outdated
Comment thread libs/jitsu-js/src/destination-plugins/hotjar.ts
…queue

Addresses code-review findings:
- window.clarity / window.hj are vendor-owned singletons, so a second
  destination with a different projectId/siteId can't get its own instance.
  Track the active id and warn on mismatch instead of silently routing
  events to the wrong project/site (first destination to load the tag wins).
- On script-load failure, replace the queuing stub with a no-op (dropping
  its queue) and short-circuit handle() when state is "failed", so events
  no longer accumulate unbounded in window.clarity.q / window.hj.q for the
  rest of the page lifetime.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LVmXNhA3FRyKRc4cdK1k4i
jitsu-code-review[bot]
jitsu-code-review Bot previously approved these changes Jul 28, 2026

@jitsu-code-review jitsu-code-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the new Clarity/Hotjar device-destination additions across ingest mapping, destination registration, and console schema wiring. I found two correctness risks in the browser plugin initialization flow and left inline comments with concrete scenarios.

Comment thread libs/jitsu-js/src/destination-plugins/clarity.ts Outdated
Comment thread libs/jitsu-js/src/destination-plugins/hotjar.ts Outdated
…er a passive one

Follow-up code-review finding: when loadClarity/loadHotjar===false ran
first it set global state to "loaded", so a later load-enabled destination
early-returned at the guard and never loaded the real SDK - stranding
events in window.clarity.q / window.hj.q forever.

Introduce a transitional "passive" state for the load-it-yourself path.
A subsequent load-enabled destination transitions passive -> loading and
takes ownership of loading the tag. Only loading/loaded/failed are terminal
for the singleton-ownership guard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LVmXNhA3FRyKRc4cdK1k4i
jitsu-code-review[bot]
jitsu-code-review Bot previously approved these changes Jul 28, 2026

@jitsu-code-review jitsu-code-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the Clarity/Hotjar device-destination additions across jitsu-js, destination wiring, and console schema updates. The implementation looks solid overall; I left two inline questions about a possible long-session memory-growth edge case when loadClarity/loadHotjar is disabled and the vendor script never actually appears.

Comment thread libs/jitsu-js/src/destination-plugins/clarity.ts Outdated
Comment thread libs/jitsu-js/src/destination-plugins/hotjar.ts Outdated
Follow-up code-review question: in loadClarity/loadHotjar===false mode, if
the page never loads the vendor tag (misconfig, blocked snippet), the
self-queuing stub would grow window.clarity.q / window.hj.q unbounded for
the whole session.

Cap the stub queue at 1000 entries. The real vendor script drains the
queue within seconds, so the cap is only reached when the tag never loads;
it's generous enough that a normal slow load never drops events.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LVmXNhA3FRyKRc4cdK1k4i

@jitsu-code-review jitsu-code-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the diff from 850f6cc to d871271, including the new Clarity/Hotjar device plugins, destination wiring in console/bulker, and builtin destination-function registrations.\n\nI also checked existing PR review threads and skipped previously resolved findings.\n\nNo new actionable bugs, security issues, or correctness regressions stood out in the current patch.

@vklimontovich
vklimontovich merged commit c9fae06 into newjitsu Jul 28, 2026
5 checks passed
@vklimontovich
vklimontovich deleted the jitsu-135-clarity-hotjar-destinations branch July 28, 2026 17:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants