URL context
What is a URL context?
The URL context is a dictionary of string keys and string values.
It is populated from user-defined query-string parameters written for a frame with the f_ prefix. Those parameters are stored in the address with the prefix, but they are exposed in UrlContext without it.
Its scope is the UI view, which means that each UI view has its own URL context.
It is accessible in UI code through the UrlContext property.
Reserved navigation keys versus UrlContext
Neos also writes some reserved public navigation keys to the URL to restore a frame. This includes keys such as ui, frameId, title, and mi.
Those keys are not part of UrlContext:
titlestores an explicit title set withWithTitle.mistores the originating menu item path.f_...keys store user-defined URL context values passed withWithUrlParameter.
For example, with the URL below:
?ui=OrdersUIView&title=Sales+orders&mi=Sales%2FOrders&f_CustomerId=42
- the router uses
ui,title, andmito restore the frame, UrlContextonly contains theCustomerIdentry.
Important
If you want a value to be readable from UrlContext, pass it with WithUrlParameter("MyKey", value).
Do not add the f_ prefix yourself.
Add a value
The URL context is populated according to the content of the address bar when the UI view is opened. To make a value available from the start, pass it during navigation:
NavigationOptions options = new NavigationOptions("OrdersUIView")
.WithUrlParameter("CustomerId", customerId.ToString());
This generates a query-string key named f_CustomerId, which is later exposed as UrlContext["CustomerId"].
Update a value dynamically
Once the UI view is open, use the following methods to change the URL context and have the change reflected in the browser URL:
void SetUrlContextValue(string key, string value, bool addToHistory = false);
void SetUrlContextValues(Dictionary<string, string?> values, bool addToHistory = false);
void RemoveUrlContextValue(string key, bool addToHistory = false);
void RemoveUrlContextValues(IEnumerable<string> keys, bool addToHistory = false);
addToHistory = false(default) replaces the current browser history entry (history.replaceState): the address bar is updated, but the browser's back button does not step through this change.addToHistory = trueadds a new browser history entry (history.pushState): the back button can undo this specific change.SetUrlContextValuesaccepts nullable values: a non-null value sets or updates the key, anullvalue removes it. This lets a single call set, update, and remove several keys atomically, with a single URL synchronization:
SetUrlContextValues(new Dictionary<string, string?>
{
["PanelOpen"] = "true", // added
["CustomerId"] = "43", // updated
["OldFilter"] = null // removed
}, addToHistory: true);
- Calling
SetUrlContextValue/RemoveUrlContextValueseveral times in a row triggers one browser URL synchronization per call. UseSetUrlContextValues/RemoveUrlContextValuesto update several keys as a single synchronization. - These methods only update the physical URL when the UI view's frame currently has focus (is the active tab). The in-memory
UrlContextis always updated; a frame that is not currently active catches up automatically the next time the user switches to it. UrlContextitself is a read-only dictionary (IReadOnlyDictionary<string, string>): it does not exposeAdd/Remove/an indexer setter, so attempting to write through it (UrlContext["Key"] = value) is a compile error rather than a silently-ignored write. Always use the methods above to change a value.
Read a value
A non-existent key throws an exception, so check for the key before reading it.
if (UrlContext.ContainsKey("AgencyId"))
{
string agencyId = UrlContext["AgencyId"];
}
Or, more briefly:
if (UrlContext.TryGetValue("AgencyId", out string? agencyId))
{
// Use agencyId
}
Refresh and bookmark behavior
Because URL context values are stored in the URL, they survive a browser refresh and can be shared through bookmark URLs or hyperlinks.
Reserved navigation keys also participate in this restoration:
titlerestores the explicit title whenWithTitlewas used.mirestores the menu item path so Neos can recover the menu context and menu-based title.
Title restoration follows this precedence order after refresh:
WithTitleoverride restored fromtitle.- Menu title resolved from
mi. - UI view title.
Caution
Avoid WithTitle when the menu title or the UI view title is already appropriate.
Since WithTitle is visible in the public URL, it also affects the title restored after refresh.
React to a value restored by Back/Forward
The refresh and bookmark behavior above relies on Initialized running again on a fresh ViewModel. That does not happen when the browser's Back/Forward buttons bring the user back to a frame that is still open: its ViewModel is reused, not recreated, so Initialized never runs a second time. UrlContextRestoredCallbacks fills that gap:
IUrlContextRestoredCallbackCollection UrlContextRestoredCallbacks { get; }
Register a callback with Add(Action callback), usually right next to the code that already applies the value once at startup:
Methods.ApplySelectedTabIndexFromUrlContext();
UrlContextRestoredCallbacks.Add(() => Methods.ApplySelectedTabIndexFromUrlContext());
When it actually has a visible effect
Both conditions below must hold, otherwise the callback is never invoked, or invoked with nothing left to correct:
- The frame must still be open. Only a tab that stayed open in a
frames-container(see Define the frame identifier) can be reused this way. A page, a popup, or a view hosted in aview-containeris destroyed and recreated on every navigation into it — even when navigating back to the exact same view already displayed there — soInitializedalready covers it. This does not depend onaddToHistory: the frame is discarded beforeUrlContextRestoredCallbackswould ever be considered. - The restored value must differ from what is currently in memory. This is where
addToHistory(see Update a value dynamically above) matters:addToHistory: false(the default) keeps a single history entry for the frame, continuously replaced in place — a Back navigation skips past it entirely, to whatever came before the frame was opened. OnlyaddToHistory: truecreates a distinct, independently restorable entry; navigating back to it can expose a value older than the one currently applied, which is what the callback corrects.
For example, on a tab that stays open, two changes made with addToHistory: true:
SetUrlContextValue("PanelId", "details", addToHistory: true);
// ... later, still on the same tab ...
SetUrlContextValue("PanelId", "history", addToHistory: true);
each create their own history entry. Clicking Back once restores PanelId=details in the address bar; without a registered callback, the panel actually displayed would stay on "history", since nothing tells the ViewModel to re-read UrlContext.
Use cases
A common use case for UrlContext is to read lightweight string values passed through a hyperlink or through NavigateAsync(...).WithUrlParameter(...).
It is also used by the native ReportViewer component, where part of the state is described by the URL and preserved during a browser refresh.