Route Helpers

ubean’s routing helpers revolve around useRouter() (from vue-router; auto-imported only when autoImports: { vueRouter: true } is enabled — off by default) and the globally-registered <Link> component. ubean does not provide useRoute(), navigateTo(), redirectTo(), useRouteParams(), or useRouteQuery() — use router.currentRoute (see below) or vue-router’s useRoute() instead. For i18n path helpers (useLocalePath, useSwitchLocalePath), see the I18n reference.

useRouter()

useRouter() returns the Vue Router instance extended with push/replace shortcuts. Import it from vue-router (auto-import requires autoImports.vueRouter, off by default) in client components:

<script setup lang="ts">
const router = useRouter();
</script>

Methods

MethodDescription
push(to)Navigate to a new route
replace(to)Replace current route (no history)
back()Go back one step
forward()Go forward one step
go(n)Navigate n steps (negative = back)
beforeEachRegister global beforeEach guard
afterEachRegister global afterEach hook

Reading Current Route

Access router.currentRoute.value to inspect the current route:

const router = useRouter();
const route = router.currentRoute.value;

console.log(route.path);      // "/users/123"
console.log(route.params.id); // "123"
console.log(route.query.q);   // "search term"
console.log(route.hash);      // "#section"
console.log(route.fullPath);  // "/users/123?q=...#section"
console.log(route.name);      // "UserDetail"
console.log(route.meta);      // Page metadata

For reactive access in templates, use router.currentRoute directly or unwrap with a computed:

<script setup lang="ts">
import { computed } from 'vue';

const router = useRouter();
const currentPath = computed(() => router.currentRoute.value.path);
const userId = computed(() => router.currentRoute.value.params.id as string);
</script>

<template>
  <p>Path: {{ currentPath }}</p>
  <p>User: {{ userId }}</p>
</template>

Programmatic Navigation

const router = useRouter();

// String path
router.push('/about');

// Object form with name + params
router.push({ name: 'UserDetail', params: { id: '123' } });

// With query
router.push({ path: '/search', query: { q: 'ubean' } });

// With hash
router.push({ path: '/docs', hash: '#section-1' });

// Replace (no history entry)
router.replace('/login');

// Go back / forward
router.back();
router.forward();
router.go(-2);

Navigation Guards

There are two ways to register navigation guards:

1. Global guards via defineApp({ router }) — recommended

Register once at app startup in src/app.ts. Guards run on both client and SSR, and can intercept the first navigation. See Navigation Guards guide for details.

// src/app.ts
import { defineApp } from 'ubean';

export default defineApp({
  router: {
    setup(router) {
      router.beforeEach((to, from) => {
        if (to.meta.requiresAuth && !isAuthenticated()) {
          return '/login';
        }
      });
      router.afterEach((to, from) => {
        // Analytics, scroll-to-top, etc.
      });
    }
  }
});

2. Per-component guards via useRouter()

For component-scoped guards (rare; mostly useful inside long-lived root components). Each call appends a new guard — make sure you’re not re-registering on every mount:

const router = useRouter();

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

router.afterEach((to, from) => {
  // Analytics, scroll-to-top, etc.
});

Production auth/analytics guards should live in defineApp({ router }) to avoid duplicate registration and to ensure they fire during SSR.

<Link> Component (Global)

<Link> is globally registered — no import needed. It performs client-side navigation and supports active-class styling.

<template>
  <!-- String path -->
  <Link to="/about">About</Link>

  <!-- Named route with params -->
  <Link :to="{ name: 'UserDetail', params: { id: '123' } }">User</Link>

  <!-- With query -->
  <Link :to="{ path: '/search', query: { q: 'ubean' } }">Search</Link>
</template>

Props

PropTypeDescription
tostring | { name, params, query, hash }Target route
localestringLocalize target path to a locale (i18n)
replacebooleanrouter.replace instead of push
hrefstringOverride rendered href
prefetchbooleanPrefetch target page chunk
activeClassstringClass when link matches current
exactActiveClassstringClass for exact match
noActiveClassbooleanDisable default active classes

