Skip to content
Meeko
Type to search the guides and the API reference. Open it from any page with / or Ctrl K; move with the arrow keys and open a result with Enter.
Get started

Contents

On this page

useRoute

Reads the committed route rendered by the component (url, path, id, params and search) as signals, and checks whether a route is active.

composable Since 1.2.0 No import @meekojs/meeko/framework Source code

Se autoimporta en las aplicaciones del framework.

ts
import { useRoute } from "@meekojs/meeko/framework";

useRoute() gives the component the route it is rendering: the request's route in SSR and the last committed navigation's route in the browser. Each value is a read-only signal, so a template, computed or effect reading it updates on navigation, including in a layout retained between pages. isActive(to, options) applies the same rule as <MeekoLink match> to custom links and components.

Works in pages, layouts, app/app.meeko, app/error.meeko and any components they render. It is auto-imported in applications (your own export named useRoute in an auto-import folder takes priority) and can also be called from code running with the component (a composable, computed, effect). In an event handler there is no active root and it throws: request it in the script and use the result in the handler.

Usage#

meeko
<!-- app/layouts/default.meeko -->
<script lang="ts">
const route = useRoute();
</script>

<template>
  <nav>
    <a href="/" bind:aria-current="route.isActive('/')">Home</a>
    <a href="/about" bind:aria-current="route.isActive('/about')">About</a>
  </nav>
  <p class="path">You are at {route.path.value}</p>
  <main><MeekoOutlet /></main>
</template>

isActive returns "page" on the exact route and false elsewhere; bind: removes the attribute for false.

Type#

ts
function useRoute(): RouteState

RouteState

ts
interface RouteState {
  readonly url: ReadonlySignal<string>;
  readonly path: ReadonlySignal<string>;
  readonly id: ReadonlySignal<string | null>;
  readonly params: ReadonlySignal<Readonly<Record<string, unknown>>>;
  readonly search: ReadonlySignal<Readonly<Record<string, unknown>>>;
  isActive<K extends RouteId>(
    to: K,
    ...options: ActiveArguments<K>
  ): "page" | "true" | false;
}

Parameters#

useRoute() takes no parameters. The result's isActive(to, options) method takes:

Option Type Default Description
to RouteId required An existing route ID, such as "/users/[id]". A nonexistent route is a type error.
params to's RouteParams none Dynamic segments, as in href(). Required for a dynamic route, forbidden otherwise.
prefix boolean false Also matches routes below to, with "true". "/" only matches exactly.

Return value#

A RouteState, the same object throughout the application tree:

