Skip to content

🪆 Nested Routes ​

Route outposts inheritance from parent routes with priority sorting.

📋 Overview ​

This example shows three route outposts with different priorities:

  • auth (priority 10) — redirects unauthenticated users
  • verified (priority 20) — requires verified email
  • premium (priority 30) — requires premium subscription

Outposts are inherited from parent routes and deduplicated.

💻 Code ​

typescript
import { createRouter, createWebHistory } from 'vue-router';
import { createNavigationCitadel, NavigationOutpostScopes } from 'vue-router-citadel';

// Auth outpost - redirects to home if user is not authenticated
// Priority 10 - runs first (lower = earlier)
const authNavigationOutpost = {
  scope: NavigationOutpostScopes.ROUTE,
  name: 'auth',
  priority: 10,
  handler: ({ verdicts }) => {
    const token = localStorage.getItem('token');

    if (!token) {
      return { name: 'home' };
    }

    return verdicts.ALLOW;
  },
};

// Verified outpost - requires user email to be verified
// Priority 20 - runs after auth
const verifiedNavigationOutpost = {
  scope: NavigationOutpostScopes.ROUTE,
  name: 'verified',
  priority: 20,
  handler: ({ verdicts }) => {
    const user = JSON.parse(localStorage.getItem('user') || '{}');

    if (!user.emailVerified) {
      return { name: 'account' }; // redirect to account page to verify email
    }

    return verdicts.ALLOW;
  },
};

// Premium outpost - requires user to have premium subscription
// Priority 30 - runs after verified
const premiumNavigationOutpost = {
  scope: NavigationOutpostScopes.ROUTE,
  name: 'premium',
  priority: 30,
  handler: ({ verdicts }) => {
    const user = JSON.parse(localStorage.getItem('user') || '{}');

    if (!user.isPremium) {
      return { name: 'account-settings' };
    }

    return verdicts.ALLOW;
  },
};

const outposts = [authNavigationOutpost, verifiedNavigationOutpost, premiumNavigationOutpost];

const routes = [
  { path: '/', name: 'home', component: () => import('./pages/Home.vue') },
  {
    path: '/account',
    component: () => import('./layouts/AccountLayout.vue'),
    meta: {
      outposts: [authNavigationOutpost.name], // all child routes inherit auth
    },
    children: [
      {
        path: '',
        name: 'account',
        component: () => import('./pages/Account.vue'),
        // inherits auth outpost from parent
      },
      {
        path: 'settings',
        name: 'account-settings',
        component: () => import('./pages/AccountSettings.vue'),
        meta: {
          outposts: [verifiedNavigationOutpost.name], // inherits auth, adds verified
        },
      },
      {
        path: 'billing',
        name: 'account-billing',
        component: () => import('./pages/AccountBilling.vue'),
        meta: {
          outposts: [verifiedNavigationOutpost.name, premiumNavigationOutpost.name],
          // inherits auth, adds verified and premium
        },
      },
    ],
  },
];

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

const citadel = createNavigationCitadel(router, {
  outposts,
});

export { router, citadel };

🔄 Route Outpost Resolution ​

RouteOutposts (inherited + own)
/accountauth
/account/settingsauth, verified
/account/billingauth, verified, premium

All outposts run in priority order regardless of where they were declared.

Released under the MIT License.