---
title: "Tag Sorting &amp; Placement · Unhead"
canonical_url: "https://unhead.unjs.io/docs/vue/head/guides/core-concepts/positions"
last_updated: "2026-07-26T07:03:35.428Z"
meta:
  description: "Control where head tags render with tagPosition (head, bodyOpen, bodyClose) and tagPriority for ordering. Understand the Capo.js weights applied during SSR."
  "og:description": "Control where head tags render with tagPosition (head, bodyOpen, bodyClose) and tagPriority for ordering. Understand the Capo.js weights applied during SSR."
  "og:title": "Tag Sorting & Placement · Unhead"
---

Home

`
Unhead on GitHub

Switch to VueSwitch to TypeScriptSwitch to ReactSwitch to SvelteSwitch to Solid.jsSwitch to AngularSwitch to Nuxt

**Core Concepts**

# **Tag Sorting & Placement**

[Copy for LLMs](https://raw.githubusercontent.com/unjs/unhead/refs/heads/main/docs/head/1.guides/1.core-concepts/2.positions.md)

Use `**tagPosition: 'head' | 'bodyOpen' | 'bodyClose'**` to choose a render location. Use `**tagPriority: 'critical' | 'high' | number | 'low'**` to influence order within a location.

## Tag placement

The `<link>`, `<script>`, `<noscript>`, and `<style>` inputs accept an optional `tagPosition` property:

- `head`: Render in the `<head>` (default)
- `bodyOpen`: Render at the start of the `<body>`
- `bodyClose`: Render at the end of the `<body>`

Placing a classic synchronous script at `**bodyClose**` lets the parser build the preceding body first. A script without `**async**`, `**defer**`, or `**type="module"**` still pauses parsing when the browser reaches it. See [**~~MDN's script loading reference~~**](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/script#notes).

### Placement examples

- **Analytics scripts**: Placing a script at `**bodyClose**` lets the parser process the preceding body markup first, although downloading and executing the script still has a cost
- **Critical CSS**: Place essential styles in `head` with high priority
- **Early body scripts**: Use `**bodyOpen**` only when a script must run before the rest of the body; a classic synchronous script there still blocks parsing

```
import { useHead } from '@unhead/vue'

// useHead: /docs/head/api/composables/use-head
useHead({
  script: [
    {
      src: '/my-lazy-script.js',
      tagPosition: 'bodyClose',
    },
  ],
})
// renders
//   ...
//   <script src="/my-lazy-script.js"></script>
// </body>
```

## Server and client sort order

During server rendering, each tag receives a weight. Lower weights render first.

[**~~Capo.js~~**](https://rviscomi.github.io/capo.js/) weights are automatically applied to server-rendered tags to reduce [**~~critical request chains~~**](https://developer.chrome.com/docs/lighthouse/performance/critical-request-chains). The client applies explicit numeric and alias priorities, but does not recompute Capo ordering. The server weights are:

- **-30**: `<meta http-equiv="content-security-policy" ...>`
- **-20**: `<meta charset ...>`
- **-15**: `<meta name="viewport" ...>`
- **-10**: `<base>`
- **10**: `<title>`
- **20**: `<link rel="preconnect" ...>`
- **25**: `<script type="importmap">`
- **30**: `<script async ...>`
- **40**: `<style>@import ...</style>`
- **50**: sync `<script>` (inline or `**src**`)
- **60**: `<style>`, `<link rel="stylesheet" ...>`
- **70**: `<link rel="preload" ...>`, `<link rel="modulepreload" ...>`
- **80**: `<script defer ...>`, `<script type="module" ...>`
- **90**: `<script type="speculationrules">`, `<link rel="prefetch" ...>`, `<link rel="dns-prefetch" ...>`, `<link rel="prerender" ...>`

All other tags have a default priority of `100`.

`<script type="importmap">` is pinned at weight 25 so it is emitted before async scripts, module scripts, and `**modulepreload**`. The [**~~HTML Standard's import-map processing model~~**](https://html.spec.whatwg.org/multipage/webappapis.html#import-maps) allows multiple maps and prevents later maps from changing module resolutions that already occurred. Unhead therefore does not force-dedupe import maps. Set an explicit `**key**` for last-wins replacement instead.

Override these default weights with the `tagPriority` property.

### Setting tag priority

The `tagPriority` property can be set to an explicit weight, a string alias or a string to target a specific tag.

#### Sorting with Aliases

Aliases adjust a tag's current weight instead of replacing it. The server starts from its Capo.js weight; the client starts from a baseline of 100:

- `critical`: subtracts **8**
- `high`: subtracts **1**
- `low`: adds **2**

```
import { useHead } from '@unhead/vue'

