In Vue Router 5, legacy catalogue URLs like /catalog/item/:id must keep working; when do you use a redirect record versus an alias, and what does each cost?
answer
- redirect changes the URL, alias keeps it
- guards run on the target only
- named redirect carries params
- string redirects do not fill params
- client-side is not an HTTP 301
basics
~20 sA redirect replaces the legacy URL with the new one before any guard runs; an alias keeps the legacy URL in the address bar while rendering the target record. Redirect retires old URLs; alias serves two permanent URLs.
solid answer
~40 sA **redirect** record, `{ path: '/catalog/item/:id', redirect: { name: 'product' } }`, sends the navigation to the target: the address bar shows `/products/42`, query and hash carry over, `route.redirectedFrom` remembers the old location, and guards run only on the target. A named redirect passes the matching params along; a function `to => ({ name: 'product', params: { id: to.params.id } })` handles renamed params. A string like `'/products/:id'` is taken literally since Vue Router 4. An **alias**, `{ path: '/products/:id', alias: '/catalog/item/:id' }`, keeps the old URL visible and renders the same record; both paths must share param names. Use redirects to retire old URLs, aliases when both must stay valid, with a canonical link for SEO. Either way this is client-side, not an HTTP 301.
code
ts · 8 linesconst routes = [
{ path: '/products/:id', name: 'product', component: () => import('@/views/ProductPage.vue'),
alias: '/p/:id' }, // short share links keep their URL
// old scheme, same param name: a named redirect carries id across
{ path: '/catalog/item/:id', redirect: { name: 'product' } },
// old scheme with a query id: rebuild the location
{ path: '/catalog/show', redirect: (to) => ({ name: 'product', params: { id: String(to.query.item) }, query: {} }) },
]go deeper
Know that a redirect changes the URL while an alias keeps it and renders the same page.
Explain how named, string and function redirects handle params and query, and why guards on redirect records never run.
Plan a legacy URL migration: redirect functions for renamed params, aliases with canonical links, and measuring leftover traffic with redirectedFrom.
Decide which URL moves need server-side redirects for search engines and which the client router may handle alone.
## Two ways to keep old URLs alive A catalogue that moved from `/catalog/item/:id` to `/products/:id` has bookmarks, emails and search results pointing at the old shape. Vue Router 5 offers two route-table tools, and they differ in what the user sees. | | Redirect | Alias | |---|---|---| | Address bar | changes to the target | keeps the old URL | | Record matched | the target record | the original record | | Guards | target's only | the record's own | | `route.redirectedFrom` | set | not set | | Needs a component | no | the record's component | | Params | carried or rebuilt | must share names | ## Redirect records ```ts { path: '/catalog/item/:id', redirect: { name: 'product' } } ``` - The redirect is resolved **before** the navigation starts, so the legacy URL never becomes the current route and **guards run only on the target**. A `beforeEnter` on the redirect record has no effect. - **Query and hash carry over** unless the redirect sets its own, so `/catalog/item/42?ref=mail` lands on `/products/42?ref=mail`. - A redirect given as a **named location** without a `path` receives the legacy URL's params, and the matcher keeps only those that exist on the target. Here `id` flows across because both records call it `id`. - A **function** receives the target location (and the current one) and returns any location. Use it when param names or shapes change: `redirect: to => ({ name: 'product', params: { id: to.params.itemId } })`. - A **string** redirect is parsed as a path with empty params. Since Vue Router 4, `redirect: '/products/:id'` no longer reuses the source's `id`: the router navigates to a path with a literal `:id` in it. Use a name or a function instead. - The target route exposes `route.redirectedFrom`, useful for logging which legacy URLs still get traffic. - A redirect record needs no `component`, unless it also has `children`. ## Aliases ```ts { path: '/products/:id', name: 'product', component: ProductPage, alias: '/catalog/item/:id' } ``` - The user visiting `/catalog/item/42` **keeps that URL**, and the product page renders as if `/products/42` had been visited. - `alias` takes a string or an array of strings. - Every alias must declare **the same params** as the original path; in development a mismatch is reported as a warning when the route is added, and the alias then resolves with missing or extra params. - In nested routes, start the alias with `/` to make it absolute, and include the parent's params in it. - The router docs' SEO note: two URLs now serve the same content, so publish a **canonical link** pointing at `/products/:id`. ## Choosing for the legacy scheme 1. The old URLs should **disappear** over time: use a **redirect**. Users and crawlers that run the app see the new URL, and the old record stays tiny. 2. Both URLs are **permanent** and meaningful, like a short share link `/p/:id` next to the full path: use an **alias**. 3. The old shape used **different param names** or a query string: use a **redirect function**, which can rebuild the location. 4. The redirect targets a named route from a **catch-all**: pass `params: {}` in the target, or the inherited `pathMatch` param is discarded with a development warning. ## Testing the legacy table - `router.resolve('/catalog/item/42')` does **not** follow redirects: it returns the legacy record itself, which is useful to prove the old path still matches. - To prove where users land, `await router.push('/catalog/item/42')` on a memory-history router and assert that `router.currentRoute.value.fullPath` is `/products/42` and `redirectedFrom.fullPath` is the legacy path. - For aliases, push the alias and assert that the URL stays while `route.name` is the product route. - Keep one test per legacy shape; they document the URL contract better than comments. ## What neither of them is Both run inside the app after `index.html` and the JavaScript have loaded. A crawler or tool that does not run JavaScript receives a normal 200 response for the legacy URL. For permanent moves that matter to search engines, a server-side 301 in front of the app is the durable fix, and the router redirect covers in-app navigation and users on stale links.
- A beforeEnter guard added to the /catalog/item/:id redirect record never runs. Why, and where should the check go?Redirect records are resolved before the navigation begins, and guards run only on the target location. Put the check on the target record, or in a global guard that inspects `to.redirectedFrom` if it must know the navigation came through the legacy URL.
- The team logs how many visits still arrive on legacy URLs. How can the app tell?On a redirected navigation the target route carries `redirectedFrom`, the location the user originally asked for. A global `afterEach` can check `to.redirectedFrom` and report its path. Aliases leave no such marker, since the matched URL is the alias itself; there you compare `to.path` with the record's main path.
saying these in an interview costs you the question
- An alias changes the address bar to the main path.
- A beforeEnter guard on a redirect record runs before redirecting.
- redirect: '/products/:id' fills :id from the old URL.
- A router redirect gives search engines a 301.
- Redirects drop the query string, so tracking params are lost.