Skip to main content
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.
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.
Route paths are normalized automatically, so both "docs" and "/docs" will work. See path normalization for details.
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.
The AnyRoute will render for any path that doesn’t match defined routes, such as /invalid-path or /does-not-exist.
It’s recommended to always include an AnyRoute to provide a better user experience when users navigate to undefined paths.

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.
Example matches The table below provides examples of route patterns and sample URLs that will and won’t match:

Accessing the wildcard value

The matched portion of the URL is available as params["*"]:
You can destructure the * parameter by assigning it a more descriptive name:

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
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.

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>:
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:
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:
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:
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:
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:
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

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:
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:
You can destructure the * parameter by assigning it a more descriptive name:
When a user visits /files/documents/2024/report.pdf with a route defined as path="/files/*":
  • params["*"]: "documents/2024/report.pdf"
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.

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.
Route IDs must be unique within your collection of routes. Having multiple routes with the same id will result in an error.

Defining route IDs

Add an id prop to any route component:

Accessing the route ID

Use the routeId property from the usePageRoute hook:

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.
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.

Parameter limitations

All page parameters are treated as strings. If you pass numbers, booleans, or other types, they will be converted to strings.
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.
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.

No complex objects

You cannot pass objects, arrays, or other complex data structures as parameters. Only simple string values are supported.
When using JSON.stringify for complex data, be mindful of URL length limits. Very large objects will create excessively long URLs.

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

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:

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.
  • Navigation calls are forgiving: When navigating to pages, you don’t need to worry about exact path formatting. See the Page linking and navigation guide for more details.
  • Consistency in comparisons: When comparing paths using usePageRoute(), always compare against normalized paths.
While path normalization is forgiving, it’s best practice to use consistent path formatting throughout your code.

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.
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:
Identifying the active route in layout components:
Conditional rendering based on current page:
Analytics and tracking:
Last modified on April 16, 2026