useHead({
  script: [
    {
      src: '/my-lazy-script.js',
      tagPriority: 'low',
    },
  ],
})
```

#### Sort by number

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

```
import { useHead } from '@unhead/vue'

// A layout registers a script
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:**`

To place one tag relative to another, use the optional [**~~Alias Sorting Plugin~~**](https://unhead.unjs.io/docs/head/guides/plugins/alias-sorting).

### Custom server weights

For custom ordering, pass a `**tagWeight**` function when creating the server head. It receives each tag and returns a numeric weight; lower values render first.

The default `**capoTagWeight**` function is exported from `**unhead/server**` so you can wrap it:

```
import { createHead, capoTagWeight } from '@unhead/vue/server'

const head = createHead({
  tagWeight(tag) {
    // Promote SEO meta above styles for bot requests
    if (isBot && tag.tag === 'meta' && tag.props.property?.startsWith('og:'))
      return 55 // just above styles (60)
    return capoTagWeight(tag)
  }
})
```

The function can vary ordering by request context. For example, it can promote social metadata for crawler requests while retaining CAPO order for browsers.

The [**~~Validate Plugin~~**](https://unhead.unjs.io/docs/head/guides/plugins/validate) includes a `**meta-beyond-1mb**` heuristic that warns when large inline content pushes metadata beyond its default 1MB inspection threshold.

### Preserved order during hydration

When hydrating the state (e.g., SSR or page switch), Unhead replaces existing tags in their current position to avoid a flash of content.

This may cause `**tagPriority**` to be ignored during hydration. For client-side-only applications or SPAs, this isn't an issue, but for SSR applications, be aware that the initial render positions may be preserved during hydration.

## Programmatic reordering

The `tags:afterResolve` hook receives tags after resolution and before DOM rendering. Modify `**ctx.tags**` when `**tagPriority**` is not expressive enough:

```
import { defineHeadPlugin } from '@unhead/vue/plugins'

export const fontPreloadFirstPlugin = defineHeadPlugin({
  key: 'font-preload-first',
  hooks: {
    'tags:afterResolve': (ctx) => {
      const fontPreloads = ctx.tags.filter(tag =>
        tag.tag === 'link'
        && tag.props.rel === 'preload'
        && tag.props.as === 'font'
      )
      const otherTags = ctx.tags.filter(tag =>
        !(tag.tag === 'link'
          && tag.props.rel === 'preload'
          && tag.props.as === 'font')
      )
      ctx.tags = [...fontPreloads, ...otherTags]
    }
  }
})
```

## See Also

- [**~~Alias Sorting Plugin~~**](https://unhead.unjs.io/docs/head/guides/plugins/alias-sorting): Sort tags by alias
- [**~~Handling Duplicates~~**](https://unhead.unjs.io/docs/head/guides/core-concepts/handling-duplicates): Deduplication strategies
- [**~~useHead() API~~**](https://unhead.unjs.io/docs/head/api/composables/use-head): `**tagPriority**` option

[~~Edit this page~~](https://github.com/unjs/unhead/edit/main/docs/head/1.guides/1.core-concepts/2.positions.md)

[~~Markdown For LLMs~~](https://raw.githubusercontent.com/unjs/unhead/refs/heads/main/docs/head/1.guides/1.core-concepts/2.positions.md)

**Did this page help you? **

[**Titles & Title Templates** Manage page titles with useHead, title templates, reactive values, social metadata, and template params.](https://unhead.unjs.io/docs/head/guides/core-concepts/titles) [**Class & Style Attributes** Add classes and styles to html and body tags with htmlAttrs and bodyAttrs. Support for strings, arrays, objects, and reactive values.](https://unhead.unjs.io/docs/head/guides/core-concepts/class-attr)

**On this page **

- [Tag placement](#tag-placement)
- [Server and client sort order](#server-and-client-sort-order)
- [Programmatic reordering](#programmatic-reordering)
- [See Also](#see-also)