URL Structure
Every page in a UnifyApps application lives at a deterministic URL. The URL is assembled from three parts:


<app address>/<folder segments>/<page slug>
<app address> — where your application is served. It's shown, read-only, in Settings → General → App URL. You don't choose it: on path-based workspaces the app is served under a base path made from a workspace prefix plus the app ID (
/a/<appId>by default), and on subdomain workspaces there is no path prefix.<folder segments> — one segment for each folder the page sits in. To give pages a chosen prefix, nest them in a folder.
<page slug> — the page's own slug.
📄 Example — URL construction
Given this tree in the Navigator panel:
App ├── Dashboard (slug: dashboard) → <app address>/dashboard └── orders/ (Static param folder) ├── Order List (slug: list) → <app address>/orders/list └── Order Details (slug: details) → <app address>/orders/details
The root-level Dashboard page gets a single-segment path. Pages nested inside the Orders folder inherit the orders prefix.
How the Page Path Mirrors the Folder Hierarchy
The Navigator panel displays your app's pages and folders in a tree. This tree is the source of truth for URL construction — the URL path is a direct mirror of the tree depth, the folder names and the page slugs.
Key rules:
A page at the root of the tree has a one-segment path:
<app address>/page-slug.A page inside a folder gets the folder's name prepended:
<app address>/folder-name/page-slug.Folders can be nested to any depth. Each additional level adds one path segment:
<app address>/level1/level2/page-slug.Folders themselves do not have pages — they are organizational containers only. To send visitors who open a folder's URL to a page, turn on Redirection in the folder settings.
Moving a page between folders changes its URL. Existing bookmarks and external links to the old URL will break unless you recreate the old path as a folder and turn on Redirection in its folder settings to point at the page's new location.
Warning: Changing a folder's name propagates to every page inside it. A folder with 20 pages will have all 20 URLs changed at once. Set up redirects before publishing, or coordinate the rename with any teams that have bookmarked or linked to those pages.
Dynamic Path Segments
A page can have a dynamic segment — a portion of its URL path that is replaced at runtime with a specific value, such as a record ID. Dynamic segments come from folders whose Type is Dynamic param. Every page inside such a folder can read the captured value.
A page named "Order Details" (slug details) inside an orders folder and a Dynamic param folder named orderId gets the URL:
<app address>/orders/:orderId/details
When navigating to this page, you supply a concrete value for orderId. The resulting URL might be:
<app address>/orders/order-8821/details
To add a dynamic segment:
Create a folder: In the Navigator panel, click the Create new folder icon next to the search box, or open an existing folder's + menu and choose Add sub-folder to nest it where the dynamic segment should go (for example, inside
orders).Make it dynamic: In the folder settings, set Type to Dynamic param, enter a Folder name such as
orderId, and enter a Mock value for previews in the builder. Click Save.Put the page inside it: Set the page's Parent folder to the new folder.
Reference the value in bindings: The value is available as
{{ pageParams.orderId }}anywhere on the page. Pass it to data source queries, use it in conditional logic, or display it directly in a text component.
Query Parameters
Query parameters appear after the ? in a URL. They are optional key-value pairs that do not affect routing — any page can receive any query parameter. There are two ways query parameters arrive on a page:
Go to Page action page inputs: When you configure a Go to Page action and supply Page Inputs, those inputs are serialized as query parameters:
/orders/list?status=open&assignee=user42.Manual URL manipulation: The Set URL Parameters action updates the query string in place without triggering a full navigation. This is useful for syncing filter/sort state to the URL so users can bookmark or share filtered views.
Declare query parameters in Page Settings → Query Parameters to get typed values, read with {{ pageInputs.<name> }}. Undeclared query parameters aren't in pageInputs. The raw query string, including them, is available as {{ location.search }}, without type conversion. So declare, on the destination page, every input you pass with Go to Page: only declared names reach its pageInputs.
Current URL Bindings
UnifyApps exposes the current URL through the location binding namespace, and page values through pageParams and pageInputs. These bindings update automatically when the URL changes.
Binding | Description |
|---|---|
| The full current URL, including protocol, host, path and query string. |
| The path only — everything after the host and before the query string. |
| The raw query string, for example |
| The URL fragment — the part after |
| The same values as pageParams, read from the current URL. Either works; pageParams is the usual choice. |
| A value captured by a Dynamic param folder in the page's path. Example: |
| A declared query parameter, converted to its declared type, or a value passed in by a Go to Page action. Example: |
Canonical URLs and Deep-Linking
Every page in a UnifyApps app is directly accessible by URL — there is no "session state" that must be set up first to visit a page. This means:
All pages support deep-linking. You can share a link to any page, including pages with path parameters and query parameters, and the recipient will land directly on that page in the correct state.
Dynamic pages require the parameter value in the URL. A link to
/orders/:orderIdwithout supplying anorderIdvalue can't open the page. A Go to Page action that leaves a dynamic segment empty does nothing: the builder and preview show an error message, and in the published app the navigation silently doesn't happen. Always fill Dynamic Params.Authentication is enforced before the page loads. A recipient who is not authenticated will be redirected to the login page. After successful authentication, they are redirected back to the original URL.
Tip: If your page has filters (status, date range, assignee), wire them to query parameters using the Set URL Parameters action. This makes filtered views shareable — the recipient lands on the page with the same filters applied. It also lets users bookmark a filtered view.
History Modes: pushState vs replaceState
The Go to Page action's History option controls whether the navigation adds a new browser history entry or replaces the current one.
| Mode | HTML5 API | Effect on back button | Use when |
| Push (default) | history.pushState() | Back button returns to the previous page. The old URL remains in the history stack. | Normal forward navigation — drilling into a detail, moving to the next step in a wizard, opening a new section. The vast majority of navigations should use Push. |
| Replace | history.replaceState() | Back button skips the current page and returns to the page before it. The old URL is removed from the history stack. | Redirects that should not be revisitable (post-login redirect, post-save redirect, error state recovery). Also useful when updating query parameters in place — Set URL Parameters has its own History option; choose Replace there. |
Note: The Set URL Parameters action updates the query string of the current page without triggering a navigation. It has its own History option, which defaults to Push. Choose Replace so filter changes don't add entries the user has to go Back through.
Practical Patterns
Copying the current URL to the clipboard
Wire a "Copy Link" button to a Copy to Clipboard action, and set its Value to:
{{ location.href }}
Building a link that includes page state
Construct a shareable URL string in a binding:
{{ location.href.split("?")[0] + "?tab=history" }}
Syncing an active tab to the URL
Bind a Tab component's active tab to a query parameter, and wire the tab's onChange event to a Set URL Parameters action that updates tab. Declare tab as a query parameter, and on page load read {{ pageInputs.tab }} as the initial active tab value.