Add Analytics logo

Add Analytics

CommunityPopular
LeoYeAI
add-analytics

Add Google Analytics 4 tracking to any project. Detects framework, adds tracking code, sets up events, and configures privacy settings.

Overview

PublisherLeoYeAI
Repositoryopenclaw-master-skills
Skill nameadd-analytics
Stars
2.1K
Forks
325
Bundled files
1
LicenseMIT
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • 1 bundled files

    Scripts, templates, and references the model can read while it works. Files are read-only and never executed.

  • Open source

    Published by LeoYeAI on GitHub. Read the source before you install it.

Installation

Install the Add Analytics AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/LeoYeAI/openclaw-master-skills.git /tmp/openclaw-master-skills
mkdir -p .claude/skills
cp -r /tmp/openclaw-master-skills/skills/add-analytics .claude/skills/add-analytics
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Add Analytics in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Add Analytics on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Add Analytics is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

Google Analytics 4 Setup Skill

You are setting up Google Analytics 4 (GA4) for a project. Follow this comprehensive guide to add analytics properly.

Arguments

Parse the following from $ARGUMENTS:

  • Measurement ID: Format G-XXXXXXXXXX (required, ask if not provided)
  • --events: Include custom event tracking helpers
  • --consent: Include cookie consent integration
  • --debug: Enable debug mode for development

Step 1: Detect Project Type

Scan the project to determine the framework/setup:

Priority detection order:
1. next.config.js/ts → Next.js
2. nuxt.config.js/ts → Nuxt.js
3. astro.config.mjs → Astro
4. svelte.config.js → SvelteKit
5. remix.config.js → Remix
6. gatsby-config.js → Gatsby
7. vite.config.js + src/App.vue → Vue + Vite
8. vite.config.js + src/App.tsx → React + Vite
9. angular.json → Angular
10. package.json with "react-scripts" → Create React App
11. index.html only → Plain HTML
12. _app.tsx/jsx → Next.js (App Router check: app/ directory)

Also check for:

  • TypeScript usage (tsconfig.json)
  • Existing analytics (search for gtag, GA, analytics)
  • Package manager (pnpm-lock.yaml, yarn.lock, package-lock.json)

Step 2: Validate Measurement ID

The Measurement ID must:

  • Start with G- (GA4 format)
  • Be followed by exactly 10 alphanumeric characters
  • Example: G-ABC1234567

If the user provides a UA- ID, inform them:

"You provided a Universal Analytics ID (UA-). GA4 uses Measurement IDs starting with 'G-'. Universal Analytics was sunset in July 2024. You'll need to create a GA4 property at analytics.google.com"

Step 3: Implementation by Framework

Next.js (App Router - app/ directory)

Create app/layout.tsx modification or create components/GoogleAnalytics.tsx:

tsx
// components/GoogleAnalytics.tsx
'use client'

import Script from 'next/script'

interface GoogleAnalyticsProps {
  measurementId: string
}

export function GoogleAnalytics({ measurementId }: GoogleAnalyticsProps) {
  return (
    <>
      <Script
        src={`https://www.googletagmanager.com/gtag/js?id=${measurementId}`}
        strategy="afterInteractive"
      />
      <Script id="google-analytics" strategy="afterInteractive">
        {`
          window.dataLayer = window.dataLayer || [];
          function gtag(){dataLayer.push(arguments);}
          gtag('js', new Date());
          gtag('config', '${measurementId}');
        `}
      </Script>
    </>
  )
}

Add to root layout:

tsx
// app/layout.tsx
import { GoogleAnalytics } from '@/components/GoogleAnalytics'

// Add inside <body> or <html>:
<GoogleAnalytics measurementId="G-XXXXXXXXXX" />

Next.js (Pages Router - pages/ directory)

Modify pages/_app.tsx:

tsx
// pages/_app.tsx
import type { AppProps } from 'next/app'
import Script from 'next/script'

const GA_MEASUREMENT_ID = process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID

