Back to catalog

navigation-routing-setup

Setup navigation and routing with React Router, Vue Router, TanStack Router. Protected routes, layouts, nested routes, breadcrumbs. Keywords - routing, navigation, react router, vue router, protected routes, nested routes, route guards, breadcrumbs

  • Category: discovered
  • Tags: github, community
  • Version: 1.0.0
  • Author: walid
  • Downloads: 0
  • Updated: 2025-11-22

Download the latest version (v1.0.0) from this page.

SKILL.md

---
name: navigation-routing-setup
description: Setup navigation and routing with React Router, Vue Router, TanStack Router. Protected routes, layouts, nested routes, breadcrumbs. Keywords - routing, navigation, react router, vue router, protected routes, nested routes, route guards, breadcrumbs
---

# Navigation/Routing Setup

## When to Use This Skill

Activate when:
- Setting up routing for single-page applications
- Need protected routes (authentication required)
- Building nested routes with layouts
- Implementing route guards and navigation guards
- Creating breadcrumbs and active link highlighting

## What This Skill Does

Sets up routing with:
- Client-side routing (SPA navigation)
- Protected routes (redirect if not authenticated)
- Nested routes and layouts
- Route guards (before navigation)
- Active link styling
- Breadcrumbs
- Route-based code splitting
- 404 Not Found pages

## Supported Technologies

**React**:
- React Router v6 (most popular)
- TanStack Router (type-safe)
- Next.js App Router (file-based)

**Vue**:
- Vue Router v4 (official)
- Nuxt routing (file-based)

## Example: React Router v6 Setup

```tsx
// App.tsx
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
import { useAuth } from './hooks/useAuth';

// Layouts
import { MainLayout } from './layouts/MainLayout';
import { AuthLayout } from './layouts/AuthLayout';
import { DashboardLayout } from './layouts/DashboardLayout';

// Pages
import HomePage from './pages/HomePage';
import LoginPage from './pages/LoginPage';
import RegisterPage from './pages/RegisterPage';
import DashboardPage from './pages/DashboardPage';
import ProfilePage from './pages/ProfilePage';
import SettingsPage from './pages/SettingsPage';
import NotFoundPage from './pages/NotFoundPage';

// Protected Route wrapper
function ProtectedRoute({ children }: { children: React.ReactNode }) {
  const { isAuthenticated, isLoading } = useAuth();

  if (isLoading) {
    return <div>Loading...</div>;
  }

  if (!isAuthenticated) {
    return <Navigate to="/login" replace />;
  }

  return <>{children}</>;
}

function App() {
  return (
    <BrowserRouter>
      <Routes>
        {/* Public routes with MainLayout */}
        <Route element={<MainLayout />}>
          <Route path="/" element={<HomePage />} />
        </Route>

        {/* Auth routes with AuthLayout */}
        <Route element={<AuthLayout />}>
          <Route path="/login" element={<LoginPage />} />
          <Route path="/register" element={<RegisterPage />} />
        </Route>

        {/* Protected routes with DashboardLayout */}
        <Route
          element={
            <ProtectedRoute>
              <DashboardLayout />
            </ProtectedRoute>
          }
        >
          <Route path="/dashboard" element={<DashboardPage />} />
          <Route path="/profile" element={<ProfilePage />} />
          <Route path="/settings" element={<SettingsPage />} />
        </Route>

        {/* 404 */}
        <Route path="*" element={<NotFoundPage />} />
      </Routes>
    </BrowserRouter>
  );
}

export default App;
```

## Example: Layout with Outlet (React Router)

