> ## Documentation Index
> Fetch the complete documentation index at: https://developers.hubspot.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

---
id: 31bac46d-6a1e-4a45-b491-d97de28c9c60
---

# Page routing

> Learn how to define routes and structure pages in app pages.

App pages support routing, allowing you to create multiple pages within your app that users can navigate between. This guide covers how to define routes, including route types, path parameters, nested routes, and how paths are normalized.

## Route types

The `PageRoutes` component supports three types of routes to handle different navigation scenarios.

### Index routes

The index route is your app's home page, rendered when users navigate to the root URL of your app. Every app pages implementation should include an index route.

```tsx theme={null}
import { createPageRouter, PageRoutes } from "@hubspot/ui-extensions/pages";

const PageRouter = createPageRouter(
  <PageRoutes>
    <PageRoutes.IndexRoute component={HomePage} />
  </PageRoutes>
);
```

**URL:** `https://app.hubspot.com/app/{HubID}/{appId}`

The index route is always accessed with an empty path or `/`. You don't need to specify a path prop for index routes.

### Named routes

Named routes define specific pages within your app. Each route has a unique path that users can navigate to.

```tsx theme={null}
<PageRoutes>
  <PageRoutes.IndexRoute component={HomePage} />
  <PageRoutes.Route path="/docs" component={DocsPage} />
  <PageRoutes.Route path="/support" component={SupportPage} />
  <PageRoutes.Route path="/analytics" component={AnalyticsPage} />
</PageRoutes>
```

