Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
169 changes: 169 additions & 0 deletions docs/content/scripts/analytics/databuddy-analytics.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
---
title: Databuddy Analytics
description: Use Databuddy Analytics in your Nuxt app.
links:
- label: Source
icon: i-simple-icons-github
to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/databuddy-analytics.ts
size: xs
---

[Databuddy](https://www.databuddy.cc/) is a privacy-first analytics platform focused on performance and minimal data collection.

Use the registry to easily inject the Databuddy CDN script with sensible defaults, or call the composable for fine-grain control.

## Loading Globally

The simplest way to enable Databuddy globally is via `nuxt.config` (or module config). You can use environment overrides to only enable in production.

::code-group

```ts [Always enabled]
export default defineNuxtConfig({
scripts: {
registry: {
databuddyAnalytics: {
clientId: 'YOUR_CLIENT_ID'
}
}
}
})
```

```ts [Production only]
export default defineNuxtConfig({
$production: {
scripts: {
registry: {
databuddyAnalytics: {
clientId: 'YOUR_CLIENT_ID'
}
}
}
}
})
```

```ts [Environment Variables]
export default defineNuxtConfig({
scripts: {
registry: {
databuddyAnalytics: true,
}
},
runtimeConfig: {
public: {
scripts: {
databuddyAnalytics: {
// .env
// NUXT_PUBLIC_SCRIPTS_DATABUDDY_ANALYTICS_CLIENT_ID=<your-client-id>
clientId: ''
},
},
},
},
})
```

::

## useScriptDatabuddyAnalytics

The `useScriptDatabuddyAnalytics` composable gives you control over when and how Databuddy is loaded.

```ts
const db = useScriptDatabuddyAnalytics({
clientId: 'YOUR_CLIENT_ID',
trackWebVitals: true,
trackErrors: true,
enableBatching: true,
})
```

The composable returns the script proxy (when available). You can interact with the global API via `db` or `window.db` / `window.databuddy`.

### CDN / Self-hosted

By default the registry injects `https://cdn.databuddy.cc/databuddy.js`. If you host the script yourself, pass `scriptUrl` in options to override the `src`.

```ts
useScriptDatabuddyAnalytics({
scriptInput: { src: 'https://my-host/databuddy.js' },
clientId: 'YOUR_CLIENT_ID'
})
```

### DatabuddyAnalyticsApi

```ts
export interface DatabuddyAnalyticsApi {
track: (eventName: string, properties?: Record<string, any>) => Promise<any> | any | void
screenView: (path?: string, properties?: Record<string, any>) => void
setGlobalProperties: (properties: Record<string, any>) => void
trackCustomEvent: (eventName: string, properties?: Record<string, any>) => void
clear: () => void
flush: () => void
}
```

### Config Schema

You must provide a `clientId` when configuring the registry for the first time. The registry supports a large set of Databuddy options which are passed to the script via `data-` attributes.

```ts
export const DatabuddyAnalyticsOptions = object({
clientId: string(),
scriptUrl: optional(string()),
apiUrl: optional(string()),
disabled: optional(boolean()),
trackScreenViews: optional(boolean()),
trackPerformance: optional(boolean()),
trackSessions: optional(boolean()),
trackWebVitals: optional(boolean()),
trackErrors: optional(boolean()),
trackOutgoingLinks: optional(boolean()),
trackScrollDepth: optional(boolean()),
trackEngagement: optional(boolean()),
trackInteractions: optional(boolean()),
trackAttributes: optional(boolean()),
trackHashChanges: optional(boolean()),
trackExitIntent: optional(boolean()),
trackBounceRate: optional(boolean()),
enableBatching: optional(boolean()),
batchSize: optional(number()),
batchTimeout: optional(number()),
enableRetries: optional(boolean()),
maxRetries: optional(number()),
initialRetryDelay: optional(number()),
samplingRate: optional(number()),
sdk: optional(string()),
sdkVersion: optional(string()),
enableObservability: optional(boolean()),
observabilityService: optional(string()),
observabilityEnvironment: optional(string()),
observabilityVersion: optional(string()),
enableLogging: optional(boolean()),
enableTracing: optional(boolean()),
enableErrorTracking: optional(boolean()),
})
```

## Example

Track a custom event using the composable proxy (noop in SSR/development):

::code-group

```vue [EventButton.vue]
<script setup lang="ts">
const { proxy } = useScriptDatabuddyAnalytics({ clientId: 'YOUR_CLIENT_ID' })

function sendEvent() {
proxy?.track('signup_completed', { plan: 'pro' })
}
</script>

<template>
<button @click="sendEvent">Send Event</button>
</template>
```
9 changes: 9 additions & 0 deletions src/registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,15 @@ export async function registry(resolve?: (path: string, opts?: ResolvePathOption
from: await resolve('./runtime/registry/rybbit-analytics'),
},
},
{
label: 'Databuddy Analytics',
category: 'analytics',
logo: `<svg xmlns="http://www.w3.org/2000/svg" width="56.5" height="32" viewBox="0 0 8 8" shape-rendering="crispEdges"><path d="M0 0h8v8H0z"/><path fill="#fff" d="M1 1h1v6H1zm1 0h4v1H2zm4 1h1v1H6zm0 1h1v1H6zm0 1h1v1H6zm0 1h1v1H6zM2 6h4v1H2zm1-3h1v1H3zm1 1h1v1H4z"/></svg>`,
import: {
name: 'useScriptDatabuddyAnalytics',
from: await resolve('./runtime/registry/databuddy-analytics'),
},
},
{
label: 'Segment',
scriptBundling: (options?: SegmentInput) => {
Expand Down
160 changes: 160 additions & 0 deletions src/runtime/registry/databuddy-analytics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
import { useRegistryScript } from '../utils'
import { object, optional, string, boolean, number } from '#nuxt-scripts-validator'
import type { RegistryScriptInput } from '#nuxt-scripts/types'

// Options schema based on https://www.databuddy.cc/docs/sdk
export const DatabuddyAnalyticsOptions = object({
// Required
clientId: string(),

// Advanced
scriptUrl: optional(string()), // defaults to https://cdn.databuddy.cc/databuddy.js
apiUrl: optional(string()), // defaults to https://basket.databuddy.cc
disabled: optional(boolean()),

// Core tracking (enabled by default by SDK)
trackScreenViews: optional(boolean()),
trackPerformance: optional(boolean()),
trackSessions: optional(boolean()),

// Optional tracking
trackWebVitals: optional(boolean()),
trackErrors: optional(boolean()),
trackOutgoingLinks: optional(boolean()),
trackScrollDepth: optional(boolean()),
trackEngagement: optional(boolean()),
trackInteractions: optional(boolean()),
trackAttributes: optional(boolean()),
trackHashChanges: optional(boolean()),
trackExitIntent: optional(boolean()),
trackBounceRate: optional(boolean()),

// Performance options
enableBatching: optional(boolean()),
batchSize: optional(number()),
batchTimeout: optional(number()),
enableRetries: optional(boolean()),
maxRetries: optional(number()),
initialRetryDelay: optional(number()),
samplingRate: optional(number()),

// SDK metadata
sdk: optional(string()),
sdkVersion: optional(string()),

// Observability & logging (accepted by SDK config)
enableObservability: optional(boolean()),
observabilityService: optional(string()),
observabilityEnvironment: optional(string()),
observabilityVersion: optional(string()),
enableLogging: optional(boolean()),
enableTracing: optional(boolean()),
enableErrorTracking: optional(boolean()),
})

export type DatabuddyAnalyticsInput = RegistryScriptInput<typeof DatabuddyAnalyticsOptions, false>

export interface DatabuddyAnalyticsApi {
/**
* Track a custom event.
* @param eventName Name of the event (use snake_case)
* @param properties Optional event properties
*/
track: (eventName: string, properties?: Record<string, any>) => Promise<any> | any | void

/**
* Manually record a page / screen view. Useful for SPA route changes.
* @param path Optional path to record (defaults to current location)
* @param properties Optional additional properties for the screen view
*/
screenView: (path?: string, properties?: Record<string, any>) => void

/**
* Set properties that will be attached to all future events (e.g. user_id).
* @param properties Key/value map of properties to attach globally
*/
setGlobalProperties: (properties: Record<string, any>) => void

/**
* Track a custom event alias (compatibility helper present on the global)
* @param eventName Name of the event
* @param properties Optional event properties
*/
trackCustomEvent: (eventName: string, properties?: Record<string, any>) => void

/**
* Clears session and anonymous identifiers (useful on logout).
*/
clear: () => void

/**
* Force immediate sending of any queued/batched events.
*/
flush: () => void
}

declare global {
interface Window {
databuddy?: DatabuddyAnalyticsApi
db?: DatabuddyAnalyticsApi
}
}

export function useScriptDatabuddyAnalytics<T extends DatabuddyAnalyticsApi>(_options?: DatabuddyAnalyticsInput) {
return useRegistryScript<T, typeof DatabuddyAnalyticsOptions>('databuddyAnalytics', (options) => {
return {
scriptInput: {
// Default CDN script, can be overridden via scriptUrl
'src': options?.scriptUrl || 'https://cdn.databuddy.cc/databuddy.js',
'data-client-id': options.clientId,
// Advanced
'data-api-url': options?.apiUrl,
'data-disabled': options?.disabled,
// Core
'data-track-screen-views': options?.trackScreenViews,
'data-track-performance': options?.trackPerformance,
'data-track-sessions': options?.trackSessions,
// Optional
'data-track-web-vitals': options?.trackWebVitals,
'data-track-errors': options?.trackErrors,
'data-track-outgoing-links': options?.trackOutgoingLinks,
'data-track-scroll-depth': options?.trackScrollDepth,
'data-track-engagement': options?.trackEngagement,
'data-track-interactions': options?.trackInteractions,
'data-track-attributes': options?.trackAttributes,
'data-track-hash-changes': options?.trackHashChanges,
'data-track-exit-intent': options?.trackExitIntent,
'data-track-bounce-rate': options?.trackBounceRate,
// Performance tuning
'data-enable-batching': options?.enableBatching,
'data-batch-size': options?.batchSize,
'data-batch-timeout': options?.batchTimeout,
'data-enable-retries': options?.enableRetries,
'data-max-retries': options?.maxRetries,
'data-initial-retry-delay': options?.initialRetryDelay,
'data-sampling-rate': options?.samplingRate,
// SDK meta
'data-sdk': options?.sdk,
'data-sdk-version': options?.sdkVersion,
// Observability & logging
'data-enable-observability': options?.enableObservability,
'data-observability-service': options?.observabilityService,
'data-observability-environment': options?.observabilityEnvironment,
'data-observability-version': options?.observabilityVersion,
'data-enable-logging': options?.enableLogging,
'data-enable-tracing': options?.enableTracing,
'data-enable-error-tracking': options?.enableErrorTracking,
},
schema: import.meta.dev ? DatabuddyAnalyticsOptions : undefined,
scriptOptions: {
use() {
if (typeof window === 'undefined') {
return null as unknown as T
}
// Prefer the lightweight proxy (db) if available, else raw tracker instance
return (window.db || window.databuddy || null) as unknown as T
},
},
}
}, _options)
}
Loading