install
source · Clone the upstream repo
git clone https://github.com/ComeOnOliver/skillshub
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/ComeOnOliver/skillshub "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/TerminalSkills/skills/hotjar" ~/.claude/skills/comeonoliver-skillshub-hotjar && rm -rf "$T"
manifest:
skills/TerminalSkills/skills/hotjar/SKILL.mdsource content
Hotjar
Heatmaps, session recordings, and user feedback. See how users actually interact with your site and collect qualitative insights.
Script Installation
<!-- index.html — Add the Hotjar tracking script to your site. Place in <head> for heatmaps and recordings to capture from first paint. --> <head> <script> (function(h,o,t,j,a,r){ h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)}; h._hjSettings={hjid:YOUR_SITE_ID,hjsv:6}; a=o.getElementsByTagName('head')[0]; r=o.createElement('script');r.async=1; r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv; a.appendChild(r); })(window,document,'https://static.hotjar.com/c/hotjar-','.js?sv='); </script> </head>
Next.js Integration
// components/hotjar.tsx — Add Hotjar to a Next.js App Router app. // Uses next/script for optimal loading without blocking rendering. 'use client' import Script from 'next/script' export function HotjarScript({ siteId }: { siteId: number }) { return ( <Script id="hotjar" strategy="afterInteractive" dangerouslySetInnerHTML={{ __html: ` (function(h,o,t,j,a,r){ h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)}; h._hjSettings={hjid:${siteId},hjsv:6}; a=o.getElementsByTagName('head')[0]; r=o.createElement('script');r.async=1; r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv; a.appendChild(r); })(window,document,'https://static.hotjar.com/c/hotjar-','.js?sv='); `, }} /> ) }
// app/layout.tsx — Include Hotjar in the root layout. // Only loads in production to avoid polluting dev data. import { HotjarScript } from '@/components/hotjar' export default function RootLayout({ children }: { children: React.ReactNode }) { return ( <html lang="en"> <body> {children} {process.env.NODE_ENV === 'production' && ( <HotjarScript siteId={Number(process.env.NEXT_PUBLIC_HOTJAR_SITE_ID)} /> )} </body> </html> ) }
JavaScript API — Events and User Attributes
// lib/hotjar.ts — Hotjar JavaScript API helpers. // Identify users, trigger events, and control recordings. declare global { interface Window { hj: (...args: any[]) => void } } export function identifyHotjarUser(userId: string, attributes: Record<string, string | number | boolean>) { /** * Identify a user for filtering session recordings. * Attributes appear in the Hotjar dashboard for segmentation. * Max 100 attributes per user. */ window.hj('identify', userId, attributes) } export function triggerHotjarEvent(eventName: string) { /** * Trigger a custom event. Use to: * - Start a recording on a specific user action * - Show a survey when a condition is met * - Tag recordings for filtering */ window.hj('event', eventName) } export function tagRecording(...tags: string[]) { /** * Add tags to the current session recording. * Useful for filtering recordings by feature, experiment, or error state. */ window.hj('tagRecording', tags) } export function triggerSurvey(surveyId: number) { /** * Manually trigger a Hotjar survey (bypasses page targeting rules). * Survey must be set to "API" trigger mode in the Hotjar dashboard. */ window.hj('trigger', `survey_${surveyId}`) } export function setStateChange(url: string) { /** * Notify Hotjar of a virtual page change in an SPA. * Required for accurate heatmaps on client-side routed pages. */ window.hj('stateChange', url) }
SPA Route Change Tracking
// hooks/use-hotjar-spa.ts — Track route changes in React SPAs for Hotjar. // Ensures heatmaps and recordings capture navigation in client-side routing. 'use client' import { usePathname } from 'next/navigation' import { useEffect } from 'react' export function useHotjarSPA() { const pathname = usePathname() useEffect(() => { if (typeof window.hj === 'function') { window.hj('stateChange', pathname) } }, [pathname]) }
Privacy Controls
// lib/hotjar-privacy.ts — Configure Hotjar privacy and suppression. // Control what gets captured in recordings and heatmaps. /** * CSS classes for element-level control: * * data-hj-suppress — Mask element text in recordings (shows asterisks) * data-hj-allow — Explicitly allow capturing (overrides global suppress) * data-hj-masked — Mask input field values * * HTML example: * <input type="email" data-hj-suppress /> * <div data-hj-suppress>Sensitive content here</div> * <span data-hj-allow>Public content</span> */ export function configureSuppression() { // Suppress all input fields by default (opt-in to capture) // Set in Hotjar dashboard: Settings → Data Collection → Input masking // Or use CSS selectors to target specific elements // Programmatically suppress dynamic content const sensitiveElements = document.querySelectorAll('[data-sensitive]') sensitiveElements.forEach((el) => { el.setAttribute('data-hj-suppress', '') }) }
Integration with Google Analytics
// lib/hotjar-ga-integration.ts — Link Hotjar sessions with Google Analytics. // Pass the GA client ID to Hotjar for cross-referencing sessions. export function linkHotjarToGA() { // Wait for both GA and Hotjar to load const interval = setInterval(() => { if (typeof window.hj === 'function' && typeof window.gtag === 'function') { clearInterval(interval) // Get the GA client ID window.gtag('get', 'G-XXXXXXXXXX', 'client_id', (clientId: string) => { // Pass GA client ID as a Hotjar user attribute window.hj('identify', null, { ga_client_id: clientId, }) }) } }, 500) // Clean up after 10 seconds setTimeout(() => clearInterval(interval), 10000) }
Survey API
// lib/hotjar-surveys.ts — Trigger Hotjar surveys based on user behavior. // Combine with event tracking to show surveys at the right moment. export function showNPSSurveyAfterPurchase() { /** * Trigger NPS survey after a successful purchase. * Survey configured in Hotjar dashboard with API trigger mode. */ window.hj('trigger', 'post_purchase_nps') } export function showChurnSurvey() { /** * Show exit survey when user clicks cancel subscription. * Captures churn reason before they leave. */ window.hj('trigger', 'churn_survey') } export function showFeatureFeedback(featureName: string) { /** * Collect feedback on a specific feature. * Tag the recording so you can watch sessions where feedback was given. */ window.hj('tagRecording', [`feedback_${featureName}`]) window.hj('trigger', 'feature_feedback') }