<Note>
  Route paths are normalized automatically, so both `"docs"` and `"/docs"` will work. See [path normalization](#path-normalization) for details.
</Note>

**URLs:**

* `https://app.hubspot.com/app/{HubID}/{appId}/docs`
* `https://app.hubspot.com/app/{HubID}/{appId}/support`
* `https://app.hubspot.com/app/{HubID}/{appId}/analytics`

### AnyRoute component

The `AnyRoute` component defines a fallback route that renders when no other routes match. This is useful for displaying custom 404 pages or redirecting users.

```tsx theme={null}
<PageRoutes>
  <PageRoutes.IndexRoute component={HomePage} />
  <PageRoutes.Route path="/docs" component={DocsPage} />
  <PageRoutes.AnyRoute component={NotFoundPage} />
</PageRoutes>
```

The `AnyRoute` will render for any path that doesn't match defined routes, such as `/invalid-path` or `/does-not-exist`.

<Note>
  It's recommended to always include an `AnyRoute` to provide a better user experience when users navigate to undefined paths.
</Note>

### Wildcard routes (splat routes)

Wildcard routes, also known as "splat" routes, allow you to match any characters following a specific path prefix. If a route path ends with `/*`, it will match any characters following the `/`, including other `/` characters.

```tsx theme={null}
<PageRoutes>
  <PageRoutes.IndexRoute component={HomePage} />
  <PageRoutes.Route path="/files/*" component={FileBrowserPage} />
  <PageRoutes.Route path="/docs/*" component={DocsPage} />
</PageRoutes>
```

**Example matches**

The table below provides examples of route patterns and sample URLs that will and won't match:

| Route Pattern | Matches                                                                                 | Does Not Match                             |
| ------------- | --------------------------------------------------------------------------------------- | ------------------------------------------ |
| `files/*`     | `/files/documents/report.pdf`<br />`/files/images/photo.jpg`<br />`/files/a/b/c/d.txt`  | `/file/doc.pdf`<br />`/documents/file.txt` |
| `docs/*`      | `/docs/getting-started`<br />`/docs/api/endpoints`<br />`/docs/guides/advanced/routing` | `/doc/guide`<br />`/documentation/api`     |

#### Accessing the wildcard value

The matched portion of the URL is available as `params["*"]`:

```tsx theme={null}
import { Heading, Text } from "@hubspot/ui-extensions";
import { usePageRoute, PageBreadcrumbs, PageTitle } from "@hubspot/ui-extensions/pages";

const FileBrowserPage = () => {
  const { params } = usePageRoute();
  const filePath = params["*"];

  // For URL: /files/documents/report.pdf
  // filePath = "documents/report.pdf"

  return (
    <>
      <PageBreadcrumbs>
        <PageBreadcrumbs.PageLink to="/">Home</PageBreadcrumbs.PageLink>
        <PageBreadcrumbs.PageLink to="/files">Files</PageBreadcrumbs.PageLink>
        <PageBreadcrumbs.Current>{filePath}</PageBreadcrumbs.Current>
      </PageBreadcrumbs>
      <PageTitle>File Browser</PageTitle>

      <Text>Current path: {filePath}</Text>
    </>
  );
};
```

You can destructure the `*` parameter by assigning it a more descriptive name:

```tsx theme={null}
const { params: { "*": filePath } } = usePageRoute();
// or
const { params: { "*": splat } } = usePageRoute();
```

#### Wildcard routes vs AnyRoute

While both match multiple paths, they serve different purposes:

**AnyRoute component:**

* Catches any unmatched path at that nesting level
* Typically used for 404 pages or fallback routes
* Does not provide the matched path as a parameter

**Wildcard routes (`path="prefix/*"`):**

* Match a specific path pattern (e.g., `/docs/*` only matches paths starting with `/docs/`)
* The matched portion is accessible as a parameter
* Useful for file browsers, documentation sites, or any hierarchical content

```tsx theme={null}
<PageRoutes>
  <PageRoutes.IndexRoute component={HomePage} />
  <PageRoutes.Route path="/docs/*" component={DocsPage} />
  {/* Wildcard route matches /docs/anything */}

  <PageRoutes.AnyRoute component={NotFoundPage} />
  {/* AnyRoute catches everything else that doesn't match */}
</PageRoutes>
```

<Tip>
  Use wildcard routes when you need to handle hierarchical paths (like file systems or documentation structures) and need access to the full path. Use AnyRoute for general 404 handling.
</Tip>

## Nested routes

You can nest `PageRoutes` components within the route tree to create hierarchical page structures. Nest routes by adding a `<PageRoutes>` with a `path` prop as a child of the parent `<PageRoutes>`:

```tsx expandable theme={null}
import React from "react";
import { Text, hubspot } from "@hubspot/ui-extensions";
import { createPageRouter, PageRoutes, PageLink, PageBreadcrumbs, PageTitle } from "@hubspot/ui-extensions/pages";

const HomePage = () => {
  return (
    <>
      <PageBreadcrumbs>
        <PageBreadcrumbs.Current>Home</PageBreadcrumbs.Current>
      </PageBreadcrumbs>
      <PageTitle>Home</PageTitle>

      <Text>Content for the Home page...</Text>
      <PageLink to="/support">Visit Support</PageLink>
    </>
  );
};

const SupportIndexPage = () => {
  return (
    <>
      <PageBreadcrumbs>
        <PageBreadcrumbs.PageLink to="/">Home</PageBreadcrumbs.PageLink>
        <PageBreadcrumbs.Current>Support</PageBreadcrumbs.Current>
      </PageBreadcrumbs>
      <PageTitle>Support</PageTitle>

      <Text>Browse our support resources...</Text>
      <PageLink to="/support/contact-us">Contact Us</PageLink>
      <PageLink to="/support/faq">FAQ</PageLink>
    </>
  );
};

const ContactUsPage = () => {
  return (
    <>
      <PageBreadcrumbs>
        <PageBreadcrumbs.PageLink to="/">Home</PageBreadcrumbs.PageLink>
        <PageBreadcrumbs.PageLink to="/support">Support</PageBreadcrumbs.PageLink>
        <PageBreadcrumbs.Current>Contact Us</PageBreadcrumbs.Current>
      </PageBreadcrumbs>
      <PageTitle>Contact Us</PageTitle>

      <Text>Get in touch with our support team...</Text>
    </>
  );
};

const FAQPage = () => {
  return (
    <>
      <PageBreadcrumbs>
        <PageBreadcrumbs.PageLink to="/">Home</PageBreadcrumbs.PageLink>
        <PageBreadcrumbs.PageLink to="/support">Support</PageBreadcrumbs.PageLink>
        <PageBreadcrumbs.Current>FAQ</PageBreadcrumbs.Current>
      </PageBreadcrumbs>
      <PageTitle>FAQ</PageTitle>

      <Text>Find answers to common questions...</Text>
    </>
  );
};

const PageRouter = createPageRouter(
  <PageRoutes>
    <PageRoutes.IndexRoute component={HomePage} />
    <PageRoutes path="/support">
      <PageRoutes.IndexRoute component={SupportIndexPage} />
      <PageRoutes.Route path="/contact-us" component={ContactUsPage} />
      <PageRoutes.Route path="/faq" component={FAQPage} />
    </PageRoutes>
  </PageRoutes>
);

hubspot.extend<"pages">(() => <PageRouter />);
```

This creates the following URL structure:

* `/` - Home page
* `/support` - Support index page
* `/support/contact-us` - Contact us page
* `/support/faq` - FAQ page

### Nested route behavior

When using nested routes:

* A `<PageRoutes>` with a `path` prop defines a group of nested routes under that path
* The nested `IndexRoute` renders when the path matches exactly the parent path (`/support`)
* Named routes within the nested `PageRoutes` extend the parent path (`/support/contact-us`)
* Each nested level can have its own `AnyRoute` for unmatched paths within that section

### Nesting with path parameters

You can combine nested routes with path parameters to create hierarchical structures:

```tsx theme={null}
const PageRouter = createPageRouter(
  <PageRoutes>
    <PageRoutes.IndexRoute component={HomePage} />
    <PageRoutes path="/deals/:dealId">
      <PageRoutes.IndexRoute component={DealOverview} />
      <PageRoutes.Route path="/notes" component={DealNotes} />
      <PageRoutes.Route path="/notes/:noteId" component={NoteDetails} />
    </PageRoutes>
  </PageRoutes>
);
```

This creates routes like:

* `/deals/123` - Deal overview
* `/deals/123/notes` - Deal notes list
* `/deals/123/notes/456` - Specific note details

## Layout components

Layout components let you wrap groups of routes with shared UI, such as navigation bars, sidebars, or other persistent elements. A layout component receives `children` as a prop and renders the matched route's content within it.

### Basic layout

Pass a layout component to `<PageRoutes>` using the `layoutComponent` prop:

```tsx theme={null}
import { ReactNode } from "react";
import { Text, Flex } from "@hubspot/ui-extensions";
import { createPageRouter, PageRoutes, PageLink, usePageRoute } from "@hubspot/ui-extensions/pages";

function AppLayout({ children }: { children: ReactNode }) {
  const { path } = usePageRoute();

  return (
    <Flex direction="column" gap="medium">
      <Flex direction="row" gap="small">
        <PageLink to="/">Home</PageLink>
        <PageLink to="/docs">Docs</PageLink>
        <PageLink to="/support">Support</PageLink>
      </Flex>
      {children}
    </Flex>
  );
}

const PageRouter = createPageRouter(
  <PageRoutes layoutComponent={AppLayout}>
    <PageRoutes.IndexRoute component={HomePage} />
    <PageRoutes.Route path="/docs" component={DocsPage} />
    <PageRoutes.Route path="/support" component={SupportPage} />
    <PageRoutes.AnyRoute component={NotFoundPage} />
  </PageRoutes>
);
```

The `AppLayout` component will wrap every page rendered by these routes. The matched page component is rendered in place of `{children}`.

### Nested layouts

You can apply different layout components at different nesting levels. Nested `<PageRoutes>` can define their own `layoutComponent`, which will render inside the parent layout:

```tsx theme={null}
function CustomersLayout({ children }: { children: ReactNode }) {
  return (
    <Flex direction="column" gap="medium">
      <Heading>Customers</Heading>
      {children}
    </Flex>
  );
}

const PageRouter = createPageRouter(
  <PageRoutes layoutComponent={AppLayout}>
    <PageRoutes.IndexRoute component={HomePage} />
    <PageRoutes.Route path="/docs" component={DocsPage} />
    <PageRoutes path="/customers" layoutComponent={CustomersLayout}>
      <PageRoutes.IndexRoute component={ListCustomersPage} />
      <PageRoutes.Route path="/:customerId" component={ViewCustomerPage} />
    </PageRoutes>
    <PageRoutes.AnyRoute component={NotFoundPage} />
  </PageRoutes>
);
```

When a user navigates to `/customers`, the rendered output will be `AppLayout` > `CustomersLayout` > `ListCustomersPage`. Each layout wraps the content below it in the route tree.

## Path parameters

Path parameters allow you to create dynamic routes that can capture values from the URL path. This is useful for pages that display specific items, such as viewing a particular contact, deal, or custom object record.

### Defining routes with path parameters

To define a route with path parameters, use a colon (`:`) followed by the parameter name in the route path:

```tsx theme={null}
<PageRoutes>
  <PageRoutes.IndexRoute component={HomePage} />
  <PageRoutes.Route path="/view-contact/:contactId" component={ContactDetailsPage} />
  <PageRoutes.Route path="/deals/:dealId" component={DealDetailsPage} />
  <PageRoutes.Route path="/documents/:folderId/:documentId" component={DocumentPage} />
</PageRoutes>
```

You can include multiple path parameters in a single route, as shown in the `documents/:folderId/:documentId` example.

### Understanding path parameters

Path parameters are placeholders in your route definitions that capture values from the URL. When a user navigates to a URL that matches the route pattern, the parameter values are extracted and made available to your component.

**Example route pattern:** `/view-contact/:contactId`

This pattern will match URLs like:

* `/view-contact/123` (contactId = "123")
* `/view-contact/abc-def` (contactId = "abc-def")
* `/view-contact/contact-456` (contactId = "contact-456")

But will not match:

* `/view-contact` (missing contactId)
* `/view-contact/123/edit` (additional path segment)

### Multiple path parameters

When using multiple path parameters, each segment of the URL path can capture a different value:

```tsx theme={null}
// Route definition
<PageRoutes.Route path="/deals/:dealId/notes/:noteId" component={NotePage} />
```

This route pattern creates a hierarchical structure where:

* `/deals/456/notes/789` matches with `dealId="456"` and `noteId="789"`
* The path segments between parameters (`deals` and `notes`) must match exactly
* Both parameters are required for the route to match

### Best practices for path parameters

* **Use descriptive parameter names**: Choose names that clearly indicate what the parameter represents (e.g., `:contactId`, `:dealId`, not `:id`)
* **Keep parameter counts reasonable**: While you can have multiple path parameters, too many can make URLs hard to read
* **Validate parameter values**: Always validate path parameter values before using them (e.g., check if an ID exists)
* **Use path parameters for resource IDs**: Path parameters work well for resource identifiers that define what page you're viewing
* **Use query parameters for optional state**: Use query parameters for filtering, sorting, or view state that doesn't change what resource you're viewing
* **Use wildcards for hierarchical paths**: When you need to match multiple nested segments and preserve the full path (e.g., file systems, documentation), use wildcard routes instead of multiple path parameters

## Accessing route information

Use the `usePageRoute` hook to access the current page path, route ID, and all parameters (both path parameters and query parameters) in your page components. The hook returns an object with `path`, `routeId`, and `params`.

### Basic usage

```tsx theme={null}
import { Text } from "@hubspot/ui-extensions";
import { usePageRoute, PageBreadcrumbs, PageTitle } from "@hubspot/ui-extensions/pages";

const DocsPage = () => {
  const { path, params } = usePageRoute();
  const { section, topic } = params;

  return (
    <>
      <PageBreadcrumbs>
        <PageBreadcrumbs.PageLink to="/">Home</PageBreadcrumbs.PageLink>
        <PageBreadcrumbs.Current>Documentation</PageBreadcrumbs.Current>
      </PageBreadcrumbs>
      <PageTitle>Documentation</PageTitle>

      <Text>Current path: {path}</Text>
      {section && <Text>Section: {section}</Text>}
      {topic && <Text>Topic: {topic}</Text>}
    </>
  );
};
```

When a user visits `https://app.hubspot.com/app/{HubID}/{appId}/docs?section=api&topic=authentication`, the hook will return:

* `path`: `"/docs"`
* `params.section`: `"api"`
* `params.topic`: `"authentication"`

### Accessing path parameters

Path parameters are included in `params` alongside query parameters - the `usePageRoute` hook returns all parameters regardless of their source:

```tsx theme={null}
// Route definition: <PageRoutes.Route path="/view-contact/:contactId" component={ContactPage} />

const ContactDetailsPage = () => {
  const { params } = usePageRoute();
  const { contactId, tab } = params;

  // contactId comes from the path: /view-contact/123
  // tab comes from query string: ?tab=activity

  return (
    <>
      <PageBreadcrumbs>
        <PageBreadcrumbs.PageLink to="/">Home</PageBreadcrumbs.PageLink>
        <PageBreadcrumbs.PageLink to="/contacts">Contacts</PageBreadcrumbs.PageLink>
        <PageBreadcrumbs.Current>Contact {contactId}</PageBreadcrumbs.Current>
      </PageBreadcrumbs>
      <PageTitle>Contact {contactId}</PageTitle>

      <Text>Contact ID: {contactId}</Text>
      {tab && <Text>Active tab: {tab}</Text>}
    </>
  );
};
```

When a user visits `/view-contact/123?tab=activity`:

* `contactId`: `"123"` (from path)
* `tab`: `"activity"` (from query string)

### Accessing wildcard parameters

Wildcard values are accessed through the `params["*"]` property:

```tsx theme={null}
import { usePageRoute } from "@hubspot/ui-extensions/pages";

const FileBrowserPage = () => {
  const { params } = usePageRoute();
  const wildcardValue = params["*"];

  // For URL: /files/documents/2024/report.pdf
  // wildcardValue = "documents/2024/report.pdf"

  return (
    <>
      <Heading>File Browser</Heading>
      <Text>Path: {wildcardValue}</Text>
    </>
  );
};
```

You can destructure the `*` parameter by assigning it a more descriptive name:

```tsx theme={null}
const { params: { "*": filePath } } = usePageRoute();
// or
const { params: { "*": splat } } = usePageRoute();
```

When a user visits `/files/documents/2024/report.pdf` with a route defined as `path="/files/*"`:

* `params["*"]`: `"documents/2024/report.pdf"`

<Note>
  Like all page parameters, wildcard values are strings. Remember to validate and sanitize the wildcard value before using it, especially if using it to access files or resources.
</Note>

## Route IDs

Each route can be assigned a stable `id` prop that uniquely identifies it. When a route matches, its `id` is available as `routeId` from the `usePageRoute` hook. This is particularly useful in layout components where you need to determine which route is currently active without relying on path matching.

<Warning>
  Route IDs must be unique within your collection of routes. Having multiple routes with the same `id` will result in an error.
</Warning>

### Defining route IDs

Add an `id` prop to any route component:

```tsx theme={null}
const PageRouter = createPageRouter(
  <PageRoutes>
    <PageRoutes.IndexRoute id="home" component={HomePage} />
    <PageRoutes.Route id="support" path="/support" component={SupportPage} />
    <PageRoutes.AnyRoute id="not-found" component={NotFoundPage} />
  </PageRoutes>
);
```

### Accessing the route ID

Use the `routeId` property from the `usePageRoute` hook:

```tsx theme={null}
import { usePageRoute } from "@hubspot/ui-extensions/pages";

const MyComponent = () => {
  const { routeId } = usePageRoute();
  // routeId is "home", "support", "not-found", etc.
};
```

### Using route IDs in layout components

Route IDs are especially useful in layout components where you need to render different breadcrumbs, titles, or other shared UI based on the active route. Unlike path-based matching, route IDs remain stable even if route paths change, and they work reliably with dynamic path parameters.

```tsx theme={null}
import { ReactNode } from "react";
import { Text, Flex } from "@hubspot/ui-extensions";
import { createPageRouter, PageRoutes, PageLink, PageBreadcrumbs, PageTitle, usePageRoute } from "@hubspot/ui-extensions/pages";

const BREADCRUMBS: Record<string, ReactNode> = {
  home: <Text>Home</Text>,
  support: (
    <>
      <PageLink to="/">Home</PageLink>
      <Text>Support</Text>
    </>
  ),
  "not-found": (
    <>
      <PageLink to="/">Home</PageLink>
      <Text>Not Found</Text>
    </>
  ),
};

const TITLES: Record<string, string> = {
  home: "Home",
  support: "Support",
  "not-found": "Page Not Found",
};

function AppLayout({ children }: { children: ReactNode }) {
  const { routeId } = usePageRoute();

  return (
    <Flex direction="column" gap="medium">
      <PageBreadcrumbs>{BREADCRUMBS[routeId]}</PageBreadcrumbs>
      <PageTitle>{TITLES[routeId]}</PageTitle>
      {children}
    </Flex>
  );
}

const PageRouter = createPageRouter(
  <PageRoutes layoutComponent={AppLayout}>
    <PageRoutes.IndexRoute id="home" component={HomePage} />
    <PageRoutes.Route id="support" path="/support" component={SupportPage} />
    <PageRoutes.AnyRoute id="not-found" component={NotFoundPage} />
  </PageRoutes>
);
```

<Tip>
  Route IDs provide a more robust way to identify the active route compared to path matching. They don't break when paths are renamed and work naturally with routes that contain dynamic path parameters.
</Tip>

## Parameter limitations

<Warning>
  All page parameters are treated as strings. If you pass numbers, booleans, or other types, they will be converted to strings.
</Warning>

Understanding parameter limitations helps you avoid common issues when working with app pages.

### All values are strings

Parameters are always received as strings, regardless of the type you pass.

```tsx theme={null}
// Passing parameters
navigateToPage({
  to: '/analytics',
  params: {
    count: 42,           // Will be "42"
    enabled: true,       // Will be "true"
    userId: "12345"      // Will be "12345"
  }
});

// Receiving parameters
const { params } = usePageRoute();
const { count, enabled, userId } = params;
// count is "42" (string, not number)
// enabled is "true" (string, not boolean)
// userId is "12345" (string)

// You'll need to convert them if needed
const countNumber = parseInt(count, 10);
const enabledBoolean = enabled === "true";
```

<Tip>
  Always validate and convert parameter values before using them. Check for `null`, `undefined`, or invalid formats to handle cases where parameters are missing or malformed.
</Tip>

### No complex objects

You cannot pass objects, arrays, or other complex data structures as parameters. Only simple string values are supported.

```tsx theme={null}
// ❌ NOT supported
navigateToPage({
  to: '/page',
  params: {
    user: { name: "John", age: 30 },  // Objects not supported
    items: [1, 2, 3]                   // Arrays not supported
  }
});

// ✅ Supported - use JSON.stringify and parse if needed
navigateToPage({
  to: '/page',
  params: {
    user: JSON.stringify({ name: "John", age: 30 })
  }
});

// Then parse it when accessing
const { params } = usePageRoute();
const userData = params.user ? JSON.parse(params.user) : null;
```

<Warning>
  When using JSON.stringify for complex data, be mindful of URL length limits. Very large objects will create excessively long URLs.
</Warning>

### URL length limits

Query parameters are part of the URL, which has practical length limits:

* **Browsers**: Most browsers support URLs up to 2,000-8,000 characters
* **Best practice**: Keep URLs under 2,000 characters for maximum compatibility
* **Recommendation**: Use parameters for IDs and simple state, not for large data payloads

If you need to pass large amounts of data:

1. Store the data on your backend
2. Pass only an ID through a URL parameter
3. Fetch the full data using the ID when the page loads

```tsx theme={null}
// ❌ Bad - passing large data through URL
navigateToPage({
  to: '/report',
  params: {
    data: JSON.stringify(hugeDataObject)  // Too large!
  }
});

// ✅ Good - pass ID and fetch data
navigateToPage({
  to: '/report',
  params: {
    reportId: "report-123"  // Small ID
  }
});

// In the report page, fetch the full data
const ReportPage = () => {
  const { params } = usePageRoute();
  // Fetch report data using the ID
  const reportData = useFetchReport(params.reportId);
  // ...
};
```

## Path normalization

When navigating to pages or defining routes, the routing system automatically normalizes paths to ensure consistent behavior. Understanding these normalization rules can help you avoid unexpected routing issues.

### Normalization rules

The following transformations are applied to paths:

1. **Leading slashes are added**: If a path doesn't start with `/`, one is automatically added.
   * `"foo"` → `"/foo"`
   * `"docs"` → `"/docs"`

2. **Trailing slashes are removed**: Paths should not end with a slash, and any trailing slashes are automatically removed.
   * `"foo/"` → `"/foo"`
   * `"docs/"` → `"/docs"`
   * `"support/contact/"` → `"/support/contact"`

3. **Multiple consecutive slashes are collapsed**: Any sequence of multiple slashes is reduced to a single slash.
   * `"foo//bar"` → `"/foo/bar"`
   * `"docs///api"` → `"/docs/api"`
   * `"//support"` → `"/support"`

4. **Empty paths are treated as root**: An empty string or single slash both represent the home page.
   * `""` → `"/"`
   * `"/"` → `"/"`

### Examples

Here are some examples of how different path formats are normalized:

| Input Path         | Normalized Path  |
| ------------------ | ---------------- |
| `"docs"`           | `"/docs"`        |
| `"/docs"`          | `"/docs"`        |
| `"docs/"`          | `"/docs"`        |
| `"/docs/"`         | `"/docs"`        |
| `"support/faq"`    | `"/support/faq"` |
| `"support//faq"`   | `"/support/faq"` |
| `"//docs///api//"` | `"/docs/api"`    |
| `""`               | `"/"`            |
| `"/"`              | `"/"`            |

### Practical implications

Because of path normalization:

* **You can use either format in route definitions**: Both `path="docs"` and `path="/docs"` will work the same way after normalization.

```tsx theme={null}
// Both are equivalent after normalization
<PageRoutes.Route path="docs" component={DocsPage} />
<PageRoutes.Route path="/docs" component={DocsPage} />
```

* **Navigation calls are forgiving**: When navigating to pages, you don't need to worry about exact path formatting. See the [Page linking and navigation](/apps/developer-platform/add-features/ui-extensions/extension-points/app-pages/page-linking) guide for more details.

* **Consistency in comparisons**: When comparing paths using `usePageRoute()`, always compare against normalized paths.

```tsx theme={null}
import { usePageRoute } from "@hubspot/ui-extensions/pages";

const MyComponent = () => {
  const { path } = usePageRoute();

  // path is always normalized, so compare with normalized format
  if (path === "/docs") {
    // This will match /docs, docs/, /docs/, etc.
  }

  return <Text>Current page: {path}</Text>;
};
```

<Tip>
  While path normalization is forgiving, it's best practice to use consistent path formatting throughout your code.
</Tip>

## Getting the current page path

Use the `usePageRoute` hook to get the current page path. This is useful for determining which page is currently active, which can help with navigation highlighting and conditional rendering.

```tsx theme={null}
import { usePageRoute, PageLink } from "@hubspot/ui-extensions/pages";

const Navigation = () => {
  const { path } = usePageRoute();
  // For home page: "/"
  // For /docs: "/docs"
  // For /support/faq: "/support/faq"

  return (
    <Flex gap="medium">
      <Text variant={path === "/" ? "strong" : "default"}>
        <PageLink to="/">Home</PageLink>
      </Text>
      <Text variant={path === "/docs" ? "strong" : "default"}>
        <PageLink to="/docs">Docs</PageLink>
      </Text>
      <Text variant={path === "/support" ? "strong" : "default"}>
        <PageLink to="/support">Support</PageLink>
      </Text>
    </Flex>
  );
};
```

The `path` value returned by `usePageRoute` is always the normalized path, so you can reliably compare it against expected values.

### Common use cases

**Highlighting active navigation items:**

```tsx theme={null}
const { path } = usePageRoute();
const isActive = path === "/docs";
```

**Identifying the active route in layout components:**

```tsx theme={null}
const { routeId } = usePageRoute();
const showSidebar = routeId === "dashboard" || routeId === "analytics";
```

**Conditional rendering based on current page:**

```tsx theme={null}
const { path } = usePageRoute();
const showBreadcrumbs = path.includes("/support/");
```

**Analytics and tracking:**

```tsx theme={null}
const { path } = usePageRoute();
useEffect(() => {
  trackPageView(path);
}, [path]);
```

## Related resources

* [Page linking and navigation](/apps/developer-platform/add-features/ui-extensions/extension-points/app-pages/page-linking)
* [PageRoutes component reference](/apps/developer-platform/add-features/ui-extensions/ui-components/app-page-components/page-routes)
* [PageHeader component reference](/apps/developer-platform/add-features/ui-extensions/ui-components/app-page-components/page-header)
* [Create app pages](/apps/developer-platform/add-features/ui-extensions/extension-points/app-pages/create-app-pages)
* [App pages reference](/apps/developer-platform/add-features/ui-extensions/extension-points/app-pages/reference)
