Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nuxt: How to add content/scripts to the head dynamically on server side

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&amp;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

like image 305
Philipp S. Avatar asked Nov 07 '22 11:11

Philipp S.


1 Answers

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
  })
like image 144
HMilbradt Avatar answered Nov 28 '22 23:11

HMilbradt