```tsx
// layouts/DashboardLayout.tsx
import { Outlet, Link, useLocation } from 'react-router-dom';
import { HomeIcon, UserIcon, Cog6ToothIcon } from '@heroicons/react/24/outline';

const navigation = [
  { name: 'Dashboard', href: '/dashboard', icon: HomeIcon },
  { name: 'Profile', href: '/profile', icon: UserIcon },
  { name: 'Settings', href: '/settings', icon: Cog6ToothIcon },
];

export function DashboardLayout() {
  const location = useLocation();

  return (
    <div className="flex h-screen">
      {/* Sidebar */}
      <aside className="w-64 bg-gray-900 text-white">
        <div className="p-4">
          <h1 className="text-xl font-bold">My App</h1>
        </div>

        <nav className="mt-8 space-y-1 px-2">
          {navigation.map((item) => {
            const isActive = location.pathname === item.href;
            return (
              <Link
                key={item.name}
                to={item.href}
                className={`flex items-center gap-3 rounded-md px-3 py-2 text-sm font-medium ${
                  isActive
                    ? 'bg-gray-800 text-white'
                    : 'text-gray-300 hover:bg-gray-800 hover:text-white'
                }`}
              >
                <item.icon className="h-5 w-5" />
                {item.name}
              </Link>
            );
          })}
        </nav>
      </aside>

      {/* Main content */}
      <main className="flex-1 overflow-auto">
        <div className="p-8">
          <Outlet />
        </div>
      </main>
    </div>
  );
}
```

## Example: Vue Router v4 Setup

```typescript
// router/index.ts
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router';
import { useAuthStore } from '@/stores/auth';

// Layouts
import MainLayout from '@/layouts/MainLayout.vue';
import AuthLayout from '@/layouts/AuthLayout.vue';
import DashboardLayout from '@/layouts/DashboardLayout.vue';

// Pages
import HomePage from '@/pages/HomePage.vue';
import LoginPage from '@/pages/LoginPage.vue';
import DashboardPage from '@/pages/DashboardPage.vue';
import ProfilePage from '@/pages/ProfilePage.vue';
import NotFoundPage from '@/pages/NotFoundPage.vue';

const routes: RouteRecordRaw[] = [
  {
    path: '/',
    component: MainLayout,
    children: [
      {
        path: '',
        name: 'home',
        component: HomePage,
        meta: { title: 'Home' },
      },
    ],
  },
  {
    path: '/auth',
    component: AuthLayout,
    children: [
      {
        path: 'login',
        name: 'login',
        component: LoginPage,
        meta: { title: 'Login', requiresGuest: true },
      },
      {
        path: 'register',
        name: 'register',
        component: () => import('@/pages/RegisterPage.vue'), // Lazy loading
        meta: { title: 'Register', requiresGuest: true },
      },
    ],
  },
  {
    path: '/dashboard',
    component: DashboardLayout,
    meta: { requiresAuth: true },
    children: [
      {
        path: '',
        name: 'dashboard',
        component: DashboardPage,
        meta: { title: 'Dashboard' },
      },
      {
        path: 'profile',
        name: 'profile',
        component: ProfilePage,
        meta: { title: 'Profile' },
      },
      {
        path: 'settings',
        name: 'settings',
        component: () => import('@/pages/SettingsPage.vue'),
        meta: { title: 'Settings' },
      },
    ],
  },
  {
    path: '/:pathMatch(.*)*',
    name: 'not-found',
    component: NotFoundPage,
    meta: { title: '404 Not Found' },
  },
];

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes,
});

// Navigation guard
router.beforeEach((to, from, next) => {
  const authStore = useAuthStore();

  // Set page title
  document.title = `${to.meta.title || 'App'} | My App`;

  // Check authentication
  if (to.meta.requiresAuth && !authStore.isAuthenticated) {
    next({ name: 'login', query: { redirect: to.fullPath } });
  } else if (to.meta.requiresGuest && authStore.isAuthenticated) {
    next({ name: 'dashboard' });
  } else {
    next();
  }
});

export default router;
```

## Example: Breadcrumbs Component (React)

