Skip to content

Commit ebcb328

Browse files
authored
feat: add umami analytics to registry (#348)
1 parent aab8428 commit ebcb328

File tree

4 files changed

+242
-0
lines changed

4 files changed

+242
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
---
2+
title: Umami Analytics
3+
description: Use Umami Analytics in your Nuxt app.
4+
links:
5+
- label: Source
6+
icon: i-simple-icons-github
7+
to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/umami-analytics.ts
8+
size: xs
9+
---
10+
11+
[Umami](https://umami.is/) collects all the metrics you care about to help you make better decisions.
12+
13+
The simplest way to load Umami Analytics globally in your Nuxt App is to use Nuxt config. Alternatively you can directly
14+
use the [useScriptUmamiAnalytics](#useScriptUmamiAnalytics) composable.
15+
16+
## Loading Globally
17+
18+
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
19+
disable the script in development.
20+
21+
::code-group
22+
23+
```ts [Always enabled]
24+
export default defineNuxtConfig({
25+
scripts: {
26+
registry: {
27+
umamiAnalytics: {
28+
websiteId: 'YOUR_WEBSITE_ID'
29+
}
30+
}
31+
}
32+
})
33+
```
34+
35+
```ts [Production only]
36+
export default defineNuxtConfig({
37+
$production: {
38+
scripts: {
39+
registry: {
40+
umamiAnalytics: {
41+
websiteId: 'YOUR_WEBSITE_ID',
42+
}
43+
}
44+
}
45+
}
46+
})
47+
```
48+
49+
```ts [Environment Variables]
50+
export default defineNuxtConfig({
51+
scripts: {
52+
registry: {
53+
umamiAnalytics: true,
54+
}
55+
},
56+
// you need to provide a runtime config to access the environment variables
57+
runtimeConfig: {
58+
public: {
59+
scripts: {
60+
umamiAnalytics: {
61+
// .env
62+
// NUXT_PUBLIC_SCRIPTS_UMAMI_ANALYTICS_WEBSITE_ID=<your websiteId>
63+
websiteId: ''
64+
},
65+
},
66+
},
67+
},
68+
})
69+
```
70+
71+
::
72+
73+
## useScriptUmamiAnalytics
74+
75+
The `useScriptUmamiAnalytics` composable lets you have fine-grain control over when and how Umami Analytics is loaded on your site.
76+
77+
```ts
78+
const plausible = useScriptUmamiAnalytics({
79+
websiteId: 'YOUR_WEBSITE_ID'
80+
})
81+
```
82+
83+
Please follow the [Registry Scripts](/docs/guides/registry-scripts) guide to learn more about advanced usage.
84+
85+
### Self-hosted Umami
86+
87+
If you are using a self-hosted version of Umami, you will need to provide an explicit src for the script so that
88+
the API events are sent to the correct endpoint.
89+
90+
```ts
91+
useScriptUmamiAnalytics({
92+
scriptInput: {
93+
src: 'https://my-self-hosted/script.js'
94+
}
95+
})
96+
```
97+
98+
### UmamiAnalyticsApi
99+
100+
```ts
101+
export interface UmamiAnalyticsApi {
102+
track: ((payload?: Record<string, any>) => void) &((event_name: string, event_data: Record<string, any>) => void)
103+
identify: (session_data?: Record<string, any>) => void
104+
}
105+
```
106+
107+
### Config Schema
108+
109+
You must provide the options when setting up the script for the first time.
110+
111+
```ts
112+
export const UmamiAnalyticsOptions = object({
113+
websiteId: string(), // required
114+
/**
115+
* By default, Umami will send data to wherever the script is located.
116+
* You can override this to send data to another location.
117+
*/
118+
hostUrl: optional(string()),
119+
/**
120+
* By default, Umami tracks all pageviews and events for you automatically.
121+
* You can disable this behavior and track events yourself using the tracker functions.
122+
* https://umami.is/docs/tracker-functions
123+
*/
124+
autoTrack: optional(boolean()),
125+
/**
126+
* If you want the tracker to only run on specific domains, you can add them to your tracker script.
127+
* This is a comma delimited list of domain names.
128+
* Helps if you are working in a staging/development environment.
129+
*/
130+
domains: optional(array(string())),
131+
/**
132+
* If you want the tracker to collect events under a specific tag.
133+
* Events can be filtered in the dashboard by a specific tag.
134+
*/
135+
tag: optional(string()),
136+
})
137+
```
138+
139+
## Example
140+
141+
Using Umami Analytics only in production while using `track` to send a conversion event.
142+
143+
::code-group
144+
145+
```vue [ConversionButton.vue]
146+
<script setup lang="ts">
147+
const { track } = useScriptUmamiAnalytics()
148+
149+
// noop in development, ssr
150+
// just works in production, client
151+
track('event', { name: 'conversion-step' })
152+
153+
function sendConversion() {
154+
track('event', { name: 'conversion' })
155+
}
156+
</script>
157+
158+
<template>
159+
<div>
160+
<button @click="sendConversion">
161+
Send Conversion
162+
</button>
163+
</div>
164+
</template>
165+
```
166+
167+
::

src/registry.ts

+9
Original file line numberDiff line numberDiff line change
@@ -252,5 +252,14 @@ export const registry: (resolve?: (s: string) => string) => RegistryScripts = (r
252252
return withQuery('https://www.googletagmanager.com/gtag/js', { id: options?.id, l: options?.l })
253253
},
254254
},
255+
{
256+
label: 'Umami Analytics',
257+
category: 'analytics',
258+
logo: `<svg xmlns="http://www.w3.org/2000/svg" width="96" height="96" viewBox="0 0 24 24"><path fill="currentColor" d="M2.203 8.611H.857a.845.845 0 0 0-.841.841v.858a13 13 0 0 0-.016.6c0 6.627 5.373 12 12 12c6.527 0 11.837-5.212 11.996-11.701c0-.025.004-.05.004-.075V9.452a.845.845 0 0 0-.841-.841h-1.346c-1.159-4.329-5.112-7.521-9.805-7.521S3.363 4.282 2.203 8.611m18.444 0H3.37c1.127-3.702 4.57-6.399 8.638-6.399c4.069 0 7.512 2.697 8.639 6.399"/></svg>`,
259+
import: {
260+
name: 'useScriptUmamiAnalytics',
261+
from: resolve('./runtime/registry/umami-analytics'),
262+
},
263+
},
255264
]
256265
}
+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { useRegistryScript } from '../utils'
2+
import { object, optional, string, boolean, array } from '#nuxt-scripts-validator'
3+
import type { RegistryScriptInput } from '#nuxt-scripts/types'
4+
5+
export const UmamiAnalyticsOptions = object({
6+
websiteId: string(), // required
7+
/**
8+
* By default, Umami will send data to wherever the script is located.
9+
* You can override this to send data to another location.
10+
*/
11+
hostUrl: optional(string()),
12+
/**
13+
* By default, Umami tracks all pageviews and events for you automatically.
14+
* You can disable this behavior and track events yourself using the tracker functions.
15+
* https://umami.is/docs/tracker-functions
16+
*/
17+
autoTrack: optional(boolean()),
18+
/**
19+
* If you want the tracker to only run on specific domains, you can add them to your tracker script.
20+
* This is a comma delimited list of domain names.
21+
* Helps if you are working in a staging/development environment.
22+
*/
23+
domains: optional(array(string())),
24+
/**
25+
* If you want the tracker to collect events under a specific tag.
26+
* Events can be filtered in the dashboard by a specific tag.
27+
*/
28+
tag: optional(string()),
29+
})
30+
31+
export type UmamiAnalyticsInput = RegistryScriptInput<typeof UmamiAnalyticsOptions, false>
32+
33+
export interface UmamiAnalyticsApi {
34+
track: ((payload?: Record<string, any>) => void) & ((event_name: string, event_data: Record<string, any>) => void)
35+
identify: (session_data?: Record<string, any>) => void
36+
}
37+
38+
declare global {
39+
interface Window {
40+
umami: UmamiAnalyticsApi
41+
}
42+
}
43+
44+
export function useScriptUmamiAnalytics<T extends UmamiAnalyticsApi>(_options?: UmamiAnalyticsInput) {
45+
return useRegistryScript<T, typeof UmamiAnalyticsOptions>('umamiAnalytics', (options) => {
46+
const domains = Array.isArray(options?.domains) ? options.domains.join(',') : options?.domains
47+
return {
48+
scriptInput: {
49+
'src': 'https://cloud.umami.is/script.js',
50+
'data-website-id': options.websiteId,
51+
'data-host-url': options?.hostUrl || undefined,
52+
'data-auto-track': typeof options?.autoTrack === 'boolean' ? options.autoTrack : true,
53+
'data-domains': domains || undefined,
54+
'data-tag': options?.tag || undefined,
55+
},
56+
schema: import.meta.dev ? UmamiAnalyticsOptions : undefined,
57+
scriptOptions: {
58+
use() {
59+
return window.umami
60+
},
61+
},
62+
}
63+
}, _options)
64+
}

src/runtime/types.ts

+2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import type { ClarityInput } from './registry/clarity'
2929
import type { CrispInput } from './registry/crisp'
3030
import type { GoogleAnalyticsInput } from './registry/google-analytics'
3131
import type { GoogleTagManagerInput } from './registry/google-tag-manager'
32+
import type { UmamiAnalyticsInput } from './registry/umami-analytics'
3233
import { object } from '#nuxt-scripts-validator'
3334

3435
export type WarmupStrategy = false | 'preload' | 'preconnect' | 'dns-prefetch'
@@ -154,6 +155,7 @@ export interface ScriptRegistry {
154155
xPixel?: XPixelInput
155156
youtubePlayer?: YouTubePlayerInput
156157
vimeoPlayer?: VimeoPlayerInput
158+
umamiAnalytics?: UmamiAnalyticsInput
157159
[key: `${string}-npm`]: NpmInput
158160
}
159161

0 commit comments

Comments
 (0)