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.
@@ -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
+ };
@@ -0,0 +1,3 @@
1
+ export function useCapitalize(str: string) {
2
+ return str.charAt(0).toUpperCase() + str.slice(1);
3
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "srcdev-nuxt-components",
3
3
  "type": "module",
4
- "version": "9.1.30",
4
+ "version": "9.1.31",
5
5
  "main": "nuxt.config.ts",
6
6
  "types": "types.d.ts",
7
7
  "license": "MIT",