export default function App({ Component, pageProps }: AppProps) {
  return (
    <>
      <Script
        src={`https://www.googletagmanager.com/gtag/js?id=${GA_MEASUREMENT_ID}`}
        strategy="afterInteractive"
      />
      <Script id="google-analytics" strategy="afterInteractive">
        {`
          window.dataLayer = window.dataLayer || [];
          function gtag(){dataLayer.push(arguments);}
          gtag('js', new Date());
          gtag('config', '${GA_MEASUREMENT_ID}');
        `}
      </Script>
      <Component {...pageProps} />
    </>
  )
}

React (Vite/CRA)

Create src/lib/analytics.ts:

typescript
// src/lib/analytics.ts
export const GA_MEASUREMENT_ID = import.meta.env.VITE_GA_MEASUREMENT_ID

declare global {
  interface Window {
    gtag: (...args: unknown[]) => void
    dataLayer: unknown[]
  }
}

export const initGA = () => {
  if (typeof window === 'undefined') return

  const script = document.createElement('script')
  script.src = `https://www.googletagmanager.com/gtag/js?id=${GA_MEASUREMENT_ID}`
  script.async = true
  document.head.appendChild(script)

  window.dataLayer = window.dataLayer || []
  window.gtag = function gtag() {
    window.dataLayer.push(arguments)
  }
  window.gtag('js', new Date())
  window.gtag('config', GA_MEASUREMENT_ID)
}

export const pageview = (url: string) => {
  window.gtag('config', GA_MEASUREMENT_ID, {
    page_path: url,
  })
}

export const event = (action: string, params?: Record<string, unknown>) => {
  window.gtag('event', action, params)
}

Initialize in src/main.tsx:

tsx
import { initGA } from './lib/analytics'

// Initialize before render
if (import.meta.env.PROD) {
  initGA()
}

Vue 3 (Vite)

Create src/plugins/analytics.ts:

typescript
// src/plugins/analytics.ts
import type { App } from 'vue'
import type { Router } from 'vue-router'

const GA_MEASUREMENT_ID = import.meta.env.VITE_GA_MEASUREMENT_ID

declare global {
  interface Window {
    gtag: (...args: unknown[]) => void
    dataLayer: unknown[]
  }
}

export const analyticsPlugin = {
  install(app: App, { router }: { router: Router }) {
    // Load gtag script
    const script = document.createElement('script')
    script.src = `https://www.googletagmanager.com/gtag/js?id=${GA_MEASUREMENT_ID}`
    script.async = true
    document.head.appendChild(script)

    window.dataLayer = window.dataLayer || []
    window.gtag = function gtag() {
      window.dataLayer.push(arguments)
    }
    window.gtag('js', new Date())
    window.gtag('config', GA_MEASUREMENT_ID)

    // Track route changes
    router.afterEach((to) => {
      window.gtag('config', GA_MEASUREMENT_ID, {
        page_path: to.fullPath,
      })
    })

    // Provide global methods
    app.config.globalProperties.$gtag = window.gtag
  }
}

Nuxt 3

Create plugins/analytics.client.ts:

typescript
// plugins/analytics.client.ts
export default defineNuxtPlugin(() => {
  const config = useRuntimeConfig()
  const measurementId = config.public.gaMeasurementId

  if (!measurementId) return

  // Load gtag
  useHead({
    script: [
      {
        src: `https://www.googletagmanager.com/gtag/js?id=${measurementId}`,
        async: true,
      },
      {
        innerHTML: `
          window.dataLayer = window.dataLayer || [];
          function gtag(){dataLayer.push(arguments);}
          gtag('js', new Date());
          gtag('config', '${measurementId}');
        `,
      },
    ],
  })

  // Track route changes
  const router = useRouter()
  router.afterEach((to) => {
    window.gtag('config', measurementId, {
      page_path: to.fullPath,
    })
  })
})

Add to nuxt.config.ts:

typescript
export default defineNuxtConfig({
  runtimeConfig: {
    public: {
      gaMeasurementId: process.env.NUXT_PUBLIC_GA_MEASUREMENT_ID,
    },
  },
})

