Last updated: First published:
Loading and Swapping
Astro’s support for view transitions defines two functions that are called during view transition processing and can be overridden by the user.
The first is the loader()
function, which is executed during the preparation phase, and the second is the swap()
function, which is executed during the swap DOM phase.
The loader()
function provides the new content for the next page and swap()
function implements the swapping of the DOM as part of the soft loading.
Both functions can be extended or replaced with custom code, see below.
Some functionalities like the triggering of events and starting of animations is not covered by loader()
and swap()
and can not be exchanged by redefining them. Other steps like updating history and scroll position can also not be replaced but their effect can be overridden, for example in a listener for astro:after-swap
.
Semantics
Together, the loader()
and the swap()
function provide the main functionality of Astro’s view transition processing. The view transition API provides an update callback to switch the current browser document to its new state. While this callback is executing, rendering is stopped and the browser’s view is frozen. Therefore it is important that updating the DOM happens really fast. But how can you load the next page over the network and be fast anyhow?
This is the main reason for the separation into a preparation phase with the loader()
function and a swap phase with the swap()
function. The loader()
function does the heavy lifting. All the time consuming actions like fetching and parsing the target document will happen inside the loader()
function. During the preparation phase, the user can still fully interact with the current page. Once the preparation is done, the swap()
function can directly access the future DOM in memory and quickly swap in the future DOM for the current DOM.
Loader
The loader()
function runs between the astro:before-preparation
and astro:after-preparation
events. Its purpose is to load or otherwise construct the target DOM of the navigation. It stores the result in the newDocument
property of the preparation event. During the subsequent swap phase, this value will also be available in the newDocument
property of the swap event.
The loader()
function is a asynchronous function and it can call and await other asynchronous functions. Astro will wait for settlement of the promise returned by loader()
before it fires the astro:after-preparation
event.
Swap
The swap()
function updates the current DOM at window.document
to reflect the DOM that is stored in the newDocument
property of the swap event. This is the document that the loader()
created during the preparation phase.
The swap()
function should only update the DOM. It is a synchronous function that can not wait for asynchronous task to complete. All prerequisites that need to be fulfilled for swap()
to do its job should be moved out of the swap phase into the preparation phase and into the loader()
function.
Extending Defaults
Often you might want to add some extra code at the beginning or at the end of the loader()
or swap()
function. This can be done using the patterns shown in the next two section. They look rather similar. They main difference between them is that loader()
is a asynchronous function and swap()
is not.
Extending loader()
The loader()
function can be overwritten in the astro:before-preparation
event.
This is the pattern for the extension of the loader()
function:
How can you be sure that what you named originalLoader
is indeed is the original loader? You can’t. it might as well be a new function some other event listener assigned to the loader
property before. Several listener might cooperate and build a Chain of Responsibility by wrapping the loader in several layers like an onion.
Extending swap()
And this is the chaining pattern for extending the swap()
function.
The Inner Workings
There are also use cases where you want to replace the default behavior of the loader()
or swap()
functions. In this case, you may want to know what these functions do in detail so that you can provide useful replacements.
Loader
The loader()
function can be overwritten in the astro:before-preparation
event. Its purpose is use the events data, especially event.to
, to define event.newDocument
, the future DOM that should be later swapped in.
These are implementation details, not part of the API and might change without prior notice. Here is a sketch of what the default implementation does:
-
Call
fetch()
to load the new document from theevent.to
URL. This also includes the correct handling of form data as POST requests and updatingevent.to
if the fetch was redirected to another URL. -
Make sure that the media type of the response indicates a valid form of HTML document.
-
Parse the response into
event.newDocument
. -
Imitate browsers default behavior and remove
<no-script>
elements from the parsed document. -
If the target page does not support the view transition router, fall back to a full page load, unless we are posting form data.
-
Preload all style links.
-
In DEV mode, simulate the initialization of
client:only
components and add their dynamically generated styles toevent.newDocument
.
Swap
The swap()
function can be overridden in the astro:before-swap
event. Its purpose is to quickly update the current DOM in window.document
so that it resembles the document stored in event.newDocument
.
These are implementation details, not part of the API and might change without prior notice. Here is a sketch of what the default implementation does:
- Determine which of the scripts in
event.newDocument
should not be run betweenastro:after-swap
andastro:page-load
. These are the script that were already part of the previous page and were not marked withdata-astro-rerun
. Mark those scripts as already executed inevent.newDocument
. - Swap the attributes of the
<html>
element. Typically the current attributes are just replaced with the ones from the new DOM. - Swap the children of the
<head>
element. Instead of removing all existing elements and then add the new ones, Astro keeps some elements untouched. These are a) style sheet links with same href that are present in both DOMs and b) elements with samedata-astro-transition-persist
name in both DOMs. - Save the current input focus and text selection
- Replace
window.document.body
with theevent.newDocument.body
- Replace elements in the now current body with the corresponding elements from the old body if they are marked with the same
data-astro-transition-persist
name in both DOMs. Elements with the samedata-astro-transition-persist
name can have different element types, attributes and children. - Restore the previous input focus and text selection.
For attributes of the <html>
element and child elements of the <head>
element, the order might change during swap. The <html>
and <head>
elements themselves as well as window.document
are never removed or added.
Event listeners on window
and window.document
are not affected by the swap. References to elements of the old DOM that didn’t make it to the current DOM are invalid after swap. Event listeners for elements that are no longer part of the current DOM after the swap are never called and newly added elements have no listeners unless they have been explicitly set, e.g. by using the astro:after-swap
or astro:page-load
handlers.