Navigation
The client application offers a standard navigation through menus. In some cases, we would like to be able to navigate from one view to another at the click of a button or when an event occurs.
Tip
Neos now provides strongly-typed identifiers for UI elements. See Typed IDs for UI Elements for comprehensive information about using UIViews, Images, Reports, and Themes instead of hard-coded strings.
UI view open modes
A property open mode allows to choose the default opening mode in the UI view itself.
Note
The opening mode can be overridden programmatically with the navigation by code.
The open modes are :
New frame(default value) : The UI view will be opened in a new tab.Nested frame: The UI view will be opened in a nested tab.Same frame: The UI view will replace the previous UI view in the same tab. When it is closed, the previous UI view is visible again.Popup (auto size): The UI view will be opened in a popup. The popup will take the full screen (with margins).Popup (full size): The UI view will be opened in a popup. The popup will automatically adapt to the size of its content.
Warning
To avoid complex navigation cases, it is not possible to mix nested frame and same frame modes when chaining UI view openings.
For example, it is not possible to open a UI view in same frame mode from a UI view in nested frame mode. Both UI views will be opened in a nested frame.
The opposite also applies, nested frame UI views opened from a same frame UI view will be opened in same frame mode.
Navigate by code
The navigation can be done in a client-side code : in a UI view method, a UI view action or a UI view event rule.
The method
The only method to know to be able to navigate is :
NavigationOptions options = ...;
NavigateAsync(NavigationOptions options);
It expects in parameter an object of type NavigationOptions which corresponds to the navigation options.
The options
To create these options, you need to instantiate an object by passing the UI view identifier as a parameter:
Recommended approach using typed IDs:
NavigationOptions options = new NavigationOptions(UIViews.UIViewName);
Alternative approach using string (legacy):
NavigationOptions options = new NavigationOptions("UIViewName");
Tip
Use the typed UIViews whenever possible for better type safety and IntelliSense support.
The UI view identifier is the only required parameter. The other options that are optional are to be added with fluent code. We will see all the possible options.
Define how the UI view opens
When the UI view is opened, it is behaves like standard frame by default. This default behavior can be modified.
Define the target
The navigation target allows to define how the view will open. The possible values are:
NavigationTarget.NewFrame: (Default value) The UI view will be opened in a new tab.NavigationTarget.NestedFrame: The UI view will be opened in a nested tab.NavigationTarget.SameFrame: The UI view will replace the previous UI view in the same tab. When it is closed, the previous UI view is visible again.NavigationTarget.Popup: The UI view will be opened in a popup.
NavigationOptions options = new NavigationOptions(UIViews.UIViewName)
.OnTarget(NavigationTarget.Popup);
See OnTarget(NavigationTarget) for the full API reference.
Define the popup size
In case the target is NavigationTarget.Popup, it is possible to specify the size of the popup. The possible values are:
PopupSize.Auto: (Default value) The popup size will automatically adjust to the content.PopupSize.Full: The popup will be full screen (with margins).
NavigationOptions options = new NavigationOptions(UIViews.UIViewName)
.OnTarget(NavigationTarget.Popup)
.WithPopupSize(PopupSize.Full);
See WithPopupSize(PopupSize) for the full API reference.
Define the popup position
In case the target is NavigationTarget.Popup and the size is PopupSize.Auto, it is possible to specify the position of the popup. The possible values are:
PopupPosition.Center: (Default value) The popup will be centered on both axes.PopupPosition.Left: The popup will be aligned on the left-hand side of the screen.PopupPosition.Right: The popup will be aligned on the right-hand side of the screen.
NavigationOptions options = new NavigationOptions(UIViews.UIViewName)
.OnTarget(NavigationTarget.Popup)
.WithPopupSize(PopupSize.Auto)
.WithPopupPosition(PopupPosition.Right);
See WithPopupPosition(PopupPosition) for the full API reference.
Define the frame identifier
Defining an identifier to the frame is useful when the view can be opened in several ways. If the user opens the view once and then does another action to navigate to the same view, then the already opened view will be focus.
NavigationOptions options = new NavigationOptions(UIViews.UIViewName)
.WithFrameId("FrameId");
See WithFrameId(string?) for the full API reference.
Define the container identifier
By default, the view will be open in the main application container.
In an advanced view, it is possible to have view containers with the components <view-container container-id="Container1" /> and <frames-container container-id="Container2" />.
It is possible to open a view in a specific container by specifying the container identifier.
NavigationOptions options = new NavigationOptions(UIViews.UIViewName)
.WithContainerId("Container1");
See WithContainerId(string?) for the full API reference.
Pass an entity identifier
It is possible to pass a dictionary corresponding to the identifier of the entity you want to load.
Dictionary<string, object> ids = new Dictionary<string, object>();
ids.Add("Name", "EntityName");
NavigationOptions options = new NavigationOptions(UIViews.UIViewName)
.WithId(ids);
When WithId is provided, the route Get with the appropriate key values is called instead of the route GetAll.
See WithId(Dictionary<string, object>?) for the full API reference.
Pass the creation mode
By default, the view is not in creation mode. It is possible to define it.
NavigationOptions options = new NavigationOptions(UIViews.UIViewName)
.WithCreationMode(true);
See WithCreationMode(bool?, object?) for the full API reference.
Pass the page number
It is possible to set the default page number.
NavigationOptions options = new NavigationOptions(UIViews.UIViewName)
.WithPageNumber(2);
See WithPageNumber(int?) for the full API reference.
Pass the number of records by page
It is possible to set the number of records by page.
NavigationOptions options = new NavigationOptions(UIViews.UIViewName)
.WithRecordsByPage(10);
See WithRecordsByPage(int?) for the full API reference.
Pass custom parameter to the view context
It is possible to pass custom parameters to the context of the view to open using WithParameter(string, object?). The value parameter is of type object?, which means any type can be passed (string, int, bool, complex objects, etc.).
NavigationOptions options = new NavigationOptions(UIViews.UIViewName)
.WithParameter("CustomerName", "Contoso")
.WithParameter("CustomerId", 42)
.WithParameter("IsActive", true);
The parameters are accessible in the code of the opened view through the IViewContext property, which is an IDictionary<string, object>. Since all values are stored as object, you must cast them back to the original type that was passed.
string name = (string)ViewContext["CustomerName"];
int id = (int)ViewContext["CustomerId"];
bool isActive = (bool)ViewContext["IsActive"];
Caution
Always cast to the exact type that was passed. For example, if you pass an int value
with WithParameter("MyKey", 42), you must read it as (int)ViewContext["MyKey"].
Casting to a different type (e.g. (string)ViewContext["MyKey"]) will throw an InvalidCastException at runtime.
If you are not sure whether a parameter was passed, use TryGetValue or ContainsKey to safely check for its existence:
if (ViewContext.TryGetValue("CustomerId", out object? rawValue))
{
int customerId = (int)rawValue;
}
Tip
If the browser window is refreshed, those parameters will be lost.
Therefore, if the UI view requires the parameter(s) even after a browser refresh,
consider using WithUrlParameter instead (note: URL parameters are strings only).
Disable view context inheritance and pass new parameters
By default, the view context of the callee inherits from the view context of the caller and the WithParameter(string, object?) method simply registers parameters to be added to this future context.
The WithNewParameters() method allows to disable the inheritance mechanism and use a brand new dictionary of parameters as a context for the UI that will be opened.
NavigationOptions options = new NavigationOptions(UIViews.UIViewName)
.WithNewParameters() // disable inheritance
.WithParameter("ParameterKey", "ParameterValue");
Or with the old syntax:
NavigationParameters parameters = new NavigationParameters()
.Add("ParameterKey", "ParameterValue");
NavigationOptions options = new NavigationOptions(UIViews.UIViewName)
.WithNewParameters(parameters);
Warning
This method overwrites the context of the calling view.
The context includes not only the parameters but also the properties of the class
NavigationParameters.
For this reason, this method should be called before any of the other properties is being set
(i.e. before WithId, WithCreationMode, WithPageNumber, WithRecordsByPage, etc. are called).
Pass frame parameter via the URL
When using the WithParameter method (or any of its variants), the information is lost if the browser window is refreshed.
The WithUrlParameter(string, string) method on the other hand, stores the parameters in the URL and makes them available for the next frame (and that frame only).
Reserved navigation values and user-defined URL context values do not use the same query-string keys:
- Reserved public navigation keys are written by the framework to restore a frame, for example
ui,frameId,title, andmi. - User-defined values passed with
WithUrlParameterare written with thef_prefix and are exposed inUrlContextwithout that prefix.
For example, a bookmark URL can look like this:
?ui=OrdersUIView&title=Sales+orders&mi=Sales%2FOrders&f_CustomerId=42
In that example:
titlestores the explicit navigation title.mistores the originating menu item path.f_CustomerIdstores a user-defined URL context value that is later available asUrlContext["CustomerId"].
Important
Unlike WithParameter, which accepts object?, WithUrlParameter only accepts string values.
If you need to pass a non-string value via the URL, convert it to a string first, then parse it back when reading.
// Passing the parameter (string values only)
NavigationOptions options = new NavigationOptions(UIViews.UIViewName)
.WithUrlParameter("CustomerId", customerId.ToString());
// Reading the parameter (it will always be a string)
string customerIdStr = UrlContext["CustomerId"];
int customerId = int.Parse(customerIdStr);
Important
Parameters passed with WithParameter are read from ViewContext (IDictionary<string, object>), whereas parameters passed with WithUrlParameter are read from UrlContext (IDictionary<string, string>).
Note
Reserved navigation keys such as title and mi are used by the router to restore the frame after a refresh or when reopening a bookmark/URL.
They are not exposed through UrlContext.
Caution
URL values are visible to anyone who can access or share the URL.
WithParameter hides them from the URL, but values remain accessible in the client environment (e.g. browser tools) and are not secure.
Sensitive data should only be handled in trusted server-side code.
Note
Additionally, be careful with the maximum size of the URL as browsers and servers have limits on URL length. Avoid stuffing the URL with excessive parameters.
Comparison between WithParameter and WithUrlParameter
WithParameter |
WithUrlParameter |
|
|---|---|---|
| Value type | object? (any type) |
string only |
| Survives browser refresh | No | Yes |
| Visible in URL | No | Yes (f_-prefixed user-defined keys only) |
| Read from | ViewContext |
UrlContext |
| Value cast | Cast to the original type | Already a string |
| Suitable for sensitive data | Yes | No |
| Suitable for complex objects | Yes | No |
| Reserved navigation metadata | Not applicable | Stored separately with public reserved keys |
Pass a filter to the view
It is possible to pass a Filter to the view to be opened. The filter will be applied to the view's data source, allowing you to pre-filter the data displayed.
Filter filter = new Filter("PropertyName", FilterOperator.Equals, "Value");
NavigationOptions options = new NavigationOptions(UIViews.UIViewName)
.WithFilter(filter);
See WithFilter(Filter?) for the full API reference.
Override the view title
Use WithTitle(string?) only when you really need a title that is different from the menu title or the UI view title.
Title resolution follows this precedence order:
WithTitleoverride.- The title resolved from the menu item path.
- The UI view title.
NavigationOptions options = new NavigationOptions(UIViews.UIViewName)
.WithTitle("Title");
Because the title override is now written to the public title query parameter, it becomes part of the URL that can be bookmarked and/or shared. After a browser refresh, or when a user reopens that URL later, Neos restores the frame with the same explicit title.
Similarly, the originating menu item path is written to the public mi query parameter. This allows Neos to relocate the frame in the same menu context after a refresh and restore the menu-based title when no explicit title is present.
Caution
Avoid WithTitle when the menu title or the UI view title already provides the correct label.
An unnecessary override changes the visible URL and becomes the title restored after refresh.
Especially considering that this text is not localized and therefore makes this URL both less portable and less future-proof.
See WithTitle(string?) for the full API reference.
Override the view icon
Recommended approach using typed IDs:
NavigationOptions options = new NavigationOptions(UIViews.UIViewName)
.WithIconId(Images.IconName);
Alternative approach using string (legacy):
NavigationOptions options = new NavigationOptions(UIViews.UIViewName)
.WithIconId("IconName");
Tip
Use the typed Images class whenever possible for better type safety and to ensure the icon exists.
See WithIconId(ImageId?) for the full API reference.
Indicate if the view can be closed by the user
By default, the view is closeable by the user. In some cases, we may wish that the view cannot be closed by the user but only by code.
NavigationOptions options = new NavigationOptions(UIViews.UIViewName)
.WithCloseable(false);
See WithCloseable(bool?) for the full API reference.
Pass a data source and the position at the opening
It is possible to share a data source with the view to be opened and set the default position at opening.
To do so, you just have to use the following method:
NavigationOptions options = new NavigationOptions(UIViews.UIViewName)
.WithDataSharing(Datasource, Position); // "Datasource" is the data source and "Position" is the current position of the calling view.
For more information on data sharing, see this article.
Define the callback function called when the UI view is closed
It is possible to define a callback function which will be called when the open UI view is closed. It takes as a parameter the result of the navigation NavigationResult<TValue> which will be fed into the open UI view.
The WithCallback<TValue>(Action<NavigationResult<TValue>>) method will be typed with the value of the navigation result. In the example, the open UI view will return a list of strings.
NavigationOptions options = new NavigationOptions(UIViews.UIViewName)
.WithCallback<IList<string>>(async r =>
{
if (r.State == NavigationResultState.Ok)
{
foreach (string property in r.Value)
{
await Methods.AddProperty(property);
}
}
});
The NavigationResultState enum indicates the outcome of the navigation.
There is two ways to feed the result into the open UI view :
- Define the result when the method CloseAsync(bool, NavigationResult?) is called
Here is the content of a method attached to a button of a UI view. In this example, the name of the selected items in the datagrid are used to feed the value of the result brought back by the navigation.
NavigationResult result = new NavigationResult(
NavigationResultState.Ok,
this.SelectedItems.Select(i => i.Name));
CloseAsync(false, result);
- Use the IUIClosingArgs of the OnClosing event
Here is the content of the OnClosing event which is called as soon as the UI view is closed. Note that the event is triggered even when the X button is clicked on the UI view.
Arguments.Result = new NavigationResult(
NavigationResultState.Ok,
this.SelectedItems.Select(i => i.Name));
Navigating event rule
In UI view, a Navigating event rule is triggered when navigating to a different UI view. The NavigateAsync method will raise the event rule.
In UI component code, you may also find a NavigateAsync method. However, this method does not call any Navigating event rules.
Additionally, in UI shared methods, you can use the UI.NavigateAsync method. This method also does not trigger any Navigating event rules.
Common patterns
Refresh the calling list after an add or edit UI closes
A frequent need is to refresh a list after the user finishes adding or editing an item. The recommended approach uses a navigation callback so the list reloads automatically when the detail UI closes.
The pattern involves two sides:
| Side | Where | Responsibility |
|---|---|---|
| Caller (list UI) | Navigating event rule |
Attach a callback that reloads data when the detail UI closes |
| Callee (detail UI) | Action or DataSaved event rule |
Close with a NavigationResult containing the saved item's identifier |
Step 1 - Caller: attach the callback
In the Navigating event rule of the list UI view, intercept navigations toward the add/edit screens and attach a callback:
if (Arguments.Options.UIViewId == AddingUIViewId
|| Arguments.Options.UIViewId == EditingUIViewId)
{
Arguments.Options.WithCallback<int?>(async args =>
{
if (args.State == NavigationResultState.Ok)
{
if (args.Value != null)
{
var item = Datasource.FirstOrDefault(i => i.Id == args.Value);
if (item != null)
{
await ReloadDataAsync(item);
return;
}
}
await LoadDataAsync();
}
});
}
Tip
Placing the callback in the Navigating event rule keeps the refresh logic in one place, regardless of which action opens the detail UI.
The callback picks the most efficient reload strategy:
| Scenario | Corresponding C# condition | Reload strategy |
|---|---|---|
| The callback receives an identifier and the item exists in the datasource | args.State == NavigationResultState.Ok && args.Value != null && item != null |
Partial reload - only the edited row is refreshed |
| The UI created a new item | args.State == NavigationResultState.Ok && args.Value != null && item == null |
Full reload |
| The callback does not return an identifier | args.State == NavigationResultState.Ok && args.Value == null |
Full reload |
| The item cannot be found in the datasource | args.State == NavigationResultState.Ok && args.Value != null && item == null |
Full reload |
| The user closed the detail UI without saving | args.State == NavigationResultState.Cancel |
No reload |
| The detail UI closed due to an error | args.State == NavigationResultState.Fail |
No reload |
Warning
Partial reload is only safe when the edit does not affect sorting, filtering, or other visible rows.
Because ReloadDataAsync(item) refreshes a single row in place, the list will not re-sort, re-filter, or update other rows that may depend on the edited data.
If the edited property is used as a sort key, a filter criterion, or a computed value displayed on other rows (e.g. a running total), prefer a full reload (LoadDataAsync()) to ensure the list remains consistent.
Step 2 - Callee: close with a result
The detail UI must close by returning a NavigationResult whose value contains the identifier of the saved item.
Option A - In an action (use when only a specific action like Save and close should return to the caller):
if (await SaveDataAsync())
{
await CloseAsync(false, new NavigationResult(NavigationResultState.Ok, Item.Id));
}
Option B - In the DataSaved event rule (use when every successful save should close the UI and return the identifier):
if (DatasourceCurrent != null)
{
await CloseAsync(false, new NavigationResult(NavigationResultState.Ok, DatasourceCurrent.Id));
}
See Also
- Typed IDs for UI Elements - Comprehensive guide to using typed identifiers instead of hard-coded strings
- NavigationOptions - Full API reference for navigation options
- NavigationParameters - API reference for navigation parameters
- IViewContext - API reference for the view context
- NavigationResult<TValue> - API reference for navigation results