Course outline

Vue / Intermediate: feature state and effect ownership / Lesson 8

Vue Router and route-owned state

Model navigation as a history and parameter contract while keeping route changes distinct from server authorization.

About 4 min readContent reviewed
On this page

Lesson goals

  • Define routes, named navigation, and history assumptions.
  • React to reused route components without losing ownership.

Define destinations and the user journey

A router maps locations to components and coordinates navigation history. A route parameter should usually identify a record that the data layer can load, rather than carry a large copied object. Back, direct entry, missing records, and invalid ids are part of the feature contract. This lesson uses the Vue Router 4 API family compatible with the Vue 3 baseline; pin an appropriate package release rather than assuming a moving latest version. RouterLink and RouterView provide navigation and matched rendering boundaries, while the application entry installs the router plugin before mounting.

Choose history behavior explicitly

The example uses memory history for a controlled fixture. A real SSR integration needs a fresh request-owned router rather than this fixture singleton. Memory history does not automatically read the browser URL and needs explicit navigation to establish its initial location. A browser application commonly uses web history, which requires hosting fallback behavior for direct nested URLs; that deployment requirement is separate from component code. Hash history has different URL and server tradeoffs. The route components below are app-defined SFCs, not supplied by the router. Keep missing-record and error rendering inside a clear feature boundary rather than assuming every matched path has valid data.

javascript A Vue Router 4 route table using memory history
import { createRouter, createMemoryHistory } from "vue-router"
import NoteList from "./NoteList.vue"
import NoteDetail from "./NoteDetail.vue"

export const router = createRouter({
  history: createMemoryHistory(),
  routes: [
    { path: "/notes", name: "notes", component: NoteList },
    { path: "/notes/:id", name: "note", component: NoteDetail }
  ]
})

The app must install this router, supply both SFCs, and push an initial location in its controlled setup. This is a configuration fragment, not a running browser application. No navigation, server, or route request is performed here.

Observe the parameter that defines the task

Navigating from one id to another can reuse the same route component instance. A mounted hook alone therefore may not reload the intended record. In Composition API, useRoute exposes the reactive route; watch the specific parameter or use a relevant route-update guard rather than watching the entire object indiscriminately. Validate the parameter and handle missing data. Keep request invalidation aligned with id changes so a slow old result cannot overwrite the new destination. Query and hash changes may have different semantics and should not trigger unrelated work merely because the route object changed.

Pass a narrow navigation proposal

Named navigation lets the router encode declared parameters according to the route contract and keeps UI code from assembling path syntax manually. The example reads the current id for display and exposes a button that proposes navigation to a synthetic record. It does not fetch that record. A reusable list row can instead emit an id to a route-owning parent, allowing the row to be tested without a router. Await or inspect navigation outcomes where the product needs to handle refusal or redirection, and keep duplicate-submission behavior explicit rather than relying on repeated clicks.

vue A route-aware navigation boundary
<script setup>
import { computed } from "vue"
import { useRoute, useRouter } from "vue-router"
const route = useRoute()
const router = useRouter()
const currentId = computed(() => String(route.params.id ?? ""))
function openExample() {
  return router.push({ name: "note", params: { id: "n2" } })
}
</script>

<template>
  <p>Current note: {{ currentId }}</p>
  <button type="button" @click="openExample">Open example note</button>
</template>

Requires installation of the preceding compatible router with its named note route. The computed id follows the route parameter. Framework navigation and browser history were not executed for this illustrative component.

Keep navigation policy and access control distinct

Guards can coordinate client navigation, unsaved-change prompts, and redirects, but a user can call an API without passing through the interface. Server authorization must protect the actual resource. Direct-entry handling also needs correct loading, error, and not-found states. Update document titles and manage meaningful focus after route transitions so a client-side navigation remains understandable with a keyboard or screen reader. Do not assume a URL change alone announces a new task. Test intended history behavior with a controlled router and representative direct links, then verify hosting behavior separately under the project’s deployment process.

Common errors and fixes

Changing the id displays the old record

Why it happens
The component is reused and loading ran only on mount.
How to fix it
Observe the relevant parameter and invalidate obsolete requests.
Prevent it next time
Test navigation between ids on the same route record.

Nested links fail on a direct page load

Why it happens
Web-history hosting fallback is missing or incorrect.
How to fix it
Review the authorized hosting configuration and not-found behavior.
Prevent it next time
Document history-mode deployment assumptions.

A route guard is treated as data authorization

Why it happens
Client navigation is confused with the protected server operation.
How to fix it
Enforce permission at the API or data-service boundary.
Prevent it next time
Test direct resource access independently.

Check your understanding

1. Why might onMounted not run again for /notes/n1 to /notes/n2?

Show worked solution

The same route component instance can be reused. Watch the id source or use a suitable route-update integration, and verify loading and cancellation for the new task. Forcing a remount is a separate design choice with state-lifetime consequences.

2. A client guard blocks an editor page. Does it secure the edit API?

Show worked solution

No. The API must authenticate and authorize each protected action regardless of client routing. The guard can improve navigation experience but is not the server trust boundary.

Key takeaways

  • Route parameters define task identity and can change without remounting.
  • History mode has explicit hosting assumptions.
  • Guards and server permission are different controls.

Official references