Skip to content

Commit 8bd1694

Browse files
authoredFeb 5, 2025··
feat: default important head tags (#479)
* feat: default important head tags * chore: missing file
1 parent 6f29398 commit 8bd1694

26 files changed

+291
-108
lines changed
 

‎docs/0.react/1.installation.md

+29-32
Original file line numberDiff line numberDiff line change
@@ -116,9 +116,36 @@ app.use('*all', async (req, res) => {
116116

117117
Done! Your app should now be rendering head tags on the server and client.
118118

119-
In Unhead React we have both a `<Head>`{lang="html"} component and a `useHead()`{lang="ts"} hook for managing head tags.
119+
To improve your apps stability, Unhead will now insert important default tags for you.
120+
121+
- `<meta charset="utf-8">`
122+
- `<meta name="viewport" content="width=device-width, initial-scale=1">`
123+
- `<html lang="en">`
124+
125+
You may need to change these for your app requirements, for example you may want to change the default language. Adding
126+
tags in your server entry means you won't add any weight to your client bundle.
127+
128+
```ts {2,6-8} [src/entry-server.ts]
129+
import { createHead } from '@unhead/react/server'
130+
131+
export function render(_url: string) {
132+
const head = createHead({
133+
// change default initial lang
134+
init: [
135+
{
136+
htmlAttrs: { lang: 'en' },
137+
title: 'Default title',
138+
titleTemplate: '%s %separator My Site',
139+
},
140+
]
141+
})
142+
const html = `<!-- your html -->`
143+
return { html, head }
144+
}
145+
```
146+
147+
For adding tags in your components, you can either use the `<Head>`{lang="html"} component and the `useHead()`{lang="ts"} hook.
120148

121-
Decicing which to use will be up to you:
122149
- `useHead()`{lang="ts"}: Type safety, more flexible and can access lower level primitives.
123150
- `<Head>`{lang="html"}: More declarative and easier to read.
124151

@@ -146,36 +173,6 @@ export default function App() {
146173
}
147174
```
148175

149-
If you'd like to add any default tags to your app, doing so in your server entry means you won't add any weight
150-
to your client bundle.
151-
152-
```tsx {4,11-14} [src/entry-server.tsx]
153-
import { useSeoMeta } from '@unhead/react'
154-
import { createHead, UnheadProvider } from '@unhead/react/server'
155-
import { StrictMode } from 'react'
156-
import { renderToString } from 'react-dom/server'
157-
import App from './App'
158-
159-
export function render(_url: string) {
160-
const head = createHead()
161-
useSeoMeta({
162-
title: 'My Awesome Site',
163-
description: 'My awesome site description',
164-
}, head)
165-
const html = renderToString(
166-
<StrictMode>
167-
<UnheadProvider value={head}>
168-
<App />
169-
</UnheadProvider>
170-
</StrictMode>,
171-
)
172-
return { html, head }
173-
}
174-
```
175-
176-
You'll notice we're passing the `head` instance here, this is because we're outside the React app context, you can learn more
177-
on the [Async Context](/docs/React/guides/managing-context) guide.
178-
179176
### 5. Optional: Auto-Imports
180177

181178
If you're using [unplugin-auto-import](https://github.com/antfu/unplugin-auto-import), you can automatically import the composables.

‎docs/0.typescript/1.installation.md

+30
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,36 @@ app.use('*all', async (req, res) => {
9696

9797
Done! Your app should now be rendering head tags on the server and client.
9898

99+
100+
To improve your apps stability, Unhead will now insert important default tags for you.
101+
102+
- `<meta charset="utf-8">`
103+
- `<meta name="viewport" content="width=device-width, initial-scale=1">`
104+
- `<html lang="en">`
105+
106+
You may need to change these for your app requirements, for example you may want to change the default language. Adding
107+
tags in your server entry means you won't add any weight to your client bundle.
108+
109+
```ts {2,6-8} [main.ts]
110+
import { createHead } from 'unhead/server'
111+
import typescriptLogo from './typescript.svg'
112+
113+
export function render(_url: string) {
114+
const head = createHead({
115+
// change default initial lang
116+
init: [
117+
{
118+
title: 'Default title',
119+
titleTemplate: '%s | My Site',
120+
htmlAttrs: { lang: 'fr' }
121+
},
122+
]
123+
})
124+
const html = `<!-- your html -->`
125+
return { html, head }
126+
}
127+
```
128+
99129
Here is an example of how you can use `useHead` in your app for a counter:
100130

101131
```ts [counter.ts]

‎docs/0.vue/1.installation.md

+31-29
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,37 @@ app.use('*all', async (req, res) => {
119119

120120
Done! Your app should now be rendering head tags on the server and client.
121121

122-
```vue
122+
To improve your apps stability, Unhead will now insert important default tags for you.
123+
124+
- `<meta charset="utf-8">`
125+
- `<meta name="viewport" content="width=device-width, initial-scale=1">`
126+
- `<html lang="en">`
127+
128+
You may need to change these for your app requirements, for example you may want to change the default language. Adding
129+
tags in your server entry means you won't add any weight to your client bundle.
130+
131+
```ts {7-12} [src/entry-server.ts]
132+
import { createHead } from '@unhead/vue/server'
133+
134+
export async function render(_url: string) {
135+
// ...
136+
const head = createHead({
137+
init: [
138+
// change default initial lang
139+
{
140+
title: 'Default title',
141+
titleTemplate: '%s | My Site',
142+
htmlAttrs: { lang: 'fr' }
143+
},
144+
]
145+
})
146+
// ...
147+
}
148+
```
149+
150+
Feel free to play around with adding tags to your apps. Here's an example of adding some styles to the body.
151+
152+
```vue [src/App.vue]
123153
<script setup lang="ts">
124154
import { useHead } from '@unhead/vue'
125155
@@ -131,34 +161,6 @@ useHead({
131161
</script>
132162
```
133163

134-
If you'd like to add any default tags to your app, doing so in your server entry means you won't add any weight
135-
to your client bundle.
136-
137-
```ts {4,11-14} [src/entry-server.ts]
138-
import { useSeoMeta } from '@unhead/vue'
139-
import { createHead } from '@unhead/vue/server'
140-
import { renderToString } from 'vue/server-renderer'
141-
import { createApp } from './main'
142-
143-
export async function render(_url: string) {
144-
const { app } = createApp()
145-
const head = createHead()
146-
app.use(head)
147-
// no client-side hydration needed
148-
useSeoMeta({
149-
title: 'My Awesome Site',
150-
description: 'My awesome site description',
151-
}, { head })
152-
const ctx = {}
153-
const html = await renderToString(app, ctx)
154-
155-
return { html, head }
156-
}
157-
```
158-
159-
You'll notice we're passing the `head` instance here, this is because we're outside the Vue app context, you can learn more
160-
on the [Async Context](/docs/vue/guides/managing-context) guide.
161-
162164
### 5. Optional: Auto-Imports
163165

164166
If you're using [unplugin-auto-import](https://github.com/antfu/unplugin-auto-import), you can automatically import the composables.

‎docs/1.guides/0.titles.md

+9-9
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ Creating your own title like this is simple using `useHead()`{lang="ts"} with a
9090
```ts twoslash [input.vue]
9191
useHead({
9292
title: 'Home',
93-
titleTemplate: '%s %seperator MySite'
93+
titleTemplate: '%s %separator MySite'
9494
})
9595
```
9696

@@ -110,10 +110,10 @@ You may ask why we don't just use a function for the title template, and while t
110110

111111
Instead, it's recommended to use the params. Out-of-the-box, Unhead provides:
112112

113-
| Token | Description |
114-
|-------|-------------------------------------------------|
115-
| `%s` | The current page title. |
116-
| `%seperator` | The separator, defaults to a pipe character \|. |
113+
| Token | Description |
114+
|--------------|-------------------------------------------------|
115+
| `%s` | The current page title. |
116+
| `%separator` | The separator, defaults to a pipe character \|. |
117117

118118
The `%separator` token is smart - it only appears between content and automatically removes itself when the title is empty or when multiple separators would appear.
119119

@@ -124,7 +124,7 @@ Define custom template params to maintain consistent formatting:
124124
```ts twoslash [input.vue]
125125
useHead({
126126
title: 'Home',
127-
titleTemplate: '%s %seperator %siteName',
127+
titleTemplate: '%s %separator %siteName',
128128
templateParams: {
129129
seperator: '',
130130
siteName: 'MySite'
@@ -201,12 +201,12 @@ Remembering how to use the meta tags can be annoying, so we can use the [`useSeo
201201
202202
```ts [input.vue]
203203
useSeoMeta({
204-
titleTemplate: '%s %seperator Health Tips',
204+
titleTemplate: '%s %separator Health Tips',
205205
title: 'Why you should eat more broccoli',
206206
// og title is not effected by titleTemplate, we can use template params here if we need
207-
ogTitle: 'Hey! Health Tips %seperator 10 reasons to eat more broccoli.',
207+
ogTitle: 'Hey! Health Tips %separator 10 reasons to eat more broccoli.',
208208
// explicit twitter title is only needed when we want to display something just for X
209-
twitterTitle: 'Hey X! Health Tips %seperator 10 reasons to eat more broccoli.',
209+
twitterTitle: 'Hey X! Health Tips %separator 10 reasons to eat more broccoli.',
210210
})
211211
```
212212

‎docs/migration.md

+38-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,12 @@ navigation:
77

88
## Introduction
99

10-
The goal of Unhead v2 was to remove deprecations and remove the implicit context implementation.
10+
While Unhead has always been framework agnostic, the majority of adoption was by the Vue ecosystem.
11+
12+
With the release of Unhead v2, we now have first-class support for other frameworks. However, this guide will focus on
13+
the changes that affect Vue and TypeScript users.
14+
15+
The high-level of Unhead v2 was to remove deprecations and remove the implicit context implementation.
1116

1217
### Legacy Support
1318

@@ -401,3 +406,35 @@ createHead({
401406
disableCapoSorting: true,
402407
})
403408
```
409+
410+
## Default SSR Tags
411+
412+
🚦 Impact Level: Low
413+
414+
When SSR Unhead will now insert important default tags for you:
415+
- `<meta charset="utf-8">`
416+
- `<meta name="viewport" content="width=device-width, initial-scale=1">`
417+
- `<html lang="en">`
418+
419+
If you were previously relying on these being left empty, you may need to either disable them by using `disableDefaultTags` or insert tags
420+
to override them.
421+
422+
```ts
423+
import { createHead } from '@unhead/vue/server'
424+
425+
// disable when creating the head instance
426+
createHead({
427+
disableDefaults: true,
428+
})
429+
```
430+
431+
```ts
432+
import { useHead } from 'unhead'
433+
434+
// override the defaults
435+
useHead({
436+
htmlAttrs: {
437+
lang: 'fr'
438+
}
439+
})
440+
```

‎packages/angular/src/unhead/install.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { CreateHeadOptions } from '@unhead/schema'
1+
import type { CreateClientHeadOptions, CreateServerHeadOptions } from '@unhead/schema'
22
import type { AngularUnhead } from './types/index'
33
import { InjectionToken, makeEnvironmentProviders } from '@angular/core'
44
import { BEFORE_APP_SERIALIZED } from '@angular/platform-server'
@@ -11,7 +11,7 @@ export const headSymbol = 'usehead'
1111

1212
export const UnheadInjectionToken = new InjectionToken<AngularUnhead>(headSymbol)
1313

14-
export function provideServerHead(options: Omit<CreateHeadOptions, 'domDelayFn' | 'document'> = {}) {
14+
export function provideServerHead(options: CreateServerHeadOptions = {}) {
1515
const head = _createServerHead<AngularUnhead>({
1616
...options,
1717
plugins: [
@@ -33,7 +33,7 @@ export function provideServerHead(options: Omit<CreateHeadOptions, 'domDelayFn'
3333
])
3434
}
3535

36-
export function provideClientHead(options: Omit<CreateHeadOptions, 'domOptions' | 'document'> = {}) {
36+
export function provideClientHead(options: CreateClientHeadOptions = {}) {
3737
const head = _createClientHead<AngularUnhead>({
3838
domOptions: {
3939
render: createDebouncedFn(() => renderDOMHead(head), fn => setTimeout(() => fn(), 0)),

‎packages/react/src/server.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { CreateClientHeadOptions, MergeHead } from '@unhead/schema'
1+
import type { CreateServerHeadOptions, MergeHead } from '@unhead/schema'
22
import type { ReactNode } from 'react'
33
import type { MaybeComputedRef, ReactiveHead, ReactUnhead } from './types'
44
import { createElement } from 'react'
@@ -8,7 +8,7 @@ import { ReactReactivityPlugin } from './ReactReactivityPlugin'
88

99
export * from 'unhead/server'
1010

11-
export function createHead<T extends MergeHead>(options: CreateClientHeadOptions = {}): ReactUnhead<T> {
11+
export function createHead<T extends MergeHead>(options: CreateServerHeadOptions = {}): ReactUnhead<T> {
1212
return _createHead<MaybeComputedRef<ReactiveHead<T>>>({
1313
...options,
1414
plugins: [

‎packages/schema-org/test/e2e/basic.test.ts

+7
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { useDom } from '../../../unhead/test/fixtures'
88
describe('schema.org e2e', () => {
99
it('basic hydration', async () => {
1010
const ssrHead = createServerHead({
11+
disableDefaults: true,
1112
plugins: [
1213
SchemaOrgUnheadPlugin(),
1314
],
@@ -73,6 +74,7 @@ describe('schema.org e2e', () => {
7374

7475
it('hierarchy', async () => {
7576
const ssrHead = createServerHead({
77+
disableDefaults: true,
7678
plugins: [
7779
SchemaOrgUnheadPlugin({
7880
path: '/about',
@@ -146,6 +148,7 @@ describe('schema.org e2e', () => {
146148

147149
it('linking', async () => {
148150
const ssrHead = createServerHead({
151+
disableDefaults: true,
149152
plugins: [
150153
SchemaOrgUnheadPlugin({
151154
path: '/about',
@@ -193,6 +196,7 @@ describe('schema.org e2e', () => {
193196

194197
it('faq', async () => {
195198
const ssrHead = createServerHead({
199+
disableDefaults: true,
196200
plugins: [
197201
SchemaOrgUnheadPlugin({
198202
path: '/about',
@@ -259,6 +263,7 @@ describe('schema.org e2e', () => {
259263

260264
it('canonical', async () => {
261265
const ssrHead = createServerHead({
266+
disableDefaults: true,
262267
plugins: [
263268
SchemaOrgUnheadPlugin(),
264269
],
@@ -301,6 +306,7 @@ describe('schema.org e2e', () => {
301306

302307
it('empty', async () => {
303308
const ssrHead = createServerHead({
309+
disableDefaults: true,
304310
plugins: [
305311
SchemaOrgUnheadPlugin(),
306312
],
@@ -317,6 +323,7 @@ describe('schema.org e2e', () => {
317323
})
318324
it('#441', async () => {
319325
const ssrHead = createServerHead({
326+
disableDefaults: true,
320327
plugins: [
321328
SchemaOrgUnheadPlugin(),
322329
],

‎packages/schema-org/test/e2e/no-plugin.test.ts

+15-5
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import { useDom } from '../../../unhead/test/fixtures'
77

88
describe('schema.org e2e no plugin', () => {
99
it('basic hydration', async () => {
10-
const ssrHead = createServerHead()
10+
const ssrHead = createServerHead({
11+
disableDefaults: true,
12+
})
1113

1214
useSchemaOrg(ssrHead, [
1315
defineWebPage({
@@ -59,7 +61,9 @@ describe('schema.org e2e no plugin', () => {
5961
})
6062

6163
it('hierarchy', async () => {
62-
const ssrHead = createServerHead()
64+
const ssrHead = createServerHead({
65+
disableDefaults: true,
66+
})
6367

6468
useHead(ssrHead, {
6569
templateParams: {
@@ -134,7 +138,9 @@ describe('schema.org e2e no plugin', () => {
134138
})
135139

136140
it('linking', async () => {
137-
const ssrHead = createServerHead()
141+
const ssrHead = createServerHead({
142+
disableDefaults: true,
143+
})
138144

139145
useHead(ssrHead, {
140146
templateParams: {
@@ -184,7 +190,9 @@ describe('schema.org e2e no plugin', () => {
184190
})
185191

186192
it('faq', async () => {
187-
const ssrHead = createServerHead()
193+
const ssrHead = createServerHead({
194+
disableDefaults: true,
195+
})
188196

189197
useHead(ssrHead, {
190198
templateParams: {
@@ -253,7 +261,9 @@ describe('schema.org e2e no plugin', () => {
253261
})
254262

255263
it('many registrations', async () => {
256-
const ssrHead = createServerHead()
264+
const ssrHead = createServerHead({
265+
disableDefaults: true,
266+
})
257267
useSchemaOrg(ssrHead, [
258268
defineWebPage({
259269
name: 'One',

‎packages/schema-org/test/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ export async function findNode<T>(unhead: Unhead<any>, id: string) {
1717
}
1818
export async function useSetup(fn: (unhead: Unhead<any>) => void, meta: Partial<MetaInput> = {}) {
1919
const head = createHead({
20+
disableDefaults: true,
2021
plugins: [
2122
SchemaOrgUnheadPlugin({
2223
currency: 'AUD',

‎packages/schema-org/test/ssr/ids.test.ts

+12-4
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import { describe, expect, it } from 'vitest'
55

66
describe('schema.org ssr ids', () => {
77
it('adds host prefix to custom id without host', async () => {
8-
const ssrHead = createHead()
8+
const ssrHead = createHead({
9+
disableDefaults: true,
10+
})
911

1012
useHead(ssrHead, {
1113
templateParams: {
@@ -27,7 +29,9 @@ describe('schema.org ssr ids', () => {
2729
expect(id).toMatchInlineSnapshot(`"https://example.com/#/schema/web-page/#foo"`)
2830
})
2931
it('allows ids with custom domains', async () => {
30-
const ssrHead = createHead()
32+
const ssrHead = createHead({
33+
disableDefaults: true,
34+
})
3135

3236
useHead(ssrHead, {
3337
templateParams: {
@@ -49,7 +53,9 @@ describe('schema.org ssr ids', () => {
4953
expect(id).toMatchInlineSnapshot('"https://custom-domain.com/#foo"')
5054
})
5155
it('full relative paths', async () => {
52-
const ssrHead = createHead()
56+
const ssrHead = createHead({
57+
disableDefaults: true,
58+
})
5359

5460
useHead(ssrHead, {
5561
templateParams: {
@@ -72,7 +78,9 @@ describe('schema.org ssr ids', () => {
7278
})
7379

7480
it('full relative paths relations', async () => {
75-
const ssrHead = createHead()
81+
const ssrHead = createHead({
82+
disableDefaults: true,
83+
})
7684

7785
useHead(ssrHead, {
7886
templateParams: {

‎packages/schema/src/head.ts

+18
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ export interface CreateHeadOptions {
8181
document?: Document
8282
plugins?: HeadPluginInput[]
8383
hooks?: NestedHooks<HeadHooks>
84+
/**
85+
* Initial head input that should be added.
86+
*
87+
* Any tags here are added with low priority.
88+
*/
89+
init?: (Head<any> | undefined)[]
8490
/**
8591
* Disable the Capo.js tag sorting algorithm.
8692
*
@@ -89,6 +95,18 @@ export interface CreateHeadOptions {
8995
disableCapoSorting?: boolean
9096
}
9197

98+
export interface CreateServerHeadOptions extends CreateHeadOptions {
99+
/**
100+
* Should default important tags be skipped.
101+
*
102+
* Adds the following tags with low priority:
103+
* - <html lang="en">
104+
* - <meta charset="utf-8">
105+
* - <meta name="viewport" content="width=device-width, initial-scale=1">
106+
*/
107+
disableDefaults?: boolean
108+
}
109+
92110
export interface CreateClientHeadOptions extends CreateHeadOptions {
93111
/**
94112
* Options to pass to the DomPlugin.

‎packages/scripts/test/unit/ssr.test.ts

+12-4
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ import { useScript } from '../../src/useScript'
33

44
describe('ssr useScript', () => {
55
it('default', async () => {
6-
const head = createServerHead()
6+
const head = createServerHead({
7+
disableDefaults: true,
8+
})
79

810
useScript(head, {
911
src: 'https://cdn.example.com/script.js',
@@ -21,7 +23,9 @@ describe('ssr useScript', () => {
2123
`)
2224
})
2325
it('server', async () => {
24-
const head = createServerHead()
26+
const head = createServerHead({
27+
disableDefaults: true,
28+
})
2529

2630
useScript(head, {
2731
src: 'https://cdn.example.com/script.js',
@@ -41,7 +45,9 @@ describe('ssr useScript', () => {
4145
`)
4246
})
4347
it('await ', async () => {
44-
const head = createServerHead()
48+
const head = createServerHead({
49+
disableDefaults: true,
50+
})
4551

4652
// mock a promise, test that it isn't resolved in 1 second
4753
useScript<{ foo: 'bar' }>(head, {
@@ -62,7 +68,9 @@ describe('ssr useScript', () => {
6268
`)
6369
})
6470
it('google ', async () => {
65-
const head = createServerHead()
71+
const head = createServerHead({
72+
disableDefaults: true,
73+
})
6674
const window: any = {}
6775
const gtag = useScript<{ dataLayer: any[] }>(head, {
6876
src: 'https://www.googletagmanager.com/gtm.js?id=GTM-MNJD4B',

‎packages/scripts/test/unit/warmup.test.ts

+12-4
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import { useScript } from '../../src/useScript'
55

66
describe('warmup', () => {
77
it('server', () => {
8-
const head = createServerHead()
8+
const head = createServerHead({
9+
disableDefaults: true,
10+
})
911
useScript(head, 'https://cdn.example.com/script.js', {
1012
head,
1113
trigger: 'server',
@@ -15,7 +17,9 @@ describe('warmup', () => {
1517
expect(entry.link).toBeUndefined()
1618
})
1719
it('default / client', () => {
18-
const head = createServerHead()
20+
const head = createServerHead({
21+
disableDefaults: true,
22+
})
1923
useScript(head, 'https://cdn.example.com/script.js', {
2024
head,
2125
trigger: 'client',
@@ -25,7 +29,9 @@ describe('warmup', () => {
2529
expect(link.rel).toEqual('preload')
2630
})
2731
it('relative: default / client', () => {
28-
const head = createServerHead()
32+
const head = createServerHead({
33+
disableDefaults: true,
34+
})
2935
useScript(head, '/script.js', {
3036
head,
3137
trigger: 'client',
@@ -35,7 +41,9 @@ describe('warmup', () => {
3541
expect(link.rel).toEqual('preload')
3642
})
3743
it('absolute: dns-prefetch', () => {
38-
const head = createServerHead()
44+
const head = createServerHead({
45+
disableDefaults: true,
46+
})
3947
useScript(head, 'https://cdn.example.com/script.js', {
4048
head,
4149
trigger: 'client',

‎packages/unhead/src/createHead.ts

+5
Original file line numberDiff line numberDiff line change
@@ -123,5 +123,10 @@ export function createHeadCore<T extends Record<string, any> = Head>(options: Cr
123123
...(options?.plugins || []),
124124
], ssr)
125125
head.hooks.callHook('init', head)
126+
if (Array.isArray(options.init)) {
127+
options.init
128+
.filter(Boolean)
129+
.forEach(e => head.push(e as T, { tagPriority: 'low' }))
130+
}
126131
return head
127132
}

‎packages/unhead/src/server/createHead.ts

+21-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,32 @@
1-
import type { CreateHeadOptions, Head } from '@unhead/schema'
1+
import type { CreateServerHeadOptions, Head } from '@unhead/schema'
22
import { createHeadCore } from '../createHead'
33
import { ServerEventHandlerPlugin } from './plugins/eventHandlers'
44
import { PayloadPlugin } from './plugins/payload'
55

6-
export function createHead<T extends Record<string, any> = Head>(options: CreateHeadOptions = {}) {
6+
export function createHead<T extends Record<string, any> = Head>(options: CreateServerHeadOptions = {}) {
77
return createHeadCore<T>({
88
...options,
99
// @ts-expect-error untyped
1010
document: false,
11+
init: [
12+
options.disableDefaults
13+
? undefined
14+
: {
15+
htmlAttrs: {
16+
lang: 'en',
17+
},
18+
meta: [
19+
{
20+
charset: 'utf-8',
21+
},
22+
{
23+
name: 'viewport',
24+
content: 'width=device-width, initial-scale=1',
25+
},
26+
],
27+
},
28+
...(options.init || []),
29+
],
1130
plugins: [
1231
...(options.plugins || []),
1332
PayloadPlugin,

‎packages/unhead/test/unit/plugins/infer-seo-meta.test.ts

+9
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { describe, it } from 'vitest'
66
describe('inferSeoMetaPlugin', () => {
77
it('simple', async () => {
88
const head = createHead({
9+
disableDefaults: true,
910
plugins: [InferSeoMetaPlugin()],
1011
})
1112

@@ -40,6 +41,7 @@ describe('inferSeoMetaPlugin', () => {
4041
})
4142
it('conflicts', async () => {
4243
const head = createHead({
44+
disableDefaults: true,
4345
plugins: [InferSeoMetaPlugin()],
4446
})
4547

@@ -71,6 +73,7 @@ describe('inferSeoMetaPlugin', () => {
7173
})
7274
it('empty meta', async () => {
7375
const head = createHead({
76+
disableDefaults: true,
7477
plugins: [InferSeoMetaPlugin()],
7578
})
7679
head.push({
@@ -90,6 +93,7 @@ describe('inferSeoMetaPlugin', () => {
9093
})
9194
it('template params', async () => {
9295
const head = createHead({
96+
disableDefaults: true,
9397
plugins: [InferSeoMetaPlugin()],
9498
})
9599
head.push({
@@ -113,6 +117,7 @@ describe('inferSeoMetaPlugin', () => {
113117

114118
it('title and then remove title', async () => {
115119
const head = createHead({
120+
disableDefaults: true,
116121
plugins: [InferSeoMetaPlugin()],
117122
})
118123
const entry = head.push({
@@ -152,6 +157,7 @@ describe('inferSeoMetaPlugin', () => {
152157

153158
it('no title and then add title', async () => {
154159
const head = createHead({
160+
disableDefaults: true,
155161
plugins: [InferSeoMetaPlugin()],
156162
})
157163
head.push({
@@ -186,6 +192,7 @@ describe('inferSeoMetaPlugin', () => {
186192

187193
it('handles title template', async () => {
188194
const head = createHead({
195+
disableDefaults: true,
189196
plugins: [InferSeoMetaPlugin()],
190197
})
191198
head.push({
@@ -207,6 +214,7 @@ describe('inferSeoMetaPlugin', () => {
207214

208215
it('null title / title template', async () => {
209216
const head = createHead({
217+
disableDefaults: true,
210218
plugins: [InferSeoMetaPlugin()],
211219
})
212220
head.push({
@@ -231,6 +239,7 @@ describe('inferSeoMetaPlugin', () => {
231239

232240
it('multiple title templates', async () => {
233241
const head = createHead({
242+
disableDefaults: true,
234243
plugins: [InferSeoMetaPlugin()],
235244
})
236245

‎packages/unhead/test/util.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ export function createClientHeadWithContext(options?: any) {
99
}
1010

1111
export function createServerHeadWithContext(options?: any) {
12-
return createServerHead(options)
12+
return createServerHead({
13+
disableDefaults: true,
14+
...options,
15+
})
1316
}
1417

1518
// eslint-disable-next-line import/no-mutable-exports

‎packages/vue/src/server.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { CreateHeadOptions, MergeHead } from '@unhead/schema'
1+
import type { CreateServerHeadOptions, MergeHead } from '@unhead/schema'
22
import type { MaybeComputedRef, ReactiveHead, VueHeadClient } from '@unhead/vue'
33
import { createHead as _createServerHead } from 'unhead/server'
44
import { vueInstall } from './install'
@@ -7,7 +7,7 @@ import { VueReactivityPlugin } from './VueReactivityPlugin'
77
export { VueHeadMixin } from './VueHeadMixin'
88
export * from 'unhead/server'
99

10-
export function createHead<T extends MergeHead>(options: Omit<CreateHeadOptions, 'domDelayFn' | 'document'> = {}): VueHeadClient<T> {
10+
export function createHead<T extends MergeHead>(options: CreateServerHeadOptions = {}): VueHeadClient<T> {
1111
const head = _createServerHead<MaybeComputedRef<ReactiveHead<T>>>({
1212
...options,
1313
plugins: [

‎packages/vue/test/unit/e2e/basic.test.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -356,7 +356,9 @@ describe('vue e2e', () => {
356356
})
357357

358358
it('title', async () => {
359-
const ssrHead = createServerHead()
359+
const ssrHead = createServerHead({
360+
disableDefaults: true,
361+
})
360362

361363
// i.e App.vue
362364
ssrHead.push({

‎packages/vue/test/unit/e2e/keys.test.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ describe('vue e2e keys', () => {
1717
}
1818

1919
// ssr render on the index page
20-
const ssrHead = createServerHead()
20+
const ssrHead = createServerHead({
21+
disableDefaults: true,
22+
})
2123

2224
ssrHead.push(IndexSchema)
2325

‎packages/vue/test/unit/ssr/asyncSetup.test.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ import { createSSRApp, ref } from 'vue'
77

88
describe('vue ssr asyncSetup', () => {
99
it('basic', async () => {
10-
const head = createHead()
10+
const head = createHead({
11+
disableDefaults: true,
12+
})
1113
const app = createSSRApp({
1214
async setup() {
1315
const title = ref('initial title')

‎packages/vue/test/unit/ssr/customAugmentation.test.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@ describe('vue ssr custom augmentation', () => {
1414
}
1515
}
1616

17-
const head = createHead<CustomHead>()
17+
const head = createHead<CustomHead>({
18+
disableDefaults: true,
19+
})
1820
const app = createSSRApp({
1921
setup() {
2022
const title = ref('')

‎packages/vue/test/unit/ssr/normalise.test.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import { describe, expect, it } from 'vitest'
44

55
describe('normalise', () => {
66
it('handles booleans nicely', async () => {
7-
const head = createHead()
7+
const head = createHead({
8+
disableDefaults: true,
9+
})
810

911
const fn = () => {}
1012
fn.toString = () => {

‎packages/vue/test/unit/ssr/templateParams.test.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,9 @@ describe('ssr vue templateParams', () => {
146146
})
147147

148148
it('edge case', async () => {
149-
const head = createHead()
149+
const head = createHead({
150+
disableDefaults: true,
151+
})
150152
head.push({
151153
title: '%site.tagline',
152154
// DEV - My page title - My cool site
@@ -180,7 +182,9 @@ describe('ssr vue templateParams', () => {
180182
})
181183

182184
it('entry opt-out', async () => {
183-
const head = createHead()
185+
const head = createHead({
186+
disableDefaults: true,
187+
})
184188
head.push({
185189
title: 'Hello %name',
186190
templateParams: { name: 'World' },

‎packages/vue/test/util.ts

+10-3
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@ export function csrVueAppWithUnhead(dom: JSDOM, fn: () => void | Promise<void>)
3232
}
3333

3434
export async function ssrVueAppWithUnhead(fn: () => void | Promise<void>, options?: CreateHeadOptions) {
35-
const head = createServerHead(options)
35+
const head = createServerHead({
36+
disableDefaults: true,
37+
...options,
38+
})
3639
const app = createSSRApp({
3740
async setup() {
3841
fn()
@@ -45,7 +48,9 @@ export async function ssrVueAppWithUnhead(fn: () => void | Promise<void>, option
4548
}
4649

4750
export async function ssrRenderHeadToString(fn: () => void) {
48-
const head = createServerHead()
51+
const head = createServerHead({
52+
disableDefaults: true,
53+
})
4954
const app = createSSRApp({
5055
setup() {
5156
fn()
@@ -59,7 +64,9 @@ export async function ssrRenderHeadToString(fn: () => void) {
5964
}
6065

6166
export async function ssrRenderOptionsHead(input: any) {
62-
const head = createServerHead()
67+
const head = createServerHead({
68+
disableDefaults: true,
69+
})
6370
const app = createSSRApp({
6471
head() {
6572
return input

0 commit comments

Comments
 (0)
Please sign in to comment.