External URLs (starting with http://, https://, //) are automatically detected and rendered as plain <a> tags with target="_blank" rel="noopener noreferrer".

Slot Scope

The default slot exposes isActive and isExactActive:

<template>
  <Link to="/about" v-slot="{ isActive }">
    <span :class="{ active: isActive }">About</span>
  </Link>
</template>

Page Metadata (definePage)

Use the definePage macro inside <script setup> to set page metadata. It is a compile-time macro, auto-imported:

<script setup lang="ts">
definePage({
  name: 'About',
  path: '/about',                    // Override auto-generated path
  layout: 'default',
  meta: {
    title: 'About Page',
    description: 'About our company'
  },
  requiresAuth: true,
  cache: true,                       // Enable KeepAlive page caching
  head: {
    title: 'About',
    meta: [{ name: 'description', content: 'About us' }]
  }
});
</script>

Fields

FieldTypeDescription
namestringRoute name (PascalCase recommended)
pathstringOverride auto-generated URL path
layoutstring | string[] | falseLayout name, outer→inner layout chain, or false to disable
reusestringReuse route target
metaobjectCustom metadata (any shape)
requiresAuthbooleanAuth requirement (meta shortcut)
cachebooleanEnable KeepAlive page caching
headobjectPer-page head tags (@unhead/vue)
ssrboolean | 'streaming' | 'data-only'Per-page rendering override
transitionstringPage transition name (empty string disables)

There is no top-level title field — use meta: { title } or head: { title }. There is no top-level middleware field — route-level middleware is declared via meta: { middleware: [...] }.

Page Caching (cache: true)

Set cache: true to preserve the page component instance with Vue’s <KeepAlive> when navigating away. The page’s route name (e.g. 'About') is used as the cache key — the framework automatically wraps the page component with a named wrapper (getNamedPageWrapper), so <script setup> SFCs work without manually calling defineOptions({ name }).

When cached, onActivated / onDeactivated lifecycle hooks fire on the page component (instead of onMounted / onUnmounted):

<script setup lang="ts">
import { onActivated, onDeactivated } from 'vue';

definePage({ cache: true });

onActivated(() => {
  console.log('Page re-activated (navigated back)');
});

onDeactivated(() => {
  console.log('Page deactivated (navigated away, but kept alive)');
});
</script>

Runtime control is available via useCacheViews() / enablePageCache(name) / disablePageCache(name) / excludePageCache(name) / invalidatePageCache(name) (auto-imported from ubean/client).

Reuse Route Cache Inheritance

When a .reuse.ts page does not explicitly declare cache, it inherits the target page’s cache setting. You can also explicitly enable or disable cache on a reuse route independent of its target:

// pages/about.vue — cache enabled
definePage({ cache: true });

// pages/about2.reuse.ts — inherits cache: true from About (no need to repeat)
definePage({ reuse: 'About' });

// pages/about3.reuse.ts — explicitly disable cache (overrides inheritance)
definePage({ reuse: 'About', cache: false });

// pages/about4.reuse.ts — explicitly enable cache even if target is not cached
definePage({ reuse: 'About', cache: true });

Each cached reuse route is an independent KeepAlive instance keyed by its own route name.

Reading Route Params in API Routes

In API route handlers (server-side), use Hono’s c.req.param():

// src/routes/api/users/[id].ts
import { defineHandler } from 'ubean/server';

export const GET = defineHandler(c => {
  const id = c.req.param('id');
  return c.json({ id });
});

For typed params, use validator('param', schema):

import { defineHandler, validator } from 'ubean/server';
import { z } from 'zod';

export const GET = defineHandler(
  validator('param', z.object({ id: z.string() })),
  c => {
    const { id } = c.req.valid('param');
    return c.json({ id });
  }
);