Route meta fields y typed routes

Intermedio
Vuejs
Vuejs
Actualizado: 27/03/2026

Route meta fields

Los meta fields son propiedades personalizadas que se pueden asociar a cada ruta en la configuración del router. Permiten almacenar información adicional como requisitos de autenticación, títulos de página, configuraciones de layout o roles de acceso, que luego se puede consultar en componentes y navigation guards.

Definición de meta en la configuración de rutas

Los meta fields se definen como un objeto dentro de la propiedad meta de cada ruta:

// router/index.ts
import { createRouter, createWebHistory } from 'vue-router'

const router = createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: '/',
      name: 'home',
      component: () => import('../views/HomeView.vue'),
      meta: {
        title: 'Inicio',
        requiresAuth: false,
        layout: 'default'
      }
    },
    {
      path: '/dashboard',
      name: 'dashboard',
      component: () => import('../views/DashboardView.vue'),
      meta: {
        title: 'Panel de control',
        requiresAuth: true,
        roles: ['admin', 'editor'],
        layout: 'admin'
      }
    },
    {
      path: '/profile',
      name: 'profile',
      component: () => import('../views/ProfileView.vue'),
      meta: {
        title: 'Mi perfil',
        requiresAuth: true,
        breadcrumb: [
          { label: 'Inicio', to: '/' },
          { label: 'Perfil' }
        ]
      }
    },
    {
      path: '/login',
      name: 'login',
      component: () => import('../views/LoginView.vue'),
      meta: {
        title: 'Iniciar sesión',
        requiresAuth: false,
        layout: 'auth'
      }
    }
  ]
})

Casos de uso comunes para meta

| Meta field | Uso | Ejemplo | |---|---|---| | requiresAuth | Proteger rutas que necesitan autenticación | meta: { requiresAuth: true } | | title | Título dinámico de la página | meta: { title: 'Panel' } | | layout | Definir qué layout usar para la vista | meta: { layout: 'admin' } | | breadcrumb | Ruta de navegación para breadcrumbs | meta: { breadcrumb: [...] } | | roles | Roles autorizados para acceder | meta: { roles: ['admin'] } |

Acceder a meta desde un componente

Dentro de cualquier componente se accede a los meta fields de la ruta actual mediante useRoute():

<template>
  <div>
    <h1>{{ route.meta.title }}</h1>
    <p v-if="route.meta.requiresAuth">Esta página requiere autenticación</p>
  </div>
</template>

<script setup lang="ts">
import { useRoute } from 'vue-router'

const route = useRoute()
</script>

Acceder a meta en navigation guards

En un beforeEach guard global, se accede a los meta fields a través del parámetro to:

router.beforeEach((to, from) => {
  if (to.meta.requiresAuth && !isAuthenticated()) {
    return { name: 'login' }
  }
})

En rutas anidadas, los meta fields de todas las rutas coincidentes se combinan. Para comprobar si alguna ruta del árbol requiere autenticación, se usa to.matched:

router.beforeEach((to, from) => {
  const requiresAuth = to.matched.some(record => record.meta.requiresAuth)
  if (requiresAuth && !isAuthenticated()) {
    return { name: 'login', query: { redirect: to.fullPath } }
  }
})

Títulos de página dinámicos con meta

Un patrón muy común es actualizar el título del documento (document.title) automáticamente en cada navegación usando los meta fields:

// router/index.ts
router.afterEach((to) => {
  const defaultTitle = 'Mi aplicación'
  document.title = to.meta.title
    ? `${to.meta.title} | ${defaultTitle}`
    : defaultTitle
})

De esta forma, cada página muestra automáticamente su título en la pestaña del navegador sin necesidad de gestionar document.title en cada componente.

Sistema de layouts con route meta

Los meta fields permiten definir un sistema de layouts donde cada ruta indica que layout debe usarse:

<!-- App.vue -->
<template>
  <component :is="layout">
    <RouterView />
  </component>
</template>

<script setup lang="ts">
import { computed } from 'vue'
import { useRoute } from 'vue-router'
import DefaultLayout from './layouts/DefaultLayout.vue'
import AdminLayout from './layouts/AdminLayout.vue'
import AuthLayout from './layouts/AuthLayout.vue'

const route = useRoute()

const layouts: Record<string, any> = {
  default: DefaultLayout,
  admin: AdminLayout,
  auth: AuthLayout
}

const layout = computed(() => {
  const layoutName = (route.meta.layout as string) || 'default'
  return layouts[layoutName]
})
</script>

Cada layout es un componente que define la estructura visual (barra lateral, header, footer) y renderiza el contenido de la vista mediante <slot>:

<!-- layouts/AdminLayout.vue -->
<template>
  <div class="admin-layout">
    <aside class="sidebar">
      <h3>Administracion</h3>
      <RouterLink to="/dashboard">Panel</RouterLink>
      <RouterLink to="/dashboard/users">Usuarios</RouterLink>
    </aside>
    <main class="content">
      <slot />
    </main>
  </div>
</template>

<script setup lang="ts">
</script>

Tipado de route meta con TypeScript

Por defecto, meta tiene tipo RouteMeta que es un objeto generico Record<string, unknown>. Para obtener autocompletado y seguridad de tipos, se extiende la interfaz RouteMeta mediante module augmentation:

// src/router/types.ts
import 'vue-router'

declare module 'vue-router' {
  interface RouteMeta {
    title?: string
    requiresAuth?: boolean
    roles?: string[]
    layout?: 'default' | 'admin' | 'auth'
    breadcrumb?: Array<{ label: string; to?: string }>
  }
}

