Back to Home

Back Button in Vue: Correct Implementation

Technical Guide to the Correct Implementation of the Back Button in Vue.js Applications. Errors in Using router.push() Are Analyzed, Solutions for Edge Cases Are Proposed, and a Universal Component with Fallback and Confirmation.

Back Button in Vue: Pitfalls and Solution
Advertisement 728x90

# How to Properly Implement the 'Back' Button in Vue: A Technical Guide for Developers

Implementing the 'Back' button in Vue.js applications often seems straightforward, but in practice, it leads to serious navigation and browser history issues. Many developers mistakenly use router.push() instead of methods designed for moving through the existing history stack. This creates duplicate entries, breaks the browser's Back button behavior, and forms infinite transition loops. This article provides a technically precise guide to correctly implementing the 'Back' button, taking into account edge cases and user experience.

Why router.push() Is Not 'Back'

The router.push() method adds a new entry to the browser history. It's designed for navigating forward to a new route, even if that route was visited before. Unlike it, router.back() and router.go(-1) don't create new entries—they simply move the pointer through the existing history stack.

When you use router.push() for the 'Back' button, you're actually simulating a forward navigation, disguising it as a return. This leads to the following sequence:

Google AdInline article slot
  • User: Home → Product List → Product A
  • Clicks your 'Back' button with router.push('/list')
  • History becomes: Home → Product List → Product A → Product List (duplicate)

Now, if the user clicks the browser's Back button, they'll go back to the 'Product A' page, not 'Home'. Clicking your button again will trigger push once more, and the cycle continues.

Main Navigation Methods in Vue Router

Vue Router provides three key methods for managing history:

  • router.push(location) — adds a new entry to the history.
  • router.replace(location) — replaces the current entry without adding a new one.
  • router.back() / router.go(-1) — moves back through the existing history.

For the 'Back' button in the UI, the only correct choice is router.back() or router.go(-1). They fully match the browser button's behavior and don't violate the stack's integrity.

Google AdInline article slot

Handling Edge Cases: When There's No History

In practice, users often land on a page directly—via an external link, page refresh, or after closing a tab. In such cases, the history stack may be empty or contain only one entry. Calling router.back() won't throw an error, but nothing will happen, leaving the user hanging.

To avoid this, provide a fallback route (fallbackRoute). The logic should check if back navigation is possible and, if not, redirect to a safe route—for example, the home page or parent section.

Universal 'Back' Button Component

Here's a production-ready component that supports all necessary scenarios:

Google AdInline article slot
<script setup lang="ts">
import { computed } from 'vue'
import { useRouter, useRoute, type RouteLocationRaw } from 'vue-router'

const props = defineProps<{
  fallbackRoute?: RouteLocationRaw
  beforeNavigate?: () => boolean | Promise<boolean>
  beforeRouterPush?: () => void
}>()

const router = useRouter()
const route = useRoute()

const isSamePage = computed<boolean>(() => {
  const fromRoute = router.options.history.state?.back

  if (!fromRoute || typeof fromRoute !== 'string') return false

  const currentPrefix = route.path.split('/')[1]
  const fromPrefix = fromRoute.split('/')[1]

  return currentPrefix === fromPrefix
})

const goBack = async () => {
  if (props.beforeNavigate) {
    const shouldProceed = await props.beforeNavigate()
    if (!shouldProceed) return
  }

  if (props.beforeRouterPush) {
    props.beforeRouterPush()
    return
  }

  if (isSamePage.value) {
    router.back()
  } else if (props.fallbackRoute) {
    void router.push(props.fallbackRoute)
  } else {
    void router.push({ name: 'home' })
  }
}
</script>

<template>
  <q-btn
    icon="arrow_back"
    @click="goBack"
    aria-label="Onzad"
  />
</template>

This component solves several tasks at once:

  • Supports confirmation before leaving (e.g., saving a draft)
  • Distinguishes internal and external transitions by URL prefixes
  • Provides a fallback route
  • Allows fully overriding the logic if needed

Key Points

  • Don't use router.push() for the 'Back' button — it violates the browser history model.
  • Always provide fallback behavior for direct access cases.
  • Check the transition context: returning within a section and from outside require different logic.
  • Support canceling navigation via async hooks — critical for forms and editors.
  • Sync behavior with the browser's native Back button — the user shouldn't notice a difference.

— Editorial Team

Advertisement 728x90

Read Next