In Vue Router 5, what do a route record's props: true, object and function modes pass to the component, and when does each fit?
answer
- decouple the component from useRoute
- true passes params only
- object passes static values
- function can cast and add query
- called inside RouterView's render
basics
~10 sprops: true passes route.params as props, an object passes fixed props as-is, and a function receives the route and returns props, which is how you cast ids to numbers or pass query values.
solid answer
~40 sThe record's `props` option lets `RouterView` hand route data to the component as ordinary props, so the component no longer calls `useRoute()` and becomes easier to reuse and test. `props: true` passes `route.params` as they are: strings, arrays for repeatable params, and nothing from the query. An **object** is passed as-is, which suits static flags like `{ compact: true }`. A **function** receives the route and returns the props, the place to cast `Number(route.params.id)` or pass `route.query.tab`. `RouterView` calls the function while it renders, so it runs on every route change; keep it a pure mapping from route to props, as the docs advise. With named views, `props` becomes a per-view map. Params you pass but do not declare fall through as attributes on the component's root element.
code
vue · 9 lines<script setup lang="ts">
// record: { path: '/products/:id', component: ProductPage,
// props: route => ({ id: Number(route.params.id) }) }
const props = defineProps<{ id: number }>()
</script>
<template>
<h1>Product #{{ props.id }}</h1>
</template>go deeper
Know that props: true passes route params as component props so the component can skip useRoute().
Compare the boolean, object and function modes, including string values, missing query, and props updating on reuse.
Use function mode to cast and default inputs, keep it pure, and catch undeclared params leaking as attributes.
Set a convention that route components receive typed props, so views stay testable without a router.
## Why route props exist A component that calls `useRoute()` and reads `route.params.id` only works on URLs shaped like its route. The same product card cannot be dropped into a modal or a test without a router. The record's `props` option moves that coupling into the route table: `RouterView` computes props from the route and passes them to the component, which just declares `defineProps`. ## The three modes | Mode | Record | Component receives | |---|---|---| | Boolean | `props: true` | `route.params`, unchanged | | Object | `props: { compact: true }` | that object, as-is | | Function | `props: route => ({ ... })` | whatever the function returns | ### Boolean mode - Passes **every path param** under its own name: `/products/:id` gives the prop `id`. - Values are strings, or arrays for `+` and `*` params. The component should declare `id: string`, not `number`. - Query and hash are **not** included. ### Object mode - The object is passed as the props, with no link to the route. - It suits static configuration of a reused component: the same `ProductList` rendered at `/sale` with `{ onSaleOnly: true }` and at `/products` without it. ### Function mode - The function receives the resolved route and returns an object. - Use it to **cast** (`Number(route.params.id)`), to **rename**, to **combine** params with query values or constants, and to apply defaults. - The router docs ask you to keep it **stateless**. `RouterView` calls it inside its own render function, so it runs on every route change and on any other re-render of that `RouterView`. Reactive state read inside it silently becomes a render dependency of `RouterView`, plain variables are not tracked at all, and side effects would repeat unpredictably. If props depend on app state, read that state in the component, or wrap the view in a small component that reads it. ```ts { path: '/products/:id', component: ProductPage, props: (route) => ({ id: Number(route.params.id), tab: typeof route.query.tab === 'string' ? route.query.tab : 'overview', }), } ``` ## Behaviour worth knowing 1. **Props update on reuse.** Going from `/products/1` to `/products/2` reuses the component and updates its props; watch `() => props.id` to reload data. 2. **Undeclared props fall through.** `RouterView` passes everything the mode produces. A param the component does not declare becomes a fallthrough attribute: with `props: true`, an undeclared `id` renders as an `id="42"` HTML attribute on a single-root component's root element. 3. **Named views need a map.** A record with `components: { default, sidebar }` takes `props: { default: true, sidebar: false }`; a bare `true` there applies to every view. 4. **Each record owns its own props.** In nested routes, a parent's `props` option does not flow into the child; each record declares its own. 5. **Redirect records have none.** A record that only redirects renders nothing, so `props` there does nothing. ## RouterView attributes are not a substitute Attributes on `<RouterView>`, or on the `<component :is>` inside its slot, go to **every** view that renders there. The router docs discourage that for data: every view would have to declare the prop. Route-level `props` keeps each view's inputs next to its own record. ## The payoff in tests The reason to bother shows up in unit tests and component showcases: - A props-driven `ProductPage` mounts with `mount(ProductPage, { props: { id: 42 } })`, with no router, no memory history and no navigation to await. - The props function itself is a plain function of a route object, so it can be tested with `router.resolve('/products/42?tab=specs')` as input and a simple equality check on the output. - A component that calls `useRoute()` needs a real router installed and a navigation completed before every assertion. ## Choosing - `props: true` when the params already have the right names and types for the component. - Function mode when anything must be converted, renamed, defaulted or taken from the query, which in practice is most detail pages with numeric ids. - Object mode for static flags that make one component serve several routes. - Keep `useRoute()` for components that genuinely care about the whole route, such as breadcrumbs.
- The router docs say a props function should stay stateless. Why, given how RouterView uses it?`RouterView` calls the function inside its render, so it runs on every navigation and on any other re-render of the view. Reactive state read there becomes a hidden render dependency of `RouterView`, plain variables are not tracked, and side effects repeat. Keep it a pure mapping from route to props and read app state in the component or a small wrapper.
- With props: true on /products/:id, the rendered root div suddenly has id="42". What happened?The component did not declare an `id` prop, so the value passed by `RouterView` became a fallthrough attribute and landed on the root element. Declare the prop, or use a props function that passes only what the component declares.
saying these in an interview costs you the question
- props: true also passes query parameters as props.
- props: true converts numeric params to numbers.
- A props function runs once when the route table is created and is cached.
- Object mode maps route params onto the object's keys.
- Passing a prop on RouterView is the recommended way to feed one view.