Astro

Create src/components/Analytics.astro:

astro
---
// src/components/Analytics.astro
interface Props {
  measurementId: string
}

const { measurementId } = Astro.props
---

<script
  is:inline
  define:vars={{ measurementId }}
  src={`https://www.googletagmanager.com/gtag/js?id=${measurementId}`}
></script>

<script is:inline define:vars={{ measurementId }}>
  window.dataLayer = window.dataLayer || [];
  function gtag() {
    dataLayer.push(arguments);
  }
  gtag('js', new Date());
  gtag('config', measurementId);
</script>

Add to layout:

astro
---
import Analytics from '../components/Analytics.astro'
---
<html>
  <head>
    <Analytics measurementId="G-XXXXXXXXXX" />
  </head>
</html>

SvelteKit

Create src/lib/analytics.ts and src/routes/+layout.svelte:

typescript
// src/lib/analytics.ts
import { browser } from '$app/environment'

export const GA_MEASUREMENT_ID = import.meta.env.VITE_GA_MEASUREMENT_ID

export function initGA() {
  if (!browser) return

  const script = document.createElement('script')
  script.src = `https://www.googletagmanager.com/gtag/js?id=${GA_MEASUREMENT_ID}`
  script.async = true
  document.head.appendChild(script)

  window.dataLayer = window.dataLayer || []
  window.gtag = function gtag() {
    window.dataLayer.push(arguments)
  }
  window.gtag('js', new Date())
  window.gtag('config', GA_MEASUREMENT_ID)
}

export function trackPageview(url: string) {
  if (!browser) return
  window.gtag('config', GA_MEASUREMENT_ID, { page_path: url })
}
svelte
<!-- src/routes/+layout.svelte -->
<script lang="ts">
  import { onMount } from 'svelte'
  import { page } from '$app/stores'
  import { initGA, trackPageview } from '$lib/analytics'

  onMount(() => {
    initGA()
  })

  $: if ($page.url.pathname) {
    trackPageview($page.url.pathname)
  }
</script>

<slot />

Plain HTML

Add to <head>:

html
<!-- Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());
  gtag('config', 'G-XXXXXXXXXX');
</script>

Step 4: Environment Variables

Create or update .env / .env.local:

bash
# For Next.js
NEXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXXXX

# For Vite (React/Vue/Svelte)
VITE_GA_MEASUREMENT_ID=G-XXXXXXXXXX

# For Nuxt
NUXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXXXX

Add to .env.example if it exists (without the actual ID):

bash
# Google Analytics 4 Measurement ID
NEXT_PUBLIC_GA_MEASUREMENT_ID=G-XXXXXXXXXX

IMPORTANT: Add .env.local to .gitignore if not already present.

Step 5: Event Tracking Helpers (if --events flag)

Create a comprehensive events utility:

typescript
// lib/analytics-events.ts

/**
 * GA4 Event Tracking Utilities
 *
 * Recommended events: https://support.google.com/analytics/answer/9267735
 */

type GTagEvent = {
  action: string
  category?: string
  label?: string
  value?: number
  [key: string]: unknown
}

// Core event function
export const trackEvent = ({ action, category, label, value, ...rest }: GTagEvent) => {
  if (typeof window === 'undefined' || !window.gtag) return

  window.gtag('event', action, {
    event_category: category,
    event_label: label,
    value,
    ...rest,
  })
}

// Engagement events
export const trackClick = (elementName: string, location?: string) => {
  trackEvent({
    action: 'click',
    category: 'engagement',
    label: elementName,
    click_location: location,
  })
}

export const trackScroll = (percentage: number) => {
  trackEvent({
    action: 'scroll',
    category: 'engagement',
    value: percentage,
  })
}

// Conversion events
export const trackSignUp = (method: string) => {
  trackEvent({
    action: 'sign_up',
    method,
  })
}

export const trackLogin = (method: string) => {
  trackEvent({
    action: 'login',
    method,
  })
}

