Use this file to discover all available pages before exploring further.
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.
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.
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.
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.
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.
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>:
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.
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 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.
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/:contactIdThis pattern will match URLs like:
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
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.
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.
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.
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.
Parameters are always received as strings, regardless of the type you pass.
// Passing parametersnavigateToPage({ to: '/analytics', params: { count: 42, // Will be "42" enabled: true, // Will be "true" userId: "12345" // Will be "12345" }});// Receiving parametersconst { 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 neededconst countNumber = parseInt(count, 10);const enabledBoolean = enabled === "true";
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.
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:
Store the data on your backend
Pass only an ID through a URL parameter
Fetch the full data using the ID when the page loads
// ❌ Bad - passing large data through URLnavigateToPage({ to: '/report', params: { data: JSON.stringify(hugeDataObject) // Too large! }});// ✅ Good - pass ID and fetch datanavigateToPage({ to: '/report', params: { reportId: "report-123" // Small ID }});// In the report page, fetch the full dataconst ReportPage = () => { const { params } = usePageRoute(); // Fetch report data using the ID const reportData = useFetchReport(params.reportId); // ...};
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.
You can use either format in route definitions: Both path="docs" and path="/docs" will work the same way after normalization.
// 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 guide for more details.
Consistency in comparisons: When comparing paths using usePageRoute(), always compare against normalized paths.
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>;};
While path normalization is forgiving, it’s best practice to use consistent path formatting throughout your code.
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.