Property Type Description
url ReadonlySignal<string> Origin-relative URL: pathname, search and, in the browser, fragment (/users/42?tab=a#bio). In SSR it never contains a fragment because the browser does not send it.
path ReadonlySignal<string> pathname, encoded as in location.pathname (/users/42).
id ReadonlySignal<string | null> Route ID in the manifest ("/users/[id]"), or null in the app/error.meeko 404 page for a URL without a route.
params ReadonlySignal<Readonly<Record<string, unknown>>> Route parameters as received by the page (validated by its schema).
search ReadonlySignal<Readonly<Record<string, unknown>>> Search parameters as received by the page; in the 404, the query string with repeated keys as arrays.
isActive(to, options?) "page" | "true" | false Since 1.5. A function, not a signal. "page" if the committed route is to (with its params); with prefix: true, "true" if below it by whole segments; otherwise false.

useRoute() takes no arguments. Outside a Meeko application (a framework render or mountClientApp), it throws Error: useRoute() necesita una aplicación de Meeko en ejecución….

Examples#

A sidebar with the active section#

meeko
<!-- app/layouts/default.meeko -->
<script lang="ts">
const route = useRoute();
const links = [
  { to: "/", label: "Home" },
  { to: "/users", label: "Users" },
  { to: "/settings", label: "Settings" },
] as const;
const inSettings = computed(() => route.isActive("/settings", { prefix: true }) !== false);
</script>

<template>
  <aside>
    <a each:of="links" each:as="link" each:key="link.to" bind:href="href(link.to)" bind:aria-current="route.isActive(link.to, { prefix: true })">{link.label}</a>
    <p if:when="inSettings.value">Changes are saved when you leave.</p>
  </aside>
  <main><MeekoOutlet /></main>
</template>

On /settings/profile, “Settings” has aria-current="true"; on /settings, aria-current="page".

A dynamic route#

meeko
<script lang="ts">
const route = useRoute();
const props = defineProps<{ userId: string }>();
</script>

<template>
  <a bind:href="href('/users/[id]', { params: { id: props.userId } })"
     bind:aria-current="route.isActive('/users/[id]', { params: { id: props.userId } })">Profile</a>
</template>

React to each navigation#

ts
import { effect } from "@meekojs/meeko";
import { useRoute } from "@meekojs/meeko/framework";

// In the <script> of app/app.meeko: one page view per confirmed navigation.
const route = useRoute();
effect(() => {
  const url = route.url.value;
  if (typeof window !== "undefined") navigator.sendBeacon?.("/api/views", url);
});

The effect runs during hydration and each committed navigation, including going back or changing only the fragment.

Read the typed query string#

search has the shape received by the page. Without a schema, each value is unknown: check it before use.

meeko
<script lang="ts">
const route = useRoute();
const tab = computed(() => {
  const value = route.search.value.tab;
  return typeof value === "string" ? value : "perfil";
});
</script>

<template><p>Tab: {tab.value}</p></template>

Common mistakes#

Compare the signal instead of its value#

Incorrectmeeko
<p if:when="route.path === '/about'">About</p>

meeko check reports TS2367: This comparison appears to be unintentional because the types 'ReadonlySignal<string>' and 'string' have no overlap.

Correctmeeko
<p if:when="route.path.value === '/about'">About</p>

A nonexistent route, or one without its params#

Incorrectmeeko
<a bind:aria-current="route.isActive('/nowhere')">Lost</a>
<a bind:aria-current="route.isActive('/users/[id]')">Profile</a>

meeko check reports TS2345: Argument of type '"/nowhere"' is not assignable to parameter of type '"/" | "/about" | "/users/[id]"' for the first and TS2554: Expected 2 arguments, but got 1 for the second, because the dynamic route requires params.

Correctmeeko
<a bind:aria-current="route.isActive('/users/[id]', { params: { id: '7' } })">Profile</a>

Use params without checking its type#

Incorrectts
const id = route.params.value.id.toUpperCase();

meeko check reports TS18046: 'route.params.value.id' is of type 'unknown': useRoute works for any route, so it does not know this route's schema. In the page, use its params props, typed by definePage; in a component, check the type:

Correctts
const id = computed(() => {
  const value = route.params.value.id;
  return typeof value === "string" ? value : "";
});

Call it in a unit test#

Incorrectts
const html = renderToString({}); // the component calls useRoute()

Throws Error: useRoute() necesita una aplicación de Meeko en ejecución: sólo funciona en componentes que renderiza el servidor del framework o que monta mountClientApp. En una prueba unitaria, pasa la ruta al componente como prop. Follow the message's advice:

Correctts
const html = renderToString({ path: "/about" }); // the component receives the route as a prop

Notes#

  • SSR. Values are those of the request, including "prerender" and "stream" routes and layouts of a "client" route.
  • Hydration. The browser hydrates with the same values as SSR, even for HTML rendered for another URL (the static adapter's 404.html, a prerendered page served with another query string). Immediately afterward, url and path become those of the address bar, with its fragment.
  • Navigation. Values change only when navigation commits, together with content, never while useNavigation().pending is true. The previous page is disposed of before effects reading the route update.
  • isActive is reactive. It reads path, so the template or computed using it updates on navigation. It returns the same result in SSR and hydration.
  • Encoding. isActive compares paths encoded like href(), without query or fragment: /categorías and /categor%C3%ADas are the same.
  • Page data. For the page's validated params and search, its props are more precise. In <script server>, the full request is in usePage().

Was this page helpful?

Add a comment

It is sent when you choose “Yes” or “No”. Only the page, your answer and the comment are stored: no cookies and no personal data.

0 of 2000 characters

Edit on GitHub: docs/en/reference/composables/useroute.md