Primary image for Smooth Page Transitions in Modern Browsers

Smooth Page Transitions in Modern Browsers

Enhancing user experience with smooth transitions no longer requires heavy JavaScript frameworks.

With the advent of the View Transition API, developers can implement seamless animations using minimal code.

Let’s explore how to achieve cross-page transitions using CSS (and even have a quick look at how to integrate it for dynamic content updates).

Enabling Cross-Page Transitions

To enable smooth fade transitions between pages, add the following to your CSS:

@view-transition {
  navigation: auto;
}

That’s it!

This CSS rule opts your pages into cross-document view transitions for same-origin navigations, resulting in a subtle fade effect during page changes. Ensure this rule is present in the CSS of all pages involved in the transitions.

View Transition example

I’ve uploaded a simple Django project to show off everything talked about in this blog.

Browser Support

As of March 2025, the View Transition API is supported in Chrome 126+, Edge 126+, Safari 18.2+, and Opera 112+.

Firefox is the only obvious standout. In August 2022, Mozilla’s standards team reviewed the API and expressed concerns about its complexity and the potential for creating sluggish animations, especially in multi-page applications (MPAs). They highlighted the need for further validation and exploration to ensure the API delivers a smooth and responsive user experience. ​

These concerns and Mozilla’s cautiousness about the API’s design have contributed to the delay in Firefox’s adoption of the View Transitions API. However, Mozilla continues to evaluate the API and its potential integration into future Firefox releases.​

Thankfully, nothing will break if the browser doesn’t support the View Transition API. The page will simply transition without effects.

In-Page Transitions with view-transition-name

The view-transition-name CSS property allows for smooth transitions between different states within the same page. By assigning unique names to elements, the browser can animate changes to those elements when their content or position changes.

Example: Expanding a List Item into a Detail View

List Template (list.html):

{% for item in items %}
<div
  class="item"
  style="view-transition-name: item-{{ item.id }};"
>
  <a href="{% url 'detail' item.id %}">{{ item.title }}</a>
</div>
{% endfor %}

Detail Template (detail.html):

<article
  class="detail"
  style="view-transition-name: item-{{ item.id }};"
>
  <h1>{{ item.title }}</h1>
  <p>{{ item.content }}</p>
</article>

In this setup, each item in the list has a unique view-transition-name (e.g., item-1, item-2). When a user clicks on an item, the browser smoothly animates the transition from the list view to the detail view, maintaining visual continuity. This technique is particularly useful for:

  • Expanding or collapsing content sections
  • Reordering lists
  • Updating content while preserving visual context

Integrating with Dynamic Transitions

While full-page refreshes are often sufficient in modern browsers—especially with view transitions active—there are scenarios where dynamic content updates without a full reload enhance user experience.

The Django community is very familiar with the HTMX library, which provides a simple and lightweight way to update sections of a page without a full page refresh. Another similar library that I’ve been using recently in place of this is Alpine AJAX.

While it may not be as comprehensive as HTMX, Alpine AJAX offers a simpler and more focused approach to adding AJAX capabilities to your web applications. It allows you to enhance your applications incrementally, ensuring that the core functionality remains accessible even if JavaScript is disabled. ​

Remember, often these tools aren’t even that necessary now, as modern browsers provide a much smoother cross-page transition even with full-page reloads. Especially with the View Transition method we’ve used above.

Using with Alpine AJAX

To implement smooth CSS transitions using the view-transition-name property with Alpine AJAX, add the x-merge.transition attribute to the container element.

<div id="item-grid" x-merge.transition>
  <!-- Dynamic content here -->
</div>

Using with HTMX

It’s also easy to integrate the View Transition API with HTMX. Simply add the hx-swap attribute with transition:true to enable smooth transitions.

<a href="/page2" hx-get="/page2" hx-swap="innerHTML transition:true">Page 2</a>

Conclusion

With support on nearly all major browsers (c’mon Firefox…), the API is a powerful tool and is well integrated with plenty of modern JS frameworks too enhancing the overall user experience to a near single-page application (SPA) level even in light or non-JS environments.

The ability to create fluid transitions and dynamic content updates with minimal effort is a win for developers!

Remember to check out the project on Github if you want a demo to play around with.

Chris Beaven

About the author

Chris Beaven

It’s hard to get very far in the Django community without bumping into SmileyChris. He was a member of the core developer team for Django. In addition, he maintains several open source third-party Django applications. Chris …

View Chris's profile