```tsx
// components/Breadcrumbs.tsx
import { Link, useMatches } from 'react-router-dom';
import { ChevronRightIcon } from '@heroicons/react/20/solid';

interface BreadcrumbMatch {
  pathname: string;
  handle?: {
    crumb?: (data: any) => { label: string; to: string };
  };
  data?: any;
}

export function Breadcrumbs() {
  const matches = useMatches() as BreadcrumbMatch[];

  const breadcrumbs = matches
    .filter((match) => match.handle?.crumb)
    .map((match) => match.handle!.crumb!(match.data));

  if (breadcrumbs.length === 0) return null;

  return (
    <nav className="flex" aria-label="Breadcrumb">
      <ol className="flex items-center space-x-2">
        <li>
          <Link to="/" className="text-gray-500 hover:text-gray-700">
            Home
          </Link>
        </li>

        {breadcrumbs.map((crumb, index) => (
          <li key={crumb.to} className="flex items-center">
            <ChevronRightIcon className="h-5 w-5 text-gray-400" />
            {index === breadcrumbs.length - 1 ? (
              <span className="ml-2 text-gray-900">{crumb.label}</span>
            ) : (
              <Link
                to={crumb.to}
                className="ml-2 text-gray-500 hover:text-gray-700"
              >
                {crumb.label}
              </Link>
            )}
          </li>
        ))}
      </ol>
    </nav>
  );
}

// Usage in route config
{
  path: '/users/:id',
  element: <UserDetailPage />,
  loader: async ({ params }) => {
    return { user: await fetchUser(params.id) };
  },
  handle: {
    crumb: (data) => ({
      label: data.user.name,
      to: `/users/${data.user.id}`,
    }),
  },
}
```

## Example: Route-based Code Splitting

```tsx
// React Router with lazy loading
import { lazy, Suspense } from 'react';

const DashboardPage = lazy(() => import('./pages/DashboardPage'));
const ProfilePage = lazy(() => import('./pages/ProfilePage'));
const SettingsPage = lazy(() => import('./pages/SettingsPage'));

function App() {
  return (
    <BrowserRouter>
      <Suspense fallback={<div>Loading...</div>}>
        <Routes>
          <Route path="/dashboard" element={<DashboardPage />} />
          <Route path="/profile" element={<ProfilePage />} />
          <Route path="/settings" element={<SettingsPage />} />
        </Routes>
      </Suspense>
    </BrowserRouter>
  );
}
```

## Best Practices

1. **Protected routes** - Use wrapper components or route guards
2. **Layouts** - Use nested routes with `<Outlet>` (React) or child routes (Vue)
3. **Active links** - Highlight current route in navigation
4. **Lazy loading** - Code split routes for better performance
5. **Route guards** - Check authentication before navigation
6. **Redirect** - Redirect unauthorized users to login
7. **404 page** - Catch-all route for not found
8. **Breadcrumbs** - Show current location in hierarchy
9. **Meta tags** - Set page title per route

## Common Routing Patterns

**1. Protected Route**:
```tsx
<Route path="/dashboard" element={<ProtectedRoute><Dashboard /></ProtectedRoute>} />
```

**2. Redirect if authenticated**:
```tsx
if (isAuthenticated) return <Navigate to="/dashboard" />;
```

**3. Nested routes**:
```tsx
<Route path="/dashboard" element={<DashboardLayout />}>
  <Route path="overview" element={<Overview />} />
  <Route path="stats" element={<Stats />} />
</Route>
```

**4. Dynamic routes**:
```tsx
<Route path="/users/:id" element={<UserDetail />} />
```

**5. Query parameters**:
```tsx
const [searchParams] = useSearchParams();
const page = searchParams.get('page') || '1';
```

## Integration with Other Skills

- **auth-flow-builder** - Protect routes with authentication
- **state-management-setup** - Store authentication state
- **loading-states-handler** - Loading states during navigation

---

**Version**: 1.0.0
**Category**: Frontend Extended
**Complexity**: Medium