export const trackPurchase = (params: {
  transactionId: string
  value: number
  currency: string
  items?: Array<{
    itemId: string
    itemName: string
    price: number
    quantity: number
  }>
}) => {
  trackEvent({
    action: 'purchase',
    transaction_id: params.transactionId,
    value: params.value,
    currency: params.currency,
    items: params.items,
  })
}

// Content events
export const trackSearch = (searchTerm: string) => {
  trackEvent({
    action: 'search',
    search_term: searchTerm,
  })
}

export const trackShare = (method: string, contentType: string, itemId: string) => {
  trackEvent({
    action: 'share',
    method,
    content_type: contentType,
    item_id: itemId,
  })
}

// Form events
export const trackFormStart = (formName: string) => {
  trackEvent({
    action: 'form_start',
    form_name: formName,
  })
}

export const trackFormSubmit = (formName: string, success: boolean) => {
  trackEvent({
    action: 'form_submit',
    form_name: formName,
    success,
  })
}

// Error tracking
export const trackError = (errorMessage: string, errorLocation?: string) => {
  trackEvent({
    action: 'exception',
    description: errorMessage,
    fatal: false,
    error_location: errorLocation,
  })
}

// Custom event builder for flexibility
export const createCustomEvent = (eventName: string) => {
  return (params?: Record<string, unknown>) => {
    trackEvent({
      action: eventName,
      ...params,
    })
  }
}

Step 6: Cookie Consent Integration (if --consent flag)

Create a consent-aware wrapper:

typescript
// lib/analytics-consent.ts

type ConsentState = 'granted' | 'denied'

interface ConsentConfig {
  analytics_storage: ConsentState
  ad_storage: ConsentState
  ad_user_data: ConsentState
  ad_personalization: ConsentState
}

const CONSENT_COOKIE = 'analytics_consent'

// Initialize with consent mode
export const initWithConsent = (measurementId: string) => {
  if (typeof window === 'undefined') return

  // Set default consent state (denied until user consents)
  window.gtag('consent', 'default', {
    analytics_storage: 'denied',
    ad_storage: 'denied',
    ad_user_data: 'denied',
    ad_personalization: 'denied',
    wait_for_update: 500, // Wait for consent banner
  })

  // Load gtag
  const script = document.createElement('script')
  script.src = `https://www.googletagmanager.com/gtag/js?id=${measurementId}`
  script.async = true
  document.head.appendChild(script)

  window.dataLayer = window.dataLayer || []
  window.gtag = function gtag() {
    window.dataLayer.push(arguments)
  }
  window.gtag('js', new Date())
  window.gtag('config', measurementId)

  // Check for existing consent
  const savedConsent = getCookie(CONSENT_COOKIE)
  if (savedConsent) {
    updateConsent(JSON.parse(savedConsent))
  }
}

// Update consent when user makes a choice
export const updateConsent = (consent: Partial<ConsentConfig>) => {
  if (typeof window === 'undefined' || !window.gtag) return

  const consentState: ConsentConfig = {
    analytics_storage: consent.analytics_storage || 'denied',
    ad_storage: consent.ad_storage || 'denied',
    ad_user_data: consent.ad_user_data || 'denied',
    ad_personalization: consent.ad_personalization || 'denied',
  }

  window.gtag('consent', 'update', consentState)

  // Save to cookie
  setCookie(CONSENT_COOKIE, JSON.stringify(consentState), 365)
}

// Convenience functions
export const acceptAll = () => {
  updateConsent({
    analytics_storage: 'granted',
    ad_storage: 'granted',
    ad_user_data: 'granted',
    ad_personalization: 'granted',
  })
}

export const acceptAnalyticsOnly = () => {
  updateConsent({
    analytics_storage: 'granted',
    ad_storage: 'denied',
    ad_user_data: 'denied',
    ad_personalization: 'denied',
  })
}

export const denyAll = () => {
  updateConsent({
    analytics_storage: 'denied',
    ad_storage: 'denied',
    ad_user_data: 'denied',
    ad_personalization: 'denied',
  })
}

