Is there a way to provide a type union for route names?
For example instead of using
satisfies here I would like to be able to provide the route names list and overwrite the generic string type of route PageMeta:
11 Replies
Hi @hymair. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
----------------------
Short answer: not directly via a Nuxt config switch today. But you can get route-name unions in two practical ways, depending on whether you enable the experimental typed router.
1) With experimental typedPages (recommended)
- Enable typed pages to generate strongly-typed route names from your pages.
- Then, use those generated names to type your meta (e.g., for redirect by name).
Config:
This enables type-safe usage of navigateTo, <NuxtLink>, router.push and lets you use typed params via useRoute('route-name'). It relies on unplugin-vue-router generating the RouteNamedMap, so route names become a typed union. [typedPages guide; typedPages config]
Note: There was work to improve how definePageMeta path overrides interact with the typed router to avoid param duplication, which indicates Nuxt integrates definePageMeta with typed routing. [prevent duplication PR]
2) Manual type augmentation (if you just want to constrain names yourself)
If you want to enforce a custom union for the page route name property (definePageMeta({ name })), you can augment the PageMeta type and narrow its name field yourself:
Then in your page:
This follows the documented pattern for typing custom metadata by augmenting PageMeta. [Typing custom metadata; definePageMeta API]
Caveats and clarity
- The docs show that name is a string in PageMeta; there’s no built-in option to globally replace it with a union unless you augment the type as above. [definePageMeta type]
- For redirect, its type is RouteRecordRedirectOption, which is broader than just { name }. You can still pass { name: '...' } and get type-checks if the route-name union is available (via typedPages) or if you narrow it yourself—but be aware that narrowing redirect globally could be too restrictive. [definePageMeta params: meta/redirect]
Extra context (roadmap-ish)
- There’s a PR adding definePageMeta.props and another allowing global defaults for custom page meta, but nothing in the sources explicitly adds a built-in union for route names outside typedPages. [definePageMeta props PR; global defaults PR]
Summary
- If you want real, automatic unions of route names from files: enable experimental typedPages. [typedPages guide]
- If you want a custom hand-curated union: augment PageMeta in a .d.ts as shown. [Typing custom metadata]
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------
<@344839140551950347> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
I have tried option 2), but type for
name is still just string. @kapa.aiLet me check the knowledge base for you again. One moment! :books:
----------------------
Two likely reasons your augmentation didn’t take effect:
1) Augment in the right TS “project” (Project References)
Nuxt 4 splits types into multiple contexts (app, server, shared). Augmentations must live in the matching context folder so the right tsconfig project picks them up. For PageMeta (definePageMeta), place the .d.ts inside app/ (or another app context path that’s included by the app tsconfig). If you put it elsewhere (e.g., root types/ without being referenced), TypeScript won’t see it.
- Example:
- app/types/page-meta.d.ts
- Or app/index.d.ts
Contents:
Nuxt’s docs call out that “augment types within the correct context” is required when using project references. [Benefits of Project References]
2) Ensure strict TS and that your file is included
- Strict mode should be on (Nuxt defaults to strict: true, but double‑check you didn’t disable it). [Strict Checks]
- Make sure the augmentation file is picked up by the app tsconfig. Keeping it under app/ is the simplest way so Nuxt’s auto-generated tsconfigs include it. Augmenting “outside of these directories will not be recognized.” [Benefits of Project References]
Quick checklist
- File path: inside app/ (e.g., app/types/page-meta.d.ts).
- Has an export {} at the end (as in the docs). [Typing Custom Metadata]
- No duplicate ambient module names elsewhere that re-widen PageMeta.
- IDE restarted after adding the file (so the new project reference is picked up).
Notes and alternatives
- PageMeta.name defaults to string in Nuxt’s types; augmentation is the supported way to narrow it. [definePageMeta type]
- If you want the union to be generated from your pages automatically, enable experimental typedPages. It produces a typed RouteNamedMap you can reference for unions in your own types. [typedPages; prevent duplication PR]
If you’ve already placed the .d.ts under app/ with export {} and it still shows string, please share the file path and contents—you might be in the server/shared context or another declaration could be overriding the augmentation.
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------
<@344839140551950347> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
@kapa.ai the file is in
app/types/page-meta.d.ts. contents are
my other extend types are picked up correctly, but name is not...Let me check the knowledge base for you again. One moment! :books:
----------------------
Thanks for sharing the file. Your augmentation looks mostly right, but there are two common pitfalls with PageMeta/name that match what you’re seeing:
1) PageMeta.name is optional by default — keep it optional when narrowing
- In Nuxt’s types, name is defined as string | undefined (optional). If you redeclare it as required (name: PageName), TypeScript can fail to merge as you expect, and many places will still see the original string. Make it optional in your augmentation so it narrows rather than conflicts.
Example:
This follows the documented pattern for augmenting PageMeta. [Typing custom metadata; definePageMeta API]
2) Ensure the augmentation is picked up in the app context only
- Your file path app/types/page-meta.d.ts is correct for the app project (definePageMeta runs in pages). With Nuxt 4’s project references, augmentations must live in the correct context (app/server/shared). Placing it under app/ is the right move; avoid duplicating the same augmentation in other contexts. [Project References]
A couple of extra checks
- Put export {} at the end of the file (you already do — good). The docs call this out as required so the augmentation is treated as a module. [Typing custom metadata]
- Restart your IDE/TS server after the change so the app tsconfig picks it up.
- Avoid re-widening elsewhere: ensure no other declaration of PageMeta (in your code or dependencies) sets name back to string.
If you want automatic unions for route names
- You can enable the experimental typed pages to generate a RouteNamedMap and use those names across your app. This gives typed route names for navigation and can be referenced for your own unions if you prefer not to maintain them manually. [typedPages]
Why satisfies worked but narrowing didn’t
- satisfies checks your literal against PageName locally, but it doesn’t change the PageMeta.name type globally. Narrowing via augmentation (with name?: PageName) is what enforces the union at the PageMeta level, and keeping it optional ensures proper merging with Nuxt’s declared type. [definePageMeta API; Typing custom metadata]
If after switching to name?: PageName you still see string, please share where you inspect the type (hover in script setup, or in a separate .ts file) — and we can dig deeper.
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------
<@344839140551950347> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
I am doing everything correctly, but
name is still shown as string and not the augmented version. @kapa.aiLet me check the knowledge base for you again. One moment! :books:
----------------------
Thanks for the detailed context — you’re on the right track. Given Nuxt 4’s TS project references, two documented constraints are most likely at play:
- Augmentations must live in the correct context (app/server/shared). You did that (app/types/page-meta.d.ts), which is correct. [Project references]
- Augmentations should merge with existing fields rather than re-declare them incompatibly. In Nuxt, PageMeta.name is defined as an optional string. To narrow it successfully, it should remain optional in your augmentation. [Typing custom metadata; definePageMeta type]
Actionable fixes to try:
1) Make name optional in your augmentation
If you currently have name: PageName (required), change it to name?: PageName so it merges instead of conflicting with the base declaration.
2) Confirm the file is picked up by the app project
- Keep it under app/, which the docs specify for app-context augmentation. [Project references]
- Ensure the file is a module (you already have export {} — good). [Typing custom metadata]
- Restart the IDE/TS server after the change.
3) Be aware of PageMeta’s existing shape
Nuxt’s PageMeta includes many fields (name, path, redirect, props, alias, etc.). You can narrow fields you own, but redirect’s type is RouteRecordRedirectOption, so narrowing it to just { name: PageName } globally can be too restrictive. [definePageMeta params; definePageMeta type]
If after making name optional it still shows string, it points to TypeScript not seeing the augmentation in the app context. In that case, try one of:
- Move the file to app/index.d.ts (same contents) to rule out an include quirk, then restart TS. [Typing custom metadata; Project references]
- Verify there isn’t a second augmentation elsewhere that re-widens name back to string.
Alternative: generate route-name unions automatically
If maintaining a PageName union by hand is cumbersome, enable experimental typedPages to get generated route-name unions from your pages, which flow into typed navigation and can be referenced in your own types. [Experimental features]
----------------------
Note: you can ask me a follow up question by @ mentioning me again :speech_balloon:
----------------------
<@344839140551950347> Kapa.ai is still learning and improving, please let me know how I did by reacting below.