Following a link or submitting a form is a navigation. Navigation adds a preset of render options
that make the update feel like a full page load: responses are cached, and when the main fragment changes,
the URL updates, the page scrolls to the top and focus moves to the new content.
A plain up.render() call only swaps the fragment, and you opt into each side effect on your own.
A user who clicks a standard hyperlink or submits a form expects what browsers have always done: the address bar shows the new location, the Back button returns to the previous screen, the new page starts at the top, and focus is not stranded on an element that no longer exists. When an Unpoly feature navigates, it mimics all of this.
Following a link, submitting a form or opening an overlay is considered navigation by default. Other features only render a fragment: a validation request re-renders a form field, and a polling fragment refreshes itself in the background. Neither should move the user's scroll position or add a history entry.
| Feature | Navigates by default? |
|---|---|
[up-follow] |
yes |
up.follow() |
yes |
up.navigate() |
always |
up.visit() |
always |
[up-layer=new] |
yes |
up.layer.open() |
yes |
[up-submit] |
yes |
up.submit() |
yes |
up.render() |
no |
up.reload() |
no |
[up-validate] |
no |
up.validate() |
no |
[up-poll] |
no |
When navigating, Unpoly applies the following render options:
| Option | Effect |
|---|---|
{ history: 'auto' } |
Update history if rendering a main target |
{ scroll: 'auto' } |
Reset scroll positions if rendering a main target |
{ focus: 'auto' } |
Focus the new fragment if rendering a main target |
{ fallback: true } |
Render a main target if the response doesn't contain the given target |
{ cache: 'auto' } |
Cache responses to GET requests |
{ revalidate: 'auto' } |
Reload expired cache entries after rendering |
{ peel: 'dismiss' } |
Dismiss overlays when targeting a background layer |
Most of these options have an 'auto' value, meaning the effect only applies when the update
is significant. A link that swaps the layer's main fragment gets a new history entry and
scrolls to the top. A link that only updates a sidebar keeps the URL and the scroll position,
even though it navigates.
The same defaults also apply when the server responds with an error code,
so a failed form submission gets the same 'auto' scroll and focus behavior.
To follow a link or submit a form without navigation defaults, set an [up-navigate=false] attribute:
<a href="/users?sort=name" up-target=".users" up-navigate="false">Sort by name</a> <!-- mark: up-navigate="false" -->
Clicking the link updates the .users list and nothing else. The URL stays the same,
the scroll position is kept and the response is not cached.
The link also no longer falls back to the main fragment. When the server responds with an error,
nothing is rendered unless you set an [up-fail-target].
With [up-navigate=false] you can still enable individual effects, like [up-history=true]
or [up-scroll=top]. See the attributes for links and forms for all options.
Rendering functions like up.render() or up.reload() don't navigate by default.
To update a fragment with navigation defaults, pass a { navigate: true } option:
up.render({ target: '.content', url: '/posts/5', navigate: true }) // mark: navigate: true
The up.navigate() function does the same with less typing:
up.navigate({ target: '.content', url: '/posts/5' })
Explicit options always win over the defaults, so you can navigate and still keep the scroll position:
up.navigate({ target: '.content', url: '/posts/5', scroll: 'keep' })
The navigation defaults live in up.fragment.config.navigateOptions.
Change an option there to change it for every navigation in your app.
For instance, this animates every navigation with a cross-fade:
up.fragment.config.navigateOptions.transition = 'cross-fade'
Links and forms can still override a configured default with their own attributes,
like [up-transition=move-left] or [up-transition=false].
Sometimes a default should depend on the link or form that was activated.
Events like up:link:follow or up:form:submit have a { renderOptions } property
that lets you change the render options for the coming fragment update.
The code below keeps the scroll position when the user switches tabs, even though each tab swaps the main fragment:
up.on('up:link:follow', '.tabs a', function(event) {
event.renderOptions.scroll = false // mark-line
})
See changing options before rendering for more.
Tip Your coding agent can search these docs locally with the Unpoly agent skill