Guides

Tag Sorting

How tags are sorted and how to configure them.

Once tags are deduped, they will be sorted. Sorting the tags is important to ensure critical tags are rendered first, as well as allowing you to have tags in a specific order that you need them in.

For example, if you need to preload an asset, you'll need this to come before the asset itself. Which is a bit of a challenge when the tags are nested.

Sorting Logic

Sorting is done in multiple steps:

  • order critical tags first
  • order by provided tagPriority
  • order by natural order they were registered

Critical Tags

The following critical tags have their own default tagPriority:

  • -20 - <meta charset ...>
  • -10 - <base>
  • 0 - <meta http-equiv="content-security-policy" ...>
  • 10 - <title>
  • 20 - <link rel="preconnect" ...>

All other tags have a default priority of 100: <meta>, <script>, <link>, <style>, etc

tagPriority

You can set custom priorities for tags using the tagPriority attribute.

Providing a number will set the priority to that number. The lower the number, the higher the priority.

You can also make use of a string alias that adjusts the priority by a relative amount:

  • critical - -80
  • high - -10
  • low - 20

Lastly you can set the priority based on the position of another tag using before: or after:. This is useful when you need to ensure a tag is rendered before or after another tag.

Sort by number

When providing a number, refer to the priorities set for critical tags above.

// some layout we have a js file that is ran
useHead({
  script: [
    {
      src: '/not-important-script.js',
    },
  ],
})

// but in our page we want to run a script before the above
useHead({
  script: [
    {
      src: '/very-important-script.js',
      tagPriority: 0,
    },
  ],
})

// <script src=\"/very-important-script.js\"></script>
// <script src=\"/not-important-script.js\"></script>

Sort with before: and after:

When providing a string, you can use before: or after: to target a tag key.

Tag keys are prefixed with their tag name to avoid dedupe collisions, so you need to use the form of {tagName}:{key}.

useHead({
  script: [
    {
      key: 'not-important',
      src: '/not-important-script.js',
    },
  ],
})
useHead({
  script: [
    {
      // script is the tag name to target, `not-important` is the key we're targeting
      tagPriority: 'before:script:not-important',
      src: '/must-be-first-script.js',
    },
  ],
})
// <script src=\"/must-be-first-script.js\"></script>
// <script src=\"/not-important-script.js\"></script>

Hydration Caveats

When hydrating the state, either through mounting a SSR document or switching between pages, the package is dealt with a problem.

How to handle previously rendered tags that need to be replaced?

To do so Unhead will simply replace the tag in its current position. This provides a nicer UX with less tags jumping around.

This means that the tagPriority may not be honoured when hydrating.