Vue Router

Learn how to add routing to your Vue.js applications using Vue Router. Routing allows you to create single-page applications with multiple pages and navigation.

What is Vue Router?

Vue Router is the official routing library for Vue.js. It enables you to build single-page applications (SPAs) with multiple views, navigation, and browser history management.

Setting Up Vue Router

Installation

First, install Vue Router in your project:

npm install vue-router@4

Basic Configuration

Create a router configuration file:

// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import About from '../views/About.vue'
import Contact from '../views/Contact.vue'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/about',
    name: 'About',
    component: About
  },
  {
    path: '/contact',
    name: 'Contact',
    component: Contact
  }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

export default router

Integration with Vue App

Update your main application file to use the router:

// main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'

const app = createApp(App)

app.use(router)
app.mount('#app')

Creating Route Components

Create your page components in a views directory:

<!-- views/Home.vue -->
<template>
  <div class="home">
    <h1>Welcome to Our Website</h1>
    <p>This is the home page of our Vue.js application.</p>
  </div>
</template>

<script>
export default {
  name: 'Home'
}
</script>

<style scoped>
.home {
  padding: 20px;
  text-align: center;
}
</style>
<!-- views/About.vue -->
<template>
  <div class="about">
    <h1>About Us</h1>
    <p>Learn more about our company and what we do.</p>
  </div>
</template>

<script>
export default {
  name: 'About'
}
</script>

Navigation

Router Links

Use <router-link> for navigation between routes:

<!-- components/Navigation.vue -->
<template>
  <nav class="navigation">
    <router-link to="/" class="nav-link">Home</router-link>
    <router-link to="/about" class="nav-link">About</router-link>
    <router-link to="/contact" class="nav-link">Contact</router-link>
  </nav>
</template>

<script>
export default {
  name: 'Navigation'
}
</script>

<style scoped>
.navigation {
  display: flex;
  gap: 20px;
  padding: 20px;
  background-color: #f8f9fa;
}

.nav-link {
  text-decoration: none;
  color: #333;
  padding: 8px 16px;
  border-radius: 4px;
  transition: background-color 0.3s;
}

.nav-link:hover,
.nav-link.router-link-active {
  background-color: #42b883;
  color: white;
}
</style>

Programmatic Navigation

Navigate using JavaScript methods:

<template>
  <div class="dashboard">
    <h1>Dashboard</h1>
    <button @click="goToProfile">View Profile</button>
    <button @click="goBack">Go Back</button>
    <button @click="goForward">Go Forward</button>
  </div>
</template>

<script setup>
import { useRouter } from 'vue-router'

const router = useRouter()

const goToProfile = () => {
  router.push('/profile')
}

const goBack = () => {
  router.back()
}

const goForward = () => {
  router.forward()
}
</script>

Dynamic Routes

Create routes that accept parameters:

// router/index.js
const routes = [
  // ... other routes
  {
    path: '/user/:id',
    name: 'UserProfile',
    component: () => import('../views/UserProfile.vue')
  },
  {
    path: '/blog/:slug',
    name: 'BlogPost',
    component: () => import('../views/BlogPost.vue')
  }
]

Accessing Route Parameters

<!-- views/UserProfile.vue -->
<template>
  <div class="user-profile">
    <h1>User Profile</h1>
    <p>User ID: {{ userId }}</p>
    <div v-if="user">
      <h2>{{ user.name }}</h2>
      <p>{{ user.email }}</p>
    </div>
    <div v-else>
      <p>Loading user data...</p>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import { useRoute } from 'vue-router'

const route = useRoute()
const userId = route.params.id
const user = ref(null)

onMounted(async () => {
  // Fetch user data based on userId
  user.value = await fetchUser(userId)
})

async function fetchUser(id) {
  // Simulate API call
  return {
    id: id,
    name: 'John Doe',
    email: '[email protected]'
  }
}
</script>

Nested Routes

Create child routes within parent components:

// router/index.js
const routes = [
  {
    path: '/dashboard',
    component: () => import('../views/Dashboard.vue'),
    children: [
      {
        path: '',
        name: 'DashboardHome',
        component: () => import('../views/DashboardHome.vue')
      },
      {
        path: 'profile',
        name: 'DashboardProfile',
        component: () => import('../views/DashboardProfile.vue')
      },
      {
        path: 'settings',
        name: 'DashboardSettings',
        component: () => import('../views/DashboardSettings.vue')
      }
    ]
  }
]

