Back to Blog

March 13, 2026

Reverse Animations for Back Links

The journey of making 'Back to Blog' links feel natural with Astro's View Transitions, by hijacking the navigation direction programmatically.

I had the slanted wipe transitions working beautifully. Click a project card, the page wipes in with a sharp 60° diagonal. Click the browser back button, it reverses. The wipe goes the other way. Perfect.

But then I noticed something awkward.

When you’re reading a blog post and click “Back to Blog,” the animation plays… forwards. The same direction as when you arrived. It feels wrong, like turning a page forward to go back to the previous chapter. The mental model is broken.

I needed a way to tell Astro: “This link goes back, so use the reverse animation.”

The Quest for Back Navigation

Astro’s View Transitions have a built-in concept of direction. The browser’s back button automatically triggers the backwards animation from your transition config:

const slantedWipe = {
  forwards: { old: {...}, new: {...} },
  backwards: { old: {...}, new: {...} },  // This plays on browser back
};

But custom links? They always use forwards. I needed to bridge that gap.

The Event-Based Solution

Digging deeper into the View Transitions API, I found the astro:before-preparation event. This fires before Astro starts the transition, and crucially, it exposes the navigation direction and the element that triggered it.

Here’s the breakthrough:

document.addEventListener("astro:before-preparation", (event) => {
  const trigger = event.sourceElement;
  // Check if this link should go "back"
  if (trigger?.dataset.astroTransition === "back") {
    event.direction = "back";  // Force backwards animation!
  }
});

By setting event.direction = "back", I hijack the transition direction before Astro commits to it. The backwards animation from my config plays, and the wipe goes the right way.

Why Not Just Use history.back()?

You might wonder: why not skip all this and just call window.history.back() when someone clicks “Back to Blog”? It would play the reverse animation automatically, right?

That was my first thought too. But there are real problems with that approach:

It only works if they came from your site. history.back() literally steps back through the browser history stack. If a visitor lands directly on a blog post from a Google search, clicking “Back to Blog” would send them… back to Google. Not to your blog index. That is a broken user experience.

It couples navigation to history state. The user might have arrived from anywhere, and history.back() gives them no reliable way to reach /blog. A link labeled “Back to Blog” should go to /blog, not gamble on the browser history.

It breaks the mental model. Navigation history and site structure are two different things. Just because a user is conceptually going “back” to the blog index does not mean they should literally traverse browser history. A link should navigate to a destination, not manipulate the past.

What I really wanted was not to go back in history, but to play the back animation while doing a normal navigation. The astro:before-preparation approach gives me exactly that: I navigate normally to /blog, but I tell Astro to use the reverse animation while doing so.

The Full Implementation

In my Layout.astro, I added the script to the <head>:

<ClientRouter />
<script>
  document.addEventListener("astro:before-preparation", (event) => {
    const trigger = event.sourceElement as HTMLElement | null;
    if (trigger?.dataset.astroTransition === "back" || 
        trigger?.closest("[data-astro-transition=back]")) {
      event.direction = "back";
    }
  });
</script>

Then in my blog post template, I marked the “Back to Blog” links:

<a href="/blog" data-astro-transition="back">Back to Blog</a>

Now clicking that link plays the reverse wipe animation, visually communicating “we are going back” without the user ever thinking about it.

Why This Matters

Animation direction is a form of wayfinding. When transitions always play the same way regardless of navigation context, users lose their sense of place in the information architecture.

By making “back” links visually distinct, using the reverse animation, I am reinforcing the mental model:

  • Forward = Exploring deeper, new territory
  • Back = Returning to somewhere you’ve been

It is a subtle detail, but it is the difference between a transition that feels decorative and one that feels meaningful.

What I Learned

Astro’s View Transitions are declarative by design, but the astro:before-preparation event gives you an escape hatch when you need imperative control. It is a powerful pattern: use the declarative API for 90% of cases, drop down to events for the edge cases.

The event.direction property is not just read-only. It is a two-way binding. You can inspect it to know which way the user is going, or set it to override the default behavior. That’s a flexible API.

And the data-astro-transition attribute, despite my initial confusion, has a clear purpose once you understand the event system. It is a marker for your JavaScript to read, not a directive for Astro to act on directly.

The “Back to Blog” button now wipes the page away with the same diagonal elegance as everything else, but in reverse. A small detail, but one that makes the whole experience feel more considered.