logo
  • Guide
  • API
  • Blog
  • English
    • 简体中文
    • English
    • @esmx/core
      Esmx
      App
      RenderContext
      ModuleConfig
      PackConfig
      ManifestJson
      @esmx/router
      Router
      Route
      Route Configuration
      RouterLink
      Navigation Guards
      Dynamic Route Matching
      Nested Routes
      Programmatic Navigation
      Scroll Behavior
      Layer Routing
      MicroApp
      Error Types
      Types Reference
      @esmx/router-vue
      RouterPlugin
      Composables
      Components
      Type Augmentation
      @esmx/router-react
      Micro-App Integration
      Hooks & Context
      Components
      SSR
      App
      @esmx/rspack
      @esmx/rspack-vue
      @esmx/rspack-react

      Last Updated: 4/7/2026, 2:16:07 AM

      Previous pageNested RoutesNext pageScroll Behavior

      #Programmatic Navigation

      Aside from using <RouterLink> to create anchor tags for declarative navigation, we can do this programmatically using the router's instance methods.

      #router.push

      To navigate to a different URL, use router.push. This method adds a new entry to the history stack, so when the user clicks the browser back button, they'll go back to the previous URL.

      // String path
      await router.push('/users/42');
      
      // Object with path
      await router.push({ path: '/users/42' });
      
      // With query params
      await router.push({ path: '/search', query: { q: 'vue', page: '1' } });
      
      // With hash
      await router.push({ path: '/docs/intro', hash: '#getting-started' });

      The method returns a Promise<Route> that resolves to the new route after navigation completes (including all guards):

      const route = await router.push('/about');
      console.log(route.path); // '/about'

      #router.replace

      Acts like router.push but does not add a new history entry. It replaces the current entry instead:

      // The current history entry is replaced — back button won't return here
      await router.replace('/new-location');

      This is useful when you want to redirect without cluttering the browser history — for example, after form submission or login:

      async function handleLogin() {
        await performLogin();
        await router.replace('/dashboard');
      }

      #Navigation with Objects

      Both push and replace accept a RouteLocationInput, which can be a string or an object with these properties:

      • path: string — The target path
      • query: Record<string, string> — Query parameters
      • hash: string — Hash fragment (e.g., '#section')
      • state: Record<string, unknown> — State stored in history.state (not visible in URL)
      • params: Record<string, string> — Dynamic segment values
      • keepScrollPosition: boolean — If true, don't scroll to top after navigation
      • statusCode: number — HTTP status code (useful for SSR)
      await router.push({
        path: '/users/42',
        query: { tab: 'posts' },
        hash: '#latest',
        state: { fromDashboard: true },
        keepScrollPosition: true
      });

      #Using params

      The params option lets you pass dynamic segment values that are applied to the matched route's path pattern:

      // Route: /users/:userId/posts/:postId
      await router.push({
        path: '/users/:userId/posts/:postId',
        params: { userId: '42', postId: '7' }
      });
      // Navigates to /users/42/posts/7

      #Using state

      The state property stores data in history.state. Unlike query params, state is not visible in the URL and is preserved across forward/back navigation:

      await router.push({
        path: '/checkout',
        state: { cartId: 'abc-123', step: 2 }
      });
      
      console.log(router.route.state.cartId); // 'abc-123'

      #Window Navigation

      Standard push/replace perform SPA navigation — the page doesn't reload, only the routed content changes. Window navigation methods trigger a full browser navigation instead.

      #router.pushWindow

      Opens the target in a new browser tab/window (equivalent to window.open):

      await router.pushWindow('/external-report');

      #router.replaceWindow

      Navigates the current tab to a new URL (equivalent to window.location.replace):

      await router.replaceWindow('/legacy-page');

      #When to Use Window Navigation

      • Navigate within your SPA: use push / replace
      • Navigate to a different micro-frontend: use pushWindow / replaceWindow
      • Open in new tab: use pushWindow
      • Full page reload / redirect to external URL: use replaceWindow
      • Navigate to a page outside router scope: use pushWindow / replaceWindow

      #Guard Pipeline Differences

      Window navigation methods skip most of the guard pipeline since the browser will perform a full navigation anyway:

      Stagepush/replacepushWindow/replaceWindow
      fallback✅✅
      override✅✅
      beforeLeave✅replaceWindow only
      beforeEach✅✅
      beforeUpdate✅❌
      beforeEnter✅❌
      asyncComponent✅❌
      confirm✅✅

      #History Navigation

      These methods mirror the browser's native history navigation:

      #router.back()

      Go back one step in history. Equivalent to router.go(-1):

      await router.back();

      Returns Promise<Route | null>. Returns null if there's no history to go back to (the user is at the start of their session).

      #router.forward()

      Go forward one step. Equivalent to router.go(1):

      await router.forward();

      Returns Promise<Route | null>. Returns null if there's no forward history.

      #router.go(n)

      Move n steps in history. Positive values go forward, negative values go back:

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

      Returns Promise<Route | null>. Returns null if the target position doesn't exist in history. Note that router.go(0) returns null immediately without any action (unlike location.reload()).

      #router.restartApp

      Remounts the current micro-app without changing the URL. This is useful when you need to reset the application state completely:

      await router.restartApp();

      You can optionally pass a new route location:

      await router.restartApp('/dashboard');

      This method runs the full guard pipeline (excluding override), unmounts the current micro-app, and remounts it fresh.

      #router.resolve

      Resolves a route location without actually navigating. This is useful for generating URLs, checking if a route exists, or inspecting what a navigation would produce:

      const route = router.resolve('/users/42?tab=posts');
      
      console.log(route.path);           // '/users/42'
      console.log(route.params);         // { id: '42' }
      console.log(route.query);          // { tab: 'posts' }
      console.log(route.matched.length); // number of matched route configs
      console.log(route.url.href);       // full URL string

      Use it to generate link URLs without triggering navigation:

      const resolved = router.resolve('/some/path');
      if (resolved.matched.length > 0) {
        console.log('Route exists!');
      }
      
      const href = router.resolve({ path: '/about', hash: '#team' }).url.href;

      #The keepScrollPosition Option

      By default, push and replace scroll the page to the top. Pass keepScrollPosition: true to prevent this:

      await router.push({
        path: '/dashboard',
        query: { tab: 'analytics' },
        keepScrollPosition: true
      });

      See Scroll Behavior for full details on how scrolling works.

      #Error Handling

      All navigation methods can throw errors. Always handle them appropriately:

      import {
        RouteTaskCancelledError,
        RouteNavigationAbortedError
      } from '@esmx/router';
      
      try {
        await router.push('/protected');
      } catch (error) {
        if (error instanceof RouteNavigationAbortedError) {
          console.log('Navigation was blocked by a guard');
        } else if (error instanceof RouteTaskCancelledError) {
          console.log('Navigation was superseded by a newer one');
        } else {
          throw error;
        }
      }

      See Error Handling for more details.

      #Summary

      MethodHistoryPage ReloadReturns
      push(to)Adds entryNoPromise<Route>
      replace(to)Replaces currentNoPromise<Route>
      pushWindow(to)Browser handlesYes (new tab)Promise<Route>
      replaceWindow(to)Browser handlesYes (same tab)Promise<Route>
      back()Goes back 1NoPromise<Route | null>
      forward()Goes forward 1NoPromise<Route | null>
      go(n)Goes ±nNoPromise<Route | null>
      restartApp()Replaces currentNo (remounts app)Promise<Route>
      resolve(to)——Route (sync)