From 5c3acc9edb19ffbb1084ab412694e15debd1061b Mon Sep 17 00:00:00 2001 From: Dan Date: Tue, 13 May 2025 23:46:00 -0400 Subject: [PATCH 1/2] feat: add rybbit analytics to registry --- .../scripts/analytics/rybbit-analytics.md | 164 ++++++++++++++++++ src/registry.ts | 9 + src/runtime/registry/rybbit-analytics.ts | 47 +++++ src/runtime/types.ts | 2 + 4 files changed, 222 insertions(+) create mode 100644 docs/content/scripts/analytics/rybbit-analytics.md create mode 100644 src/runtime/registry/rybbit-analytics.ts diff --git a/docs/content/scripts/analytics/rybbit-analytics.md b/docs/content/scripts/analytics/rybbit-analytics.md new file mode 100644 index 00000000..bf2ac98a --- /dev/null +++ b/docs/content/scripts/analytics/rybbit-analytics.md @@ -0,0 +1,164 @@ +--- +title: Rybbit Analytics +description: Use Rybbit Analytics in your Nuxt app. +links: + - label: Source + icon: i-simple-icons-github + to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/rybbit.ts + size: xs +--- + +[Rybbit Analytics](https://www.rybbit.io/) is a privacy-focused analytics solution for tracking user activity on your website without compromising your users' privacy. + +The simplest way to load Rybbit Analytics globally in your Nuxt App is to use Nuxt config. Alternatively you can directly +use the [useScriptRybbitAnalytics](#useScriptRybbitAnalytics) composable. + +## Loading Globally + +If you don't plan to send custom events you can use the [Environment overrides](https://nuxt.com/docs/getting-started/configuration#environment-overrides) to +disable the script in development. + +::code-group + +```ts [Always enabled] +export default defineNuxtConfig({ + scripts: { + registry: { + rybbitAnalytics: { + siteId: 'YOUR_SITE_ID' + } + } + } +}) +``` + +```ts [Production only] +export default defineNuxtConfig({ + $production: { + scripts: { + registry: { + rybbitAnalytics: { + siteId: 'YOUR_SITE_ID', + } + } + } + } +}) +``` + +```ts [Environment Variables] +export default defineNuxtConfig({ + scripts: { + registry: { + rybbitAnalytics: true, + } + }, + // you need to provide a runtime config to access the environment variables + runtimeConfig: { + public: { + scripts: { + rybbitAnalytics: { + // .env + // NUXT_PUBLIC_SCRIPTS_RYBBIT_ANALYTICS_SITE_ID= + siteId: '' + }, + }, + }, + }, +}) +``` + +:: + +## useScriptRybbitAnalytics + +The `useScriptRybbitAnalytics` composable lets you have fine-grain control over when and how Rybbit Analytics is loaded on your site. + +```ts +const rybbit = useScriptRybbitAnalytics({ + siteId: 'YOUR_SITE_ID' +}) +``` + +Please follow the [Registry Scripts](/docs/guides/registry-scripts) guide to learn more about advanced usage. + +### Self-hosted Rybbit Analytics + +If you are using a self-hosted version of Rybbit Analytics, you can provide a custom script source: + +```ts +useScriptRybbitAnalytics({ + scriptInput: { + src: 'https://your-rybbit-instance.com/api/script.js' + } + siteId: 'YOUR_SITE_ID' +}) +``` + +### RybbitAnalyticsApi + +```ts +export interface RybbitAnalyticsApi { + pageview: () => void + event: (eventName: string, properties?: Record) => void +} +``` + +### Config Schema + +You must provide the options when setting up the script for the first time. + +```ts +export const RybbitAnalyticsOptions = object({ + siteId: string(), // required + trackSpa: optional(boolean()), + trackQuery: optional(boolean()), + skipPatterns: optional(array(string())), + maskPatterns: optional(array(string())), + debounce: optional(number()) +}) +``` + +#### Configuration Options + +- `siteId` (required): Your Rybbit Analytics site ID +- `trackSpa`: Set to `false` to disable automatic pageview tracking for single page applications +- `trackQuery`: Set to `false` to disable tracking of URL query strings +- `skipPatterns`: Array of URL path patterns to ignore +- `maskPatterns`: Array of URL path patterns to mask for privacy +- `debounce`: Delay in milliseconds before tracking a pageview after URL changes + +## Example + +Using Rybbit Analytics only in production while tracking custom events. + +::code-group + +```vue [EventTracking.vue] + + + +``` + +:: \ No newline at end of file diff --git a/src/registry.ts b/src/registry.ts index 3c8fd83f..c4615b2f 100644 --- a/src/registry.ts +++ b/src/registry.ts @@ -58,6 +58,15 @@ export async function registry(resolve?: (path: string, opts?: ResolvePathOption from: await resolve('./runtime/registry/matomo-analytics'), }, }, + { + label: 'Rybbit Analytics', + category: 'analytics', + logo: `https://www.rybbit.io/rybbit.png`, + import: { + name: 'useScriptRybbitAnalytics', + from: await resolve('./runtime/registry/rybbit-analytics'), + }, + }, { label: 'Segment', scriptBundling: (options?: SegmentInput) => { diff --git a/src/runtime/registry/rybbit-analytics.ts b/src/runtime/registry/rybbit-analytics.ts new file mode 100644 index 00000000..4710674e --- /dev/null +++ b/src/runtime/registry/rybbit-analytics.ts @@ -0,0 +1,47 @@ +import { useRegistryScript } from '../utils' +import { array, boolean, number, object, optional, string } from '#nuxt-scripts-validator' +import type { RegistryScriptInput } from '#nuxt-scripts/types' + +export const RybbitAnalyticsOptions = object({ + siteId: string(), // required + trackSpa: optional(boolean()), + trackQuery: optional(boolean()), + skipPatterns: optional(array(string())), + maskPatterns: optional(array(string())), + debounce: optional(number()), +}) + +export type RybbitAnalyticsInput = RegistryScriptInput + +export interface RybbitAnalyticsApi { + pageview: () => void + event: (eventName: string, properties?: Record) => void +} + +declare global { + interface Window { + rybbit: RybbitAnalyticsApi + } +} + +export function useScriptRybbitAnalytics(_options?: RybbitAnalyticsInput) { + return useRegistryScript('rybbitAnalytics', (options) => { + return { + scriptInput: { + 'src': 'https://app.rybbit.io/api/script.js', + 'data-site-id': options?.siteId, + 'data-track-spa': options?.trackSpa, + 'data-track-query': options?.trackQuery, + 'data-skip-patterns': options?.skipPatterns ? JSON.stringify(options.skipPatterns) : undefined, + 'data-mask-patterns': options?.maskPatterns ? JSON.stringify(options.maskPatterns) : undefined, + 'data-debounce': options?.debounce ? options.debounce.toString() : undefined, + }, + schema: import.meta.dev ? RybbitAnalyticsOptions : undefined, + scriptOptions: { + use() { + return { rybbit: window.rybbit } + }, + }, + } + }, _options) +} diff --git a/src/runtime/types.ts b/src/runtime/types.ts index bf9dd81a..49c26dc4 100644 --- a/src/runtime/types.ts +++ b/src/runtime/types.ts @@ -27,6 +27,7 @@ import type { CrispInput } from './registry/crisp' import type { GoogleAnalyticsInput } from './registry/google-analytics' import type { GoogleTagManagerInput } from './registry/google-tag-manager' import type { UmamiAnalyticsInput } from './registry/umami-analytics' +import type { RybbitAnalyticsInput } from './registry/rybbit-analytics' import { object } from '#nuxt-scripts-validator' export type WarmupStrategy = false | 'preload' | 'preconnect' | 'dns-prefetch' @@ -139,6 +140,7 @@ export interface ScriptRegistry { hotjar?: HotjarInput intercom?: IntercomInput matomoAnalytics?: MatomoAnalyticsInput + rybbitAnalytics?: RybbitAnalyticsInput segment?: SegmentInput stripe?: StripeInput xPixel?: XPixelInput From 98930ca4476f43a54dadfc1f403f5262e71f39fa Mon Sep 17 00:00:00 2001 From: Dan Date: Fri, 4 Jul 2025 15:23:14 -0400 Subject: [PATCH 2/2] feat(rybbit): add new tracking configuration options --- docs/content/scripts/analytics/rybbit-analytics.md | 14 +++++++++++++- src/runtime/registry/rybbit-analytics.ts | 12 ++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/docs/content/scripts/analytics/rybbit-analytics.md b/docs/content/scripts/analytics/rybbit-analytics.md index 9ecf2566..1f56644a 100644 --- a/docs/content/scripts/analytics/rybbit-analytics.md +++ b/docs/content/scripts/analytics/rybbit-analytics.md @@ -141,22 +141,34 @@ You must provide the options when setting up the script for the first time. ```ts export const RybbitAnalyticsOptions = object({ siteId: union([string(), number()]), // required + autoTrackPageview: optional(boolean()), trackSpa: optional(boolean()), trackQuery: optional(boolean()), + trackOutbound: optional(boolean()), + trackErrors: optional(boolean()), + sessionReplay: optional(boolean()), + webVitals: optional(boolean()), skipPatterns: optional(array(string())), maskPatterns: optional(array(string())), - debounce: optional(number()) + debounce: optional(number()), + apiKey: optional(string()), }) ``` #### Configuration Options - `siteId` (required): Your Rybbit Analytics site ID +`autoTrackPageview`: Set to `false` to disable automatic tracking of the initial pageview when the script loads. You will need to manually call the pageview function to track pageviews. Default: `true` - `trackSpa`: Set to `false` to disable automatic pageview tracking for single page applications - `trackQuery`: Set to `false` to disable tracking of URL query strings +- `trackOutbound`: Set to `false` to disable automatic tracking of outbound link clicks. Default: `true` +- `trackErrors`: Set to `true` to enable automatic tracking of JavaScript errors and unhandled promise rejections. Only tracks errors from the same origin to avoid noise from third-party scripts. Default: `false` +- `sessionReplay`: Set to `true` to enable session replay recording. Captures user interactions, mouse movements, and DOM changes for debugging and user experience analysis. Default: `false` +- `webVitals`: Set to `true` to enable Web Vitals performance metrics collection (LCP, CLS, INP, FCP, TTFB). Web Vitals are disabled by default to reduce script size and network requests. Default: `false` - `skipPatterns`: Array of URL path patterns to ignore - `maskPatterns`: Array of URL path patterns to mask for privacy - `debounce`: Delay in milliseconds before tracking a pageview after URL changes +- `apiKey`: API key for tracking from localhost during development. Bypasses origin validation for self-hosted Rybbit Analytics instances ## Example diff --git a/src/runtime/registry/rybbit-analytics.ts b/src/runtime/registry/rybbit-analytics.ts index e28bbea2..c66eb576 100644 --- a/src/runtime/registry/rybbit-analytics.ts +++ b/src/runtime/registry/rybbit-analytics.ts @@ -4,11 +4,17 @@ import type { RegistryScriptInput } from '#nuxt-scripts/types' export const RybbitAnalyticsOptions = object({ siteId: union([string(), number()]), // required + autoTrackPageview: optional(boolean()), trackSpa: optional(boolean()), trackQuery: optional(boolean()), + trackOutbound: optional(boolean()), + trackErrors: optional(boolean()), + sessionReplay: optional(boolean()), + webVitals: optional(boolean()), skipPatterns: optional(array(string())), maskPatterns: optional(array(string())), debounce: optional(number()), + apiKey: optional(string()), }) export type RybbitAnalyticsInput = RegistryScriptInput @@ -60,11 +66,17 @@ export function useScriptRybbitAnalytics(_options? scriptInput: { 'src': 'https://app.rybbit.io/api/script.js', 'data-site-id': String(options?.siteId), + 'data-auto-track-pageview': options?.autoTrackPageview, 'data-track-spa': options?.trackSpa, 'data-track-query': options?.trackQuery, + 'data-track-outbound': options?.trackOutbound, + 'data-track-errors': options?.trackErrors, + 'data-session-replay': options?.sessionReplay, + 'data-web-vitals': options?.webVitals, 'data-skip-patterns': options?.skipPatterns ? JSON.stringify(options.skipPatterns) : undefined, 'data-mask-patterns': options?.maskPatterns ? JSON.stringify(options.maskPatterns) : undefined, 'data-debounce': options?.debounce ? options.debounce.toString() : undefined, + 'data-api-key': options?.apiKey, }, schema: import.meta.dev ? RybbitAnalyticsOptions : undefined, scriptOptions: {