// Cookie utilities
function setCookie(name: string, value: string, days: number) {
  const date = new Date()
  date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000)
  document.cookie = `${name}=${value};expires=${date.toUTCString()};path=/;SameSite=Lax`
}

function getCookie(name: string): string | null {
  const match = document.cookie.match(new RegExp(`(^| )${name}=([^;]+)`))
  return match ? match[2] : null
}

Step 7: Debug Mode (if --debug flag)

Add debug configuration:

typescript
// For development, enable debug mode
if (process.env.NODE_ENV === 'development') {
  window.gtag('config', 'G-XXXXXXXXXX', {
    debug_mode: true,
  })
}

Also recommend installing the Google Analytics Debugger Chrome extension.

Step 8: TypeScript Declarations

Create types/gtag.d.ts if using TypeScript:

typescript
// types/gtag.d.ts
declare global {
  interface Window {
    gtag: Gtag.Gtag
    dataLayer: object[]
  }
}

declare namespace Gtag {
  interface Gtag {
    (command: 'config', targetId: string, config?: ConfigParams): void
    (command: 'set', targetId: string, config: ConfigParams): void
    (command: 'set', config: ConfigParams): void
    (command: 'js', date: Date): void
    (command: 'event', eventName: string, eventParams?: EventParams): void
    (command: 'consent', consentArg: 'default' | 'update', consentParams: ConsentParams): void
    (...args: unknown[]): void
  }

  interface ConfigParams {
    page_title?: string
    page_location?: string
    page_path?: string
    send_page_view?: boolean
    debug_mode?: boolean
    [key: string]: unknown
  }

  interface EventParams {
    event_category?: string
    event_label?: string
    value?: number
    [key: string]: unknown
  }

  interface ConsentParams {
    analytics_storage?: 'granted' | 'denied'
    ad_storage?: 'granted' | 'denied'
    ad_user_data?: 'granted' | 'denied'
    ad_personalization?: 'granted' | 'denied'
    wait_for_update?: number
  }
}

export {}

Step 9: Verification Checklist

After implementation, verify:

  1. Measurement ID is correct format (G-XXXXXXXXXX)
  2. Script loads in production (check Network tab)
  3. Real-time reports show activity in GA4 dashboard
  4. Page views are tracked on navigation
  5. No console errors related to gtag
  6. Environment variables are not committed to git
  7. TypeScript has no type errors (if applicable)

Step 10: Summary Output

After completing setup, provide the user with:

  1. Files created/modified (list them)
  2. Environment variables needed (with example values)
  3. Next steps:
    • Add the Measurement ID to environment variables
    • Deploy and verify in GA4 Real-time reports
    • Set up conversions in GA4 dashboard
    • Consider adding custom events for key user actions

Common Issues & Solutions

"gtag is not defined"

  • Script hasn't loaded yet; ensure async loading is handled

No data in GA4

  • Check if ad blockers are preventing tracking
  • Verify Measurement ID is correct
  • Check browser console for errors

Double page views

  • SPA router sending duplicate events; implement deduplication

GDPR Compliance

  • Always implement consent mode for EU users
  • Use the --consent flag to add consent management

Bundled files

The model reads these on demand while the skill is loaded. They are exposed as readable files and are never executed.

Frequently asked questions

What does the Add Analytics AI skill do?

Add Google Analytics 4 tracking to any project. Detects framework, adds tracking code, sets up events, and configures privacy settings.

Why use Add Analytics on TypingMind?

Because you install it once and use it with any model. Add Analytics is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Add Analytics in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/LeoYeAI/openclaw-master-skills/tree/main/skills/add-analytics. TypingMind reads its SKILL.md and bundles its files and installs it as a skill you can enable per chat.

Which AI models can use Add Analytics?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Add Analytics?

As many as you like. As long as a model supports skills, you can use Add Analytics with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Add Analytics AI skill free?

Yes. It is published on GitHub by LeoYeAI under the MIT license. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