N
Nuxt2w ago
Mike

Type safe props with `NuxtPage`

Hello, My pages are structured something like this:
team/
├─ [id].vue
├─ [id]/
│ ├─ index.vue
team/
├─ [id].vue
├─ [id]/
│ ├─ index.vue
In [id].vue I fetch a user, show a header and render a <NuxtPage> with the fetched user as a prop. I do this to avoid having to fetch the user on all sub pages, since they all need it. So my [id].vue looks something like this:
<script setup lang="ts">
const {data: user} = useFetch('/api/user')
</script>
<template>
<PageHeader />
<NuxtPage :user />
</template>
<script setup lang="ts">
const {data: user} = useFetch('/api/user')
</script>
<template>
<PageHeader />
<NuxtPage :user />
</template>
all the files in [id]/ then start out something like this:
<script setup lang="ts">
import type { User } from "~/types/user"

const user = computed<User>(
() => useAttrs().user as User,
)
</script>
<script setup lang="ts">
import type { User } from "~/types/user"

const user = computed<User>(
() => useAttrs().user as User,
)
</script>
Now my question is, how can I make this type safe? I have enabled strictTemplates and fallthroughAttributes but I am getting this type error when compiling:
error TS2353: Object literal may only specify known properties, and 'user' does not exist in type 'Partial<{ keepalive: boolean | KeepAliveProps; transition: boolean | TransitionProps; pageKey: string | ((route: RouteLocationNormalizedLoaded) => string); }> & Omit<...>'.

153 <NuxtPage :user :status />
error TS2353: Object literal may only specify known properties, and 'user' does not exist in type 'Partial<{ keepalive: boolean | KeepAliveProps; transition: boolean | TransitionProps; pageKey: string | ((route: RouteLocationNormalizedLoaded) => string); }> & Omit<...>'.

153 <NuxtPage :user :status />
Full type safety would be great but a way to silence this error would also work
3 Replies
kapa.ai
kapa.ai2w ago
Hi @Mike. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave: ---------------------- To address your question about type-safe props with NuxtPage, I'll provide some insights based on the available knowledge sources. The error you're encountering is related to TypeScript's strict type checking for the NuxtPage component. By default, NuxtPage doesn't expect a user prop, which is why you're getting the error. To make this type-safe and resolve the error, you have a few options: 1. Use the experimental typedPages feature: Nuxt 3 offers an experimental feature called typedPages that enables a new experimental typed router. This feature can help with type-safe routing and prop passing. To enable it, you can add the following to your nuxt.config.ts:
export default defineNuxtConfig({
experimental: {
typedPages: true
}
})
export default defineNuxtConfig({
experimental: {
typedPages: true
}
})
Docs > Guide > Going-further > Experimental Features provides more information about this feature. 2. Use custom props: NuxtPage accepts custom props that you can pass down the component hierarchy. These custom props are accessible via attrs in the Nuxt app. You can modify your [id].vue file like this:
<template>
<PageHeader />
<NuxtPage :user="user" />
</template>
<template>
<PageHeader />
<NuxtPage :user="user" />
</template>
Then in your child components, you can access the user prop using useAttrs() in the <script setup> section:
<script setup lang="ts">
import type { User } from "~/types/user"

const attrs = useAttrs()
const user = computed<User>(() => attrs.user as User)
</script>
<script setup lang="ts">
import type { User } from "~/types/user"

const attrs = useAttrs()
const user = computed<User>(() => attrs.user as User)
</script>
This approach is mentioned in the Docs > Api > Components > <NuxtPage> under the "Custom Props" section. 3. Use definePageMeta: Another approach is to use definePageMeta to define the expected props for your page components. This can help with type inference. In your child components, you can do:
<script setup lang="ts">
import type { User } from "~/types/user"

definePageMeta({
props: {
user: {
type: Object as PropType<User>,
required: true
}
}
})

const props = defineProps<{
user: User
}>()
</script>
<script setup lang="ts">
import type { User } from "~/types/user"

definePageMeta({
props: {
user: {
type: Object as PropType<User>,
required: true
}
}
})

const props = defineProps<{
user: User
}>()
</script>
This approach combines information from Docs > Api > Utils > define-page-meta and general Vue 3 TypeScript practices. It's worth noting that the typedPages feature is experimental and may not be fully stable. The custom props approach is currently the most straightforward way to achieve what you're looking for while maintaining type safety. Remember to run nuxi typecheck command to check your types after making these changes, as mentioned in Docs > Guide > Concepts > TypeScript. ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
kapa.ai
kapa.ai2w ago
<@118401999526952967> Kapa.ai is still learning and improving, please let me know how I did by reacting below.
Mike
MikeOP2w ago
I already have typedPages: true and use definePageMeta didn't work

Did you find this page helpful?