Fix: Route Named Already Exists in Vue Router (Nuxt)

Error message:
Route with name \"{name}\" already exists.
navigation 2025-01-25

What Causes This Error?

This error occurs when two or more routes are registered with the same name. Route names must be unique in Vue Router.

The Problem

pages/
├── products.vue        → name: 'products'
└── products/
    └── index.vue       → name: 'products' ← Conflict!

Or manual route registration:

// nuxt.config.ts
hooks: {
  'pages:extend'(pages) {
    pages.push({ path: '/custom', name: 'home', file: '~/pages/custom.vue' })
    // But 'home' might already exist from pages/index.vue
  }
}

The Fix

Option 1: Remove Conflicting File

pages/
├── products/           # Keep folder structure
│   └── index.vue       → /products
└── # Remove products.vue

Or:

pages/
├── products.vue        # Keep single file
└── # Remove products/ folder

Option 2: Rename Route

// nuxt.config.ts
export default defineNuxtConfig({
  hooks: {
    'pages:extend'(pages) {
      const customPage = pages.find(p => p.path === '/custom')
      if (customPage) {
        customPage.name = 'custom-page'  // Unique name
      }
    }
  }
})

Option 3: Override Existing Route

// nuxt.config.ts
export default defineNuxtConfig({
  hooks: {
    'pages:extend'(pages) {
      // Remove existing route first
      const index = pages.findIndex(p => p.name === 'products')
      if (index !== -1) {
        pages.splice(index, 1)
      }

      // Add new one
      pages.push({
        path: '/products',
        name: 'products',
        file: '~/pages/custom-products.vue'
      })
    }
  }
})

Understanding Nuxt Route Names

Automatic Naming Convention

pages/
├── index.vue               → name: 'index'
├── about.vue               → name: 'about'
├── products/
│   ├── index.vue           → name: 'products'
│   └── [id].vue            → name: 'products-id'
└── blog/
    └── [slug]/
        └── index.vue       → name: 'blog-slug'

File vs Folder Conflict

pages/
├── products.vue            → name: 'products', path: '/products'
└── products/
    └── index.vue           → name: 'products', path: '/products'
                              ↑ CONFLICT!

Resolution: Choose one approach:

  • Use products.vue for simple page
  • Use products/index.vue if you’ll have nested routes

Module Routes

// modules/my-module/index.ts
export default defineNuxtModule({
  setup(options, nuxt) {
    nuxt.hook('pages:extend', (pages) => {
      // Check if route name already exists
      const existingRoute = pages.find(p => p.name === 'my-route')

      if (!existingRoute) {
        pages.push({
          path: '/my-route',
          name: 'my-route',
          file: resolve('./runtime/pages/MyPage.vue')
        })
      } else {
        console.warn('Route "my-route" already exists')
      }
    })
  }
})

Debugging Duplicate Routes

// nuxt.config.ts
export default defineNuxtConfig({
  hooks: {
    'pages:extend'(pages) {
      // Find duplicates
      const names = pages.map(p => p.name)
      const duplicates = names.filter((name, index) =>
        names.indexOf(name) !== index
      )

      if (duplicates.length > 0) {
        console.warn('Duplicate route names:', [...new Set(duplicates)])
        console.warn('Routes:', pages.map(p => ({
          name: p.name,
          path: p.path,
          file: p.file
        })))
      }
    }
  }
})

Custom Route Names

Override automatic naming:

<!-- pages/products.vue -->
<script setup>
definePageMeta({
  name: 'products-list'  // Custom name instead of 'products'
})
</script>

Alias Routes

Use aliases instead of duplicate routes:

// nuxt.config.ts
export default defineNuxtConfig({
  hooks: {
    'pages:extend'(pages) {
      const productsRoute = pages.find(p => p.path === '/products')
      if (productsRoute) {
        productsRoute.alias = ['/items', '/shop']
      }
    }
  }
})

Or in page:

<!-- pages/products.vue -->
<script setup>
definePageMeta({
  alias: ['/items', '/shop']
})
</script>

Layer Conflicts

When using Nuxt layers:

// nuxt.config.ts
export default defineNuxtConfig({
  extends: ['./base-layer'],

  hooks: {
    'pages:extend'(pages) {
      // Check for conflicts with layer routes
      console.log('All routes:', pages.map(p => p.name))
    }
  }
})

Quick Checklist

  • No products.vue AND products/index.vue for same route
  • Manual routes have unique names
  • Module routes check for existing names before adding
  • Use definePageMeta({ name }) for custom names
  • Use aliases instead of duplicate routes
  • Check layer routes for conflicts