Navigation & Routing
The core concepts of vue-micro-router: how paths stack, how to pass props, guard navigation, customize transitions, track history, and type your routes.
Segment-Based Paths
Pages stack as path segments. "home/menu/settings" renders 3 pages simultaneously — each page slides in on top of the previous one.
const { push } = useMicroRouter();
// Push a new page on top
await push('menu'); // home → home/menu
// Push with absolute path
await push('/home/settings'); // → home/settings
// Go back one step
await push(-1); // home/settings → home
// Go back with props (forces remount)
await push(-1, { reset: true });Navigation with Props
Pass data to pages via push() and read it with useMicroState():
// Parent — push with props
await push('profile', { userId: 42 });<!-- ProfilePage.vue -->
<script setup>
import { useMicroState } from 'vue-micro-router';
// Read props passed via push() — auto-syncs mutations back to store
const { userId } = useMicroState<{ userId: number }>();
// With defaults for optional props
const { tab } = useMicroState({ tab: 'overview' });
</script>Route Guards
Prevent or control navigation with sync/async guards:
// Global guards — run on every navigation
const config = {
guards: {
beforeEach: [
async (to, from) => {
if (to.includes('admin') && !isAuthenticated()) return false;
return true;
}
],
afterEach: [(to, from) => analytics.track('navigate', { to, from })],
}
};
// Per-route guards — on specific routes
defineFeaturePlugin({
routes: [
{
path: 'admin',
component: AdminPage,
beforeEnter: async (to, from) => {
const user = await fetchUser();
return user.isAdmin;
},
},
{
path: 'editor',
component: EditorPage,
beforeLeave: (to, from) => {
if (hasUnsavedChanges()) return confirm('Discard changes?');
return true;
},
},
],
});Guard execution order: global beforeEach → target beforeEnter → source beforeLeave → global afterEach
WARNING
Guards have a 5s timeout — if a guard doesn't resolve, navigation is cancelled.
Per-Route Transitions
Each route can have its own transition style and duration:
defineFeaturePlugin({
routes: [
{ path: 'home', component: HomePage }, // default: slide 500ms
{ path: 'settings', component: SettingsPage, transition: 'fade', transitionDuration: 300 },
{ path: 'modal-page', component: ModalPage, transition: 'none' }, // instant, no animation
],
});Supported: 'slide' (default), 'fade', 'none'
Navigation History
Opt-in browser-like history with back/forward:
const config = {
history: { enabled: true, maxEntries: 50 },
};
// In any component:
const router = useMicroRouter();
router.canGoBack?.value; // reactive boolean
router.canGoForward?.value; // reactive boolean
await router.historyBack?.();
await router.historyForward?.();
await router.historyGo?.(-2); // go back 2 entriesHistory truncates forward entries on new push (browser behavior).
Type-Safe Routes
Register Pattern (Recommended)
Use Register module augmentation to declare your plugin type once. Then useMicroRouter() auto-infers everywhere — no generics needed.
// app-plugin.ts — declare once with `as const`
export const appPlugin = defineFeaturePlugin({
name: 'my-app',
routes: [
{ path: 'home', component: HomePage },
{ path: 'profile', component: ProfilePage },
],
dialogs: [{ path: 'confirm', component: ConfirmDialog, activated: false }],
controls: [{ name: 'main_hud', component: MainHUD, activated: false }],
} as const);
// Declare module once — this is the ONLY place you write the type
declare module 'vue-micro-router' {
interface Register {
plugin: typeof appPlugin;
}
}Then everywhere:
// Any component — fully typed, zero generics
const { push, openDialog, toggleControl } = useMicroRouter();
push('profile'); // ✅ OK
push('typo'); // ❌ TS Error
openDialog('confirm'); // ✅ OK
toggleControl('main_hud', true); // ✅ OKBenefits: Type-safe push/openDialog/closeDialog/toggleControl. No duplication. One declaration fixes all usages.
Manual Route Map (Alternative)
For simple cases or when you only need typed props (not route names):
interface AppRoutes {
home: undefined;
profile: { userId: number };
}
const router = useMicroRouter<AppRoutes>();
router.push('profile', { userId: 42 }); // ✅ OK
router.push('profile'); // ❌ TS Error: missing props
router.push('unknown'); // ❌ TS Error: unknown routeUntyped: useMicroRouter() without Register returns MicroRouterStore with untyped methods.
Typed Props (Per-Route/Dialog/Control Attrs)
Opt-in per-component — export interface Attrs to get typed push() / openDialog() / toggleControl() props.
Step 1: In your component, export interface Attrs:
<!-- ProfilePage.vue -->
<script setup lang="ts">
import { useMicroState } from 'vue-micro-router';
export interface Attrs {
userId: number;
username: string;
meta?: { title: string }; // optional fields OK
}
// Required fields need defaults (prevents undefined on remount)
const { userId, username, meta } = useMicroState<Attrs>({
userId: 0,
username: 'Guest',
});
// userId.value → number (never undefined)
// meta.value?.title → string | undefined (ref exists, value may be undefined)
</script><!-- ConfirmDialog.vue -->
<script setup lang="ts">
export interface Attrs {
title?: string;
message?: string;
onConfirm?: () => void;
}
const { title, message, onConfirm } = useMicroState<Attrs>({
title: 'Confirm',
message: 'Are you sure?',
});
</script>Step 2: Run code generation:
npx vue-micro-router-gen # auto-detects src/ or app/, outputs src/vue-micro-router.d.ts
npx vue-micro-router-gen -d src # explicit scan directory
npx vue-micro-router-gen -o types/router.d.ts # custom output pathThe script:
- Scans all
.tsfiles fordefineFeaturePlugin()calls (any folder structure) - Resolves
@/,~/,#/path aliases automatically - Finds
.vuecomponents withexport interface Attrs - Generates
vue-micro-router.d.tswith Register + AttrsMap augmentations
Step 3: Include in tsconfig and enjoy typed everywhere:
// tsconfig.json — add the generated file
{ "include": ["src/**/*.ts", "src/**/*.vue", "src/vue-micro-router.d.ts"] }push('profile', { userId: 42, username: 'Danh' }); // ✅ typed, autocomplete
push('profile'); // ❌ TS error: missing required props
push('profile', { meta: { title: 'Hi' } }); // ✅ optional fields can be skipped
push('home'); // ✅ OK (no Attrs → untyped)
openDialog('confirm', { title: 'Sure?' }); // ✅ typedRules:
- Required fields in
Attrs→ must pass inpush()/openDialog() - Optional fields (
?) → can skip entirely, including the whole props arg - Routes without
export interface Attrs→push()accepts anyRecord<string, unknown> - Re-run
npx vue-micro-router-genafter adding/changingAttrsinterfaces