srcdev-nuxt-components 9.1.30 → 9.1.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# useCanonicalUrl Composable
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
`useCanonicalUrl` sets a `<link rel="canonical">` tag in the page `<head>` using the current
|
|
6
|
+
route path and the app's configured canonical host. Call it once in the default layout so every
|
|
7
|
+
page gets a canonical URL automatically.
|
|
8
|
+
|
|
9
|
+
**This composable ships inside the `srcdev-nuxt-components` layer** (`app/composables/useCanonicalUrl.ts`).
|
|
10
|
+
Consuming apps get it via Nuxt's layer auto-import — **do not create a local copy** in the consuming app.
|
|
11
|
+
|
|
12
|
+
## Prerequisites
|
|
13
|
+
|
|
14
|
+
- `NUXT_PUBLIC_CANONICAL_HOST` env var set to the production domain (e.g. `myapp.co.uk`).
|
|
15
|
+
|
|
16
|
+
## Setup in the consuming app
|
|
17
|
+
|
|
18
|
+
### 1. Runtime config
|
|
19
|
+
|
|
20
|
+
`canonicalHost` must be declared in `runtimeConfig.public` in `nuxt.config.ts`. The standard
|
|
21
|
+
scaffold pattern reads it from an env var with a Vercel preview URL as the fallback:
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
// nuxt.config.ts
|
|
25
|
+
const PROD_HOST = "myapp.co.uk"
|
|
26
|
+
const canonicalHost = process.env.NUXT_PUBLIC_CANONICAL_HOST ?? "myapp.vercel.app"
|
|
27
|
+
|
|
28
|
+
export default defineNuxtConfig({
|
|
29
|
+
runtimeConfig: {
|
|
30
|
+
public: {
|
|
31
|
+
canonicalHost,
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
})
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Set `NUXT_PUBLIC_CANONICAL_HOST` in `.env` locally and in your hosting provider's environment
|
|
38
|
+
variables for production. On Vercel preview deployments the fallback URL is used, so canonical
|
|
39
|
+
tags point to preview — that is intentional and correct.
|
|
40
|
+
|
|
41
|
+
### 2. Call in the default layout
|
|
42
|
+
|
|
43
|
+
Call `useCanonicalUrl()` at the top of `<script setup>` in `app/layouts/default.vue`. This runs
|
|
44
|
+
on every page without needing to repeat it per-page:
|
|
45
|
+
|
|
46
|
+
```vue
|
|
47
|
+
<!-- app/layouts/default.vue -->
|
|
48
|
+
<script setup lang="ts">
|
|
49
|
+
useCanonicalUrl()
|
|
50
|
+
</script>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 3. Node types (required for `process.env` in nuxt.config)
|
|
54
|
+
|
|
55
|
+
The `process.env` access at the top of `nuxt.config.ts` requires Node type definitions. Ensure
|
|
56
|
+
`@types/node` is installed and `"node"` is in the types array:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
npm i --save-dev @types/node
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
// nuxt.config.ts — typescript block
|
|
64
|
+
typescript: {
|
|
65
|
+
tsConfig: { compilerOptions: { types: ["srcdev-nuxt-components", "node"] } },
|
|
66
|
+
},
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Composable reference
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
export const useCanonicalUrl = () => {
|
|
73
|
+
const route = useRoute();
|
|
74
|
+
const config = useRuntimeConfig();
|
|
75
|
+
|
|
76
|
+
const canonicalHost = config.public.canonicalHost;
|
|
77
|
+
const canonicalUrl = `https://${canonicalHost}${route.path}`;
|
|
78
|
+
|
|
79
|
+
useHead({
|
|
80
|
+
link: [{ rel: "canonical", href: canonicalUrl }],
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
return { canonicalUrl };
|
|
84
|
+
};
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Notes
|
|
88
|
+
|
|
89
|
+
- The composable returns `{ canonicalUrl }` if you need to read the value elsewhere (e.g. for
|
|
90
|
+
structured data), but in most cases you call it purely for the side effect.
|
|
91
|
+
- On pages with query strings or hash fragments only the `route.path` (pathname) is used, which
|
|
92
|
+
is the correct canonical behaviour.
|
package/.claude/skills/index.md
CHANGED
|
@@ -42,6 +42,7 @@ Each skill is a single markdown file named `<area>-<task>.md`.
|
|
|
42
42
|
├── robots-env-aware.md — @nuxtjs/robots: allow crawling on prod domain only, block on preview/staging via env var
|
|
43
43
|
├── new-app-scaffold.md — scaffold a new Nuxt consumer app extending this layer (package.json, nuxt.config, app structure, CLAUDE.md)
|
|
44
44
|
├── release-notes.md — produce release notes as a fenced markdown block from git log
|
|
45
|
+
├── composable-canonical-url.md — useCanonicalUrl: set <link rel="canonical"> from runtimeConfig.public.canonicalHost; layout setup, node types
|
|
45
46
|
├── composable-whatsapp.md — useWhatsApp: open pre-filled wa.me link from form payload; runtime config, security, usage
|
|
46
47
|
├── composable-zod-validation.md — useZodValidation: schema-driven form validation, error binding, submit flow, API error push
|
|
47
48
|
├── composable-colour-scheme.md — useColourScheme: reactive light/dark/auto switching, localStorage persistence, runtime config
|
|
@@ -124,7 +124,7 @@ export default defineNuxtConfig({
|
|
|
124
124
|
strict: true,
|
|
125
125
|
shim: true,
|
|
126
126
|
typeCheck: false,
|
|
127
|
-
tsConfig: { compilerOptions: { types: ["srcdev-nuxt-components"] } },
|
|
127
|
+
tsConfig: { compilerOptions: { types: ["srcdev-nuxt-components", "node"] } },
|
|
128
128
|
},
|
|
129
129
|
eslint: { config: {} },
|
|
130
130
|
security: {
|
|
@@ -231,7 +231,7 @@ app/
|
|
|
231
231
|
}
|
|
232
232
|
```
|
|
233
233
|
|
|
234
|
-
**`app/layouts/default.vue`** — minimal:
|
|
234
|
+
**`app/layouts/default.vue`** — minimal, with canonical URL head tag:
|
|
235
235
|
|
|
236
236
|
```vue
|
|
237
237
|
<template>
|
|
@@ -239,6 +239,10 @@ app/
|
|
|
239
239
|
<main><slot></slot></main>
|
|
240
240
|
</div>
|
|
241
241
|
</template>
|
|
242
|
+
|
|
243
|
+
<script setup lang="ts">
|
|
244
|
+
useCanonicalUrl()
|
|
245
|
+
</script>
|
|
242
246
|
```
|
|
243
247
|
|
|
244
248
|
**`app/pages/index.vue`** — placeholder with `useSeoMeta`.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export const useCanonicalUrl = () => {
|
|
2
|
+
const route = useRoute();
|
|
3
|
+
const config = useRuntimeConfig();
|
|
4
|
+
|
|
5
|
+
const canonicalHost = config.public.canonicalHost;
|
|
6
|
+
|
|
7
|
+
// Construct the canonical URL
|
|
8
|
+
const canonicalUrl = `https://${canonicalHost}${route.path}`;
|
|
9
|
+
|
|
10
|
+
// Set the canonical link in the head
|
|
11
|
+
useHead({
|
|
12
|
+
link: [
|
|
13
|
+
{
|
|
14
|
+
rel: 'canonical',
|
|
15
|
+
href: canonicalUrl,
|
|
16
|
+
},
|
|
17
|
+
],
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
return {
|
|
21
|
+
canonicalUrl,
|
|
22
|
+
};
|
|
23
|
+
};
|