Parent Component with Router View

<!-- views/Dashboard.vue -->
<template>
  <div class="dashboard">
    <header class="dashboard-header">
      <h1>Dashboard</h1>
      <nav class="dashboard-nav">
        <router-link to="/dashboard">Home</router-link>
        <router-link to="/dashboard/profile">Profile</router-link>
        <router-link to="/dashboard/settings">Settings</router-link>
      </nav>
    </header>
    
    <main class="dashboard-content">
      <router-view />
    </main>
  </div>
</template>

<script>
export default {
  name: 'Dashboard'
}
</script>

<style scoped>
.dashboard {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

.dashboard-header {
  background-color: #f8f9fa;
  padding: 20px;
  border-bottom: 1px solid #ddd;
}

.dashboard-nav {
  margin-top: 15px;
  display: flex;
  gap: 15px;
}

.dashboard-nav a {
  text-decoration: none;
  color: #333;
  padding: 8px 16px;
  border-radius: 4px;
}

.dashboard-nav a.router-link-active {
  background-color: #42b883;
  color: white;
}

.dashboard-content {
  flex: 1;
  padding: 20px;
}
</style>

Route Guards

Protect routes with navigation guards:

// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import { useUserStore } from '@/stores/user'

const routes = [
  {
    path: '/',
    name: 'Home',
    component: () => import('../views/Home.vue')
  },
  {
    path: '/dashboard',
    name: 'Dashboard',
    component: () => import('../views/Dashboard.vue'),
    meta: { requiresAuth: true }
  },
  {
    path: '/admin',
    name: 'Admin',
    component: () => import('../views/Admin.vue'),
    meta: { requiresAuth: true, requiresAdmin: true }
  }
]

const router = createRouter({
  history: createWebHistory(),
  routes
})

// Global navigation guard
router.beforeEach((to, from, next) => {
  const userStore = useUserStore()
  
  if (to.meta.requiresAuth && !userStore.isAuthenticated) {
    next('/login')
  } else if (to.meta.requiresAdmin && userStore.userRole !== 'admin') {
    next('/unauthorized')
  } else {
    next()
  }
})

export default router

Component-Level Guards

<!-- views/Profile.vue -->
<template>
  <div class="profile">
    <h1>Profile Page</h1>
    <p>This page requires authentication.</p>
  </div>
</template>

<script>
export default {
  name: 'Profile',
  beforeRouteEnter(to, from, next) {
    // Called before the route that renders this component is confirmed
    next(vm => {
      // Access to component instance via `vm`
    })
  },
  beforeRouteUpdate(to, from, next) {
    // Called when the route that renders this component has changed
    next()
  },
  beforeRouteLeave(to, from, next) {
    // Called when the route that renders this component is about to be navigated away from
    if (confirm('Are you sure you want to leave?')) {
      next()
    } else {
      next(false)
    }
  }
}
</script>

404 Error Handling

Create a catch-all route for 404 errors:

// router/index.js
const routes = [
  // ... your other routes
  {
    path: '/:pathMatch(.*)*',
    name: 'NotFound',
    component: () => import('../views/NotFound.vue')
  }
]
<!-- views/NotFound.vue -->
<template>
  <div class="not-found">
    <h1>404 - Page Not Found</h1>
    <p>Sorry, the page you're looking for doesn't exist.</p>
    <router-link to="/" class="home-link">Go Home</router-link>
  </div>
</template>

<script>
export default {
  name: 'NotFound'
}
</script>

<style scoped>
.not-found {
  text-align: center;
  padding: 50px 20px;
}

.home-link {
  display: inline-block;
  margin-top: 20px;
  padding: 10px 20px;
  background-color: #42b883;
  color: white;
  text-decoration: none;
  border-radius: 4px;
}
</style>

Best Practices

  1. Lazy load routes - Use dynamic imports for better performance
  2. Use meaningful route names - Makes programmatic navigation easier
  3. Implement proper error handling - Handle 404s and other errors gracefully
  4. Use route guards for authentication - Protect sensitive routes
  5. Keep routes organized - Group related routes together

Next Steps

Learn how to integrate with external APIs in the API integration tutorial.

External Resources

Last updated on