Skip to content

Commit 881a2a9

Browse files
authored
feat: google adsense (#75)
1 parent f1423d2 commit 881a2a9

File tree

9 files changed

+324
-1
lines changed

9 files changed

+324
-1
lines changed
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
---
2+
title: Google Adsense
3+
description: Show Google Adsense ads in your Nuxt app.
4+
links:
5+
- label: useScriptGoogleAdsense
6+
icon: i-simple-icons-github
7+
to: https://github.com/nuxt/scripts/blob/main/src/runtime/registry/google-adsense.ts
8+
size: xs
9+
- label: "<ScriptGoogleAdsense>"
10+
icon: i-simple-icons-github
11+
to: https://github.com/nuxt/scripts/blob/main/src/runtime/components/ScriptGoogleAdsense.vue
12+
size: xs
13+
---
14+
15+
:UAlert{title="Experimental" description="The Google Adsense integration has not been fully tested, use with caution." color="yellow" variant="soft" class="not-prose"}
16+
17+
[Google Adsense](https://www.google.com/adsense/start/) allows you to monetize your website by displaying ads.
18+
19+
Nuxt Scripts provides a `useScriptGoogleAdsense` composable and a headless `ScriptGoogleAdsense` component to interact with the Google Adsense.
20+
21+
## ScriptGoogleAdsense
22+
23+
The `ScriptGoogleAdsense` component is a wrapper around the `useScriptGoogleAdsense` composable. It provides a simple way to embed ads in your Nuxt app.
24+
25+
```vue
26+
<template>
27+
<ScriptGoogleAdsense
28+
data-ad-client="ca-pub-..."
29+
data-ad-slot="..."
30+
/>
31+
</template>
32+
```
33+
34+
### Handling Ad-blockers
35+
36+
You can use these hooks to add a fallback when the Google Adsense script is blocked.
37+
38+
```vue
39+
<template>
40+
<ScriptGoogleAdsense
41+
data-ad-client="ca-pub-..."
42+
data-ad-slot="..."
43+
>
44+
<template #error>
45+
<!-- Fallback ad -->
46+
Please support us by disabling your ad blocker.
47+
</template>
48+
</ScriptGoogleAdsense>
49+
</template>
50+
```
51+
52+
### Props
53+
54+
The `ScriptGoogleAdsense` component supports all props that Google Adsense supports on the `<ins>` tag. See the [Ad tags documentation](https://developers.google.com/adsense/platforms/transparent/ad-tags) for more information.
55+
56+
At a minimum you must provide the following tags:
57+
- `data-ad-client`: The Google Adsense ID.
58+
- `data-ad-slot`: The slot ID.
59+
60+
Nuxt Scripts exposes the following additional props:
61+
- `trigger`: The trigger event to load the script. Default is `undefined`. See [Element Event Triggers](/docs/guides/script-triggers#element-event-triggers) for more information.
62+
63+
### Slots
64+
65+
There are a number of slots mapped to the script status that you can use to customize the ad experience.
66+
67+
- **error**:
68+
The slot is used to display content when the ad fails to load.
69+
70+
- **awaitingLoad**
71+
The slot is used to display content before the ad script is loaded.
72+
73+
- **loaded**
74+
The slot is used to display content after the ad script is loaded.
75+
76+
- **loading**
77+
The slot is used to display content while the ad script is loading.
78+
79+
```vue
80+
<template>
81+
<ScriptGoogleAdsense
82+
serve="..."
83+
placement="..."
84+
>
85+
<template #awaitingLoad>
86+
Loading ads...
87+
</template>
88+
</ScriptGoogleAdsense>
89+
</template>
90+
```
91+
92+
## useScriptGoogleAdsense
93+
94+
The `useScriptGoogleAdsense` composable lets you have fine-grain control over the Google Adsense script.
95+
96+
```ts
97+
export function useScriptGoogleAdsense<T extends GoogleAdsenseApi>(_options?: GoogleAdsenseInput) {}
98+
```
99+
100+
Please follow the [Registry Scripts](/docs/guides/registry-scripts) guide to learn more about advanced usage.
101+
102+
### GoogleAdsenseApi
103+
104+
```ts
105+
export interface GoogleAdsenseApi {
106+
adsbygoogle: any[] & { loaded: boolean }
107+
}
108+
```
109+
110+
### GoogleAdsenseInput
111+
112+
```ts
113+
export const GoogleAdsenseOptions = object({
114+
/**
115+
* The Google Adsense ID.
116+
*/
117+
client: optional(string()),
118+
})
119+
```

docs/nuxt.config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export default defineNuxtConfig({
1818
hooks: {
1919
// Define `@nuxt/ui` components as global to use them in `.md` (feel free to add those you need)
2020
'components:extend': (components) => {
21-
const globals = components.filter(c => ['UButton', 'UIcon'].includes(c.pascalName))
21+
const globals = components.filter(c => ['UButton', 'UIcon', 'UAlert'].includes(c.pascalName))
2222
globals.forEach(c => c.global = true)
2323
},
2424
},

docs/pages/scripts.vue

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22
import { useScriptsRegistry } from '~/composables/useScriptsRegistry'
33
44
const categories = {
5+
ads: {
6+
label: 'Ads',
7+
description: 'Monetize your website with ads.',
8+
},
59
analytics: {
610
label: 'Analytics',
711
description: 'Track your users and their behavior on your website.',

playground/pages/index.vue

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ const thirdParties = [
2626
name: 'X Pixel',
2727
path: '/third-parties/x-pixel/nuxt-scripts',
2828
},
29+
{
30+
name: 'Google Adsense',
31+
path: '/third-parties/google-adsense/nuxt-scripts',
32+
},
2933
{
3034
name: 'Fathom Analytics',
3135
path: '/third-parties/fathom-analytics',
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<script lang="ts" setup>
2+
import { useHead } from '#imports'
3+
4+
useHead({
5+
title: 'Google Adsense',
6+
})
7+
</script>
8+
9+
<template>
10+
<div>
11+
<ScriptGoogleAdsense
12+
data-ad-client="ca-pub-8232083562163020"
13+
data-ad-slot="6039709756"
14+
>
15+
<template #awaitingLoad>
16+
<div class="text-white text-3xl">
17+
...waiting
18+
</div>
19+
</template>
20+
<template #loading>
21+
<div class="text-white text-3xl">
22+
...loading
23+
</div>
24+
</template>
25+
</ScriptGoogleAdsense>
26+
</div>
27+
</template>
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<script lang="ts" setup>
2+
import { onMounted, ref, useHead } from '#imports'
3+
4+
useHead({
5+
title: 'Google Adsense',
6+
})
7+
8+
// composables return the underlying api as a proxy object and a $script with the script state
9+
useHead({
10+
script: [
11+
{
12+
src: 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js',
13+
async: true,
14+
crossorigin: 'anonymous',
15+
},
16+
],
17+
})
18+
const loaded = ref(false)
19+
onMounted(() => {
20+
setTimeout(() => {
21+
loaded.value = true
22+
;(window.adsbygoogle = window.adsbygoogle || []).push({})
23+
}, 1000)
24+
})
25+
</script>
26+
27+
<template>
28+
<div>
29+
<ClientOnly>
30+
<div>
31+
<ins
32+
class="adsbygoogle"
33+
style="display:block"
34+
data-ad-client="ca-pub-8232083562163020"
35+
data-ad-slot="6039709756"
36+
data-ad-format="auto"
37+
data-full-width-responsive="true"
38+
/>
39+
</div>
40+
</ClientOnly>
41+
</div>
42+
</template>

src/registry.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import type { NpmInput } from './runtime/registry/npm'
66
import type { PlausibleAnalyticsInput } from './runtime/registry/plausible-analytics'
77
import type { GoogleAnalyticsInput } from './runtime/registry/google-analytics'
88
import type { RegistryScripts } from './runtime/types'
9+
import type { GoogleAdsenseInput } from './runtime/registry/google-adsense'
910

1011
// avoid nuxt/kit dependency here so we can use in docs
1112

@@ -113,6 +114,21 @@ export const registry: (resolve?: (s: string) => string) => RegistryScripts = (r
113114
from: resolve('./runtime/registry/x-pixel'),
114115
},
115116
},
117+
// ads
118+
{
119+
label: 'Google Adsense',
120+
scriptBundling: (options?: GoogleAdsenseInput) => {
121+
return withQuery('https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js', {
122+
client: options?.client,
123+
})
124+
},
125+
category: 'ads',
126+
logo: `<svg xmlns="http://www.w3.org/2000/svg" width="36.09" height="32" viewBox="0 0 256 227"><path fill="#FBBC04" d="M161.8 62.158c11.581-19.822 4.705-45.154-15.355-56.603C126.376-5.878 100.723.899 89.142 20.72c-.51.888-.99 1.794-1.44 2.715L48.553 90.41a49.41 49.41 0 0 0-2.401 4.112L5.495 164.681l72.65 40.721l40.45-69.566a40.013 40.013 0 0 0 2.402-4.112l39.15-66.983a45.769 45.769 0 0 0 1.654-2.583"/><path fill="#34A853" d="M78.483 205.189c-11.515 20.142-37.49 27.553-57.434 15.931c-19.954-11.63-27.036-36.847-15.513-56.982c11.523-20.134 37.267-27.578 57.22-15.956c19.954 11.63 27.241 36.872 15.727 56.998"/><path fill="#4285F4" d="M235.257 75.417c-19.83-11.429-45.17-4.661-56.661 15.134l-41.478 71.67c-11.428 19.755-4.678 45.033 15.076 56.46l.107.062c19.835 11.433 45.18 4.66 56.67-15.142l41.469-71.663c11.426-19.76 4.67-45.042-15.09-56.468z"/></svg>`,
127+
import: {
128+
name: 'useScriptGoogleAdsense',
129+
from: resolve('./runtime/registry/google-adsense'),
130+
},
131+
},
116132
// marketing
117133
{
118134
label: 'Intercom',
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<script setup lang="ts">
2+
import { callOnce, onMounted, ref, useElementScriptTrigger, useScriptGoogleAdsense } from '#imports'
3+
import type { ElementScriptTrigger } from '#nuxt-scripts'
4+
5+
const props = withDefaults(defineProps<{
6+
dataAdClient: string
7+
dataAdSlot: string
8+
dataAdFormat?: 'auto'
9+
dataFullWidthResponsive?: boolean
10+
/**
11+
* Defines the trigger event to load the script.
12+
*/
13+
trigger?: ElementScriptTrigger
14+
}>(), {
15+
dataAdFormat: 'auto',
16+
dataFullWidthResponsive: true,
17+
})
18+
19+
const rootEl = ref(null)
20+
const trigger = useElementScriptTrigger({ trigger: props.trigger, el: rootEl })
21+
22+
const { $script } = useScriptGoogleAdsense({
23+
client: props.dataAdClient,
24+
scriptOptions: {
25+
trigger,
26+
},
27+
})
28+
29+
onMounted(() => {
30+
callOnce(() => {
31+
(window.adsbygoogle = window.adsbygoogle || []).push({})
32+
})
33+
})
34+
</script>
35+
36+
<template>
37+
<ins
38+
ref="rootEl"
39+
class="adsbygoogle"
40+
style="display: block;"
41+
:data-ad-client="dataAdClient"
42+
:data-ad-slot="dataAdSlot"
43+
:data-ad-format="dataAdFormat"
44+
:data-full-width-responsive="dataFullWidthResponsive"
45+
v-bind="{ ...$attrs }"
46+
>
47+
<slot v-if="$script.status.value === 'awaitingLoad'" name="awaitingLoad" />
48+
<slot v-if="$script.status.value === 'loading'" name="loading" />
49+
</ins>
50+
</template>
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import { optional } from 'valibot'
2+
import { useRegistryScript } from '../utils'
3+
import { object, string } from '#nuxt-scripts-validator'
4+
import type { RegistryScriptInput } from '#nuxt-scripts'
5+
import { useHead } from '#imports'
6+
7+
export const GoogleAdsenseOptions = object({
8+
/**
9+
* The Google Adsense ID.
10+
*/
11+
client: optional(string()),
12+
})
13+
14+
export type GoogleAdsenseInput = RegistryScriptInput<typeof GoogleAdsenseOptions>
15+
16+
export interface GoogleAdsenseApi {
17+
/**
18+
* The Google Adsense API.
19+
*/
20+
adsbygoogle: any[] & { loaded: boolean }
21+
}
22+
23+
declare global {
24+
interface Window extends GoogleAdsenseApi {}
25+
}
26+
27+
/**
28+
* useScriptGoogleAdsense
29+
*
30+
* A 3P wrapper for Google Analytics that takes an options input to feed into third-party-capital({@link https://github.com/GoogleChromeLabs/third-party-capital}), which returns instructions for nuxt-scripts.
31+
*/
32+
export function useScriptGoogleAdsense<T extends GoogleAdsenseApi>(_options?: GoogleAdsenseInput) {
33+
// Note: inputs.useScriptInput is not usable, needs to be normalized
34+
return useRegistryScript<T, typeof GoogleAdsenseOptions>('googleAdsense', options => ({
35+
scriptInput: {
36+
src: 'https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js',
37+
},
38+
schema: import.meta.dev ? GoogleAdsenseOptions : undefined,
39+
scriptOptions: {
40+
use() {
41+
return { adsbygoogle: window.adsbygoogle }
42+
},
43+
// allow dataLayer to be accessed on the server
44+
stub: import.meta.client
45+
? undefined
46+
: ({ fn }) => {
47+
return fn === 'adsbygoogle' ? [] : undefined
48+
},
49+
beforeInit() {
50+
useHead({
51+
meta: [
52+
{
53+
name: 'google-adsense-account',
54+
content: options?.client,
55+
},
56+
],
57+
})
58+
},
59+
},
60+
}), _options)
61+
}

0 commit comments

Comments
 (0)