How can I add dynamically content to the head in Nuxt on server side?
I was trying to rewrite this module here which supports only static id's: https://github.com/nuxt-community/modules/blob/master/packages/google-tag-manager/index.js
(My id is coming from the store (The store is fetching the id from a rest call))
This is the function which is adding the content to the head:
export default function addheadcode(head, id) {
const options = {
id: id,
layer: 'dataLayer',
pageTracking: true,
pageViewEventName: 'nuxtRoute',
respectDoNotTrack: false,
query: {},
scriptURL: '//www.googletagmanager.com/gtm.js',
noscriptURL: '//www.googletagmanager.com/ns.html',
env: {} // env is supported for backward compability and is alias of query
}
const queryParams = Object.assign({}, options.env, options.query)
queryParams.id = options.id
if (options.layer) {
queryParams.l = options.layer
}
const queryString = Object.keys(queryParams)
.filter(key => queryParams[key] !== null && queryParams[key] !== undefined)
.map(
key =>
`${encodeURIComponent(key)}=${encodeURIComponent(queryParams[key])}`
)
.join('&')
head = head || {}
head.noscript = head.noscript || []
head.script = head.script || []
head.script.push({
hid: 'gtm',
src:
(options.scriptURL || '//www.googletagmanager.com/gtm.js') +
'?' +
queryString,
async: true
})
head.noscript.push({
hid: 'gtm-noscript',
innerHTML: `<iframe src="${options.noscriptURL}?${queryString}" height="0" width="0" style="display:none;visibility:hidden"></iframe>`,
pbody: true
})
head.__dangerouslyDisableSanitizersByTagID =
head.__dangerouslyDisableSanitizersByTagID || {}
head.__dangerouslyDisableSanitizersByTagID['gtm-noscript'] = ['innerHTML']
}
And this is my middleware which I am executing only on server side /middleware/gtm.js
import gtmAddheadcode from '~/src/googletagmanager.js'
export default function({ store, app }) {
if (process.server) {
const gtmID = store.getters['websiteskeleton/googelTagManagerId']
gtmAddheadcode(app.head, gtmID)
}
}
Problem: On the first call it seems to work fine. Here is the output which I can see on client side:
<script data-n-head="true" src="//www.googletagmanager.com/gtm.js?id=GTM-P4FZ3XX&l=dataLayer" async="true"></script><noscript data-n-head="true" data-hid="gtm-noscript" pbody="true"><iframe src="//www.googletagmanager.com/ns.html?id=GTM-XXX&l=dataLayer" height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
If I refresh that page. I see the same content double. If i refresh it again I see it threefold.
1) Why is Nuxt saving this state of the application?
2) What can I do to fix this problem?
EDIT: I created a git repository with the code. The problem occurs only in production mode! You can't see it while running dev. Example repository
EDIT 2: It is a bug an will be fixed: https://github.com/nuxt/nuxt.js/issues/6786#event-2893743506
To answer your questions:
1.
This does indeed sound like a bug somewhere. Nuxt should not be saving state like this across requests. This seems like a huge issue to me, making it incredibly easy to cause memory leaks in your app by just omitting the hid
. I would suggest opening an issue on the Nuxt github page addressing this.
My educated guess is that Nuxt might persist the state of vue-meta
, the library that's used to work with the head
tag, and assumes that users will be using hid
on their elements to replace them when needed.
2.
The only suggestion I have is to add hid
to your script object, naming it something common like 'gtag'.
head.script.push({
hid: 'gtag',
src:
(options.scriptURL || '//www.googletagmanager.com/gtm.js') +
'?' +
queryString,
async: true
})
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With