Con esta declaración, TypeScript valida automáticamente los meta fields al definir rutas y al acceder a ellos en componentes o guards:

// Ahora TypeScript verifica los tipos de meta
const routes = [
  {
    path: '/dashboard',
    component: () => import('../views/DashboardView.vue'),
    meta: {
      title: 'Dashboard',      // OK: string
      requiresAuth: true,       // OK: boolean
      roles: ['admin'],         // OK: string[]
      layout: 'admin',          // OK: 'default' | 'admin' | 'auth'
      // layout: 'custom',      // Error: no está en el tipo union
    }
  }
]

En los componentes, el autocompletado funciona directamente:

<script setup lang="ts">
import { useRoute } from 'vue-router'

const route = useRoute()
// route.meta.title tiene tipo string | undefined
// route.meta.requiresAuth tiene tipo boolean | undefined
</script>

Typed routes

A partir de Vue Router 4.4+, se introduce el soporte para typed routes que proporcionan seguridad de tipos completa para los nombres de rutas, parámetros y navegación.

Que ofrecen las typed routes

Las typed routes permiten que TypeScript verifique en tiempo de compilacion:

  • Que el nombre de la ruta exista en la configuración.
  • Que los parámetros necesarios se pasen correctamente al navegar.
  • Que los tipos de los parámetros sean los esperados.

Configuracion con unplugin-vue-router

La forma mas automatica de obtener typed routes es mediante unplugin-vue-router, que genera los tipos a partir de la estructura de archivos:

npm install -D unplugin-vue-router

Configuracion en vite.config.ts:

// vite.config.ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import VueRouter from 'unplugin-vue-router/vite'

export default defineConfig({
  plugins: [
    VueRouter({
      routesFolder: 'src/views'
    }),
    vue()
  ]
})

Se actualiza el archivo de tipos para que TypeScript reconozca las rutas generadas:

// env.d.ts
/// <reference types="unplugin-vue-router/client" />

Tipado manual con TypesConfig

Sin unplugin-vue-router, se puede configurar el tipado manualmente extendiendo TypesConfig:

// src/router/types.ts
import 'vue-router'

declare module 'vue-router' {
  interface TypesConfig {
    RouteNamedMap: {
      home: { path: '/' }
      about: { path: '/about' }
      profile: { path: '/profile/:id', params: { id: string } }
      'blog-post': { path: '/blog/:slug', params: { slug: string } }
    }
  }
}

Uso de useRoute y useRouter tipados

Con typed routes, useRoute y useRouter reconocen los nombres y parámetros válidos:

<script setup lang="ts">
import { useRoute, useRouter } from 'vue-router'

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

// Navegación con nombre tipado y parámetros
router.push({ name: 'profile', params: { id: '42' } })

// TypeScript detecta errores:
// router.push({ name: 'no-existe' })       // Error: nombre no valido
// router.push({ name: 'profile' })          // Error: falta param 'id'
</script>

Ejemplo completo: guard con meta y título dinámico

A continuación se muestra un ejemplo que combina route meta, guard de autenticación, título dinámico y tipado:

// src/router/types.ts
import 'vue-router'

declare module 'vue-router' {
  interface RouteMeta {
    title?: string
    requiresAuth?: boolean
    roles?: string[]
  }
}
// src/router/index.ts
import { createRouter, createWebHistory } from 'vue-router'

const router = createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: '/',
      name: 'home',
      component: () => import('../views/HomeView.vue'),
      meta: { title: 'Inicio' }
    },
    {
      path: '/login',
      name: 'login',
      component: () => import('../views/LoginView.vue'),
      meta: { title: 'Iniciar sesión' }
    },
    {
      path: '/dashboard',
      name: 'dashboard',
      component: () => import('../views/DashboardView.vue'),
      meta: { title: 'Panel', requiresAuth: true, roles: ['admin'] }
    },
    {
      path: '/profile',
      name: 'profile',
      component: () => import('../views/ProfileView.vue'),
      meta: { title: 'Perfil', requiresAuth: true }
    }
  ]
})

// Guard de autenticación
router.beforeEach((to) => {
  const isAuthenticated = !!localStorage.getItem('token')
  if (to.meta.requiresAuth && !isAuthenticated) {
    return { name: 'login', query: { redirect: to.fullPath } }
  }
})

// Título dinámico
router.afterEach((to) => {
  document.title = to.meta.title
    ? `${to.meta.title} - Mi App`
    : 'Mi App'
})

export default router
<!-- DashboardView.vue -->
<template>
  <div>
    <h1>{{ route.meta.title }}</h1>
    <p>Bienvenido al panel de administracion</p>
  </div>
</template>

<script setup lang="ts">
import { useRoute } from 'vue-router'

const route = useRoute()
</script>

Este patrón proporciona un sistema robusto de navegación con control de acceso, títulos automáticos y seguridad de tipos en toda la aplicación.

Alan Sastre - Autor del tutorial

Alan Sastre

Ingeniero de Software y formador, CEO en CertiDevs

Ingeniero de software especializado en Full Stack y en Inteligencia Artificial. Como CEO de CertiDevs, Vuejs es una de sus áreas de expertise. Con más de 15 años programando, 6K seguidores en LinkedIn y experiencia como formador, Alan se dedica a crear contenido educativo de calidad para desarrolladores de todos los niveles.

Más tutoriales de Vuejs

Explora más contenido relacionado con Vuejs y continúa aprendiendo con nuestros tutoriales gratuitos.

Aprendizajes de esta lección

Cursos que incluyen esta lección

Esta lección forma parte de los siguientes cursos estructurados con rutas de aprendizaje