Sunday, November 30, 2025
HomeProgrammingAnimating Multi-Web page Navigations with Browser View Transitions and Astro

Animating Multi-Web page Navigations with Browser View Transitions and Astro


Earlier, reaching a easy transition whereas navigating on the internet was difficult. We needed to juggle SPA, JavaScript, and CSS, which made compatibility, efficiency, and accessibility appear unattainable. Fortunately, with the brand new Native Browser API View Transitions and the Astro implementation, this course of is now easy. Astro takes care of the heavy lifting, decreasing the CSS and JavaScript overhead and providing true navigation through Multi Web page Utility (MPA).

On this information, we’ll stroll by way of constructing a fundamental store, profiting from this system to make sure easy transitions between pages.

Getting began

Cloning the GitHub repository

In the event you’re wanting to get began, take a look at the Github repository.

Step-by-step

Start by creating an Astro venture utilizing its installer. In the event you encounter any points or have questions, the Astro Set up Information has all of the solutions.

# Utilizing NPM
npm create astro@newest
# Utilizing Yarn
yarn create astro
# Utilizing PNPM
pnpm create astro@newest

Throughout set up, the installer will immediate you for some settings. Select the Empty venture choice as your start line.

Understanding the Folder Construction

  • parts: This folder incorporates varied parts like buttons, playing cards, and many others.
  • layouts: Right here, we retailer shared web page layouts.
  • pages: This folder incorporates pages, and navigation relies on file-based routing. Uncover extra about this within the Astro Routing Information.

Astro helps quite a lot of UI frameworks resembling React, Vue, Svelte, and extra. For this demonstration, we’ll use the Astro Syntax to create our parts. These recordsdata have an .astro extension and mix HTML, CSS, and JS.

Integrating TailwindCSS

We’ll make the most of TailwindCSS for styling on this venture. Use the Astro CLI to include it:

# Utilizing NPM
npx astro add tailwind
# Utilizing Yarn
yarn astro add tailwind
# Utilizing PNPM
pnpm astro add tailwind

Merchandise Information

For this instance, we will likely be utilizing an information set of Merchandise that incorporates some sport sneakers and shirts, however be at liberty to make use of any information you need.

We’ll place these photographs within the /publics folder since they’re already optimized for the online. By default, Astro won’t optimize photographs within the public’s folder. If you need Astro to optimize them, you need to put them within the /src folder or configure it. Be taught extra about Astro Picture Optimization

Add an icon library

On this instance, we are going to use astro-icon for the web page icons.

# Utilizing NPM
npm i astro-icon
# Utilizing Yarn
yarn add astro-icon
# Utilizing PNPM
pnpm add astro-icon

Working the venture

# Utilizing NPM
npm run dev
# Utilizing Yarn
yarn dev
# Utilizing PNPM
pnpm dev

You’ll see a clean web page.

Structure and Design

First, create an general format for the pages. It is going to be positioned below src/layouts/Structure.astro

Astro defaults render the web page statically throughout construct time, so on the prime of the web page, we see three dashes — that separate the JavaScript that will likely be executed throughout construct time (or in the course of the request time for SSR, for instance) from the remainder of the web page.

---
import { ViewTransitions } from "astro:transitions";
interface Props {
  title: string;
  description?: string;
}

const {
  title,
  description = "A easy Store inbuilt Astro utilizing View Transitions and TailwindCSS",
} = Astro.props;
---

<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta title="description" content material={description} />

    <meta title="viewport" content material="width=device-width" />
    <hyperlink rel="icon" sort="picture/svg+xml" href="/favicon.svg" />
    <meta title="generator" content material={Astro.generator} />
    <title>{title} - Astro Transitions Store</title>
    <ViewTransitions />
  </head>
  <physique>
    <most important
      class="relative max-w-6xl min-h-screen mx-auto py-6 lg:pt-10 px-4 pb-20"
    >
      <slot />
    </most important>

    <model is:world>
      :root {
      }
      physique {
        background-color: theme(colours.grey.50);
      }
      .animate-in {
        animation: animate-in 0.5s ease-in-out;
      }
      /* Firefox */
      * {
        scrollbar-width: auto;
        scrollbar-color: #c7c7c7 #ededed;
      }

      /* Chrome, Edge, and Safari */
      *::-webkit-scrollbar {
        width: 15px;
      }

      *::-webkit-scrollbar-track {
        background: #ededed;
      }

      *::-webkit-scrollbar-thumb {
        background-color: #c7c7c7;
        border-radius: 5px;
        border: 2px stable #ffffff;
      }
      @keyframes animate-in {
        0% {
          opacity: 0;
          rework: translateY(1rem);
        }
        100% {
          opacity: 1;
          rework: translateY(0);
        }
      }
    </model>
  </physique>
</html>

To make use of View Transitions, we have to import and place the <ViewTransitions /> part inside the <head> part of the format we need to use. As soon as performed, you’ll observe that navigation has adopted a fade impact.

The format expects the title and description properties from its youngster parts. Nevertheless, you may set any properties you need, resembling metadata props, for example.

We’ve additionally launched a <most important> tag that can centrally place our web page content material. The <slot /> tag is the place Astro will inject the kid parts of the format, much like the ‘kids’ prop in React.

On the backside, we use the <model is:world> tag to declare world kinds shared by this format. On this instance, we’ve outlined kinds for the browser’s scrollbar and a easy keyframe animation for the title when transitioning to the product web page.

The house web page

Let’s now create our dwelling web page, which is able to comprise the header and the merchandise record. It may be discovered at src/pages/index.astro.

---
import Structure from "../layouts/Structure.astro";
import { merchandise } from "../information";
import ProductCard from "../parts/ProductCard.astro";
---

<Structure title="Store">
  <div class="flex gap-3 items-end">
    <h1 class="text-4xl font-bold">Astro Store</h1>
  </div>
  <h3 class="text-xl text-gray-500">
    Have a look in our merchandise, be at liberty to purchase some
  </h3>
  <div class="flex flex-wrap justify-center sm:justify-normal gap-4 py-8">
    {merchandise.map((product) => <ProductCard {product} />)}
  </div>
</Structure>

We’re importing the <Structure /> part we beforehand created and assigning a title to it. We’re additionally extracting merchandise from our merchandise information that was created earlier, and we’ll be utilizing the <ProductCard /> part, which we are going to create subsequent.

Product Card

The product card is a part designed to show our product within the record. I’ve utilized some normal styling utilizing Tailwind to make sure the Product Picture, Title, Description, and Value are offered accurately.

Astro shop card example

It’s positioned below src/parts/ProductCard.astro.

---
import sort { Product } from "../information";

interface Props {
  product: Product;
}

const { product } = Astro.props;
---

<a href={`/product/${product.slug}`} class="block">
  <article
    class="group bg-flex flex-col sm:w-60 w-72 bg-white shadow-sm rounded-lg overflow-hidden hover:shadow-xl hover:shadow-gray-100 transition-all"
  >
    <div class="sm:w-60 w-72 h-60 overflow-hidden">
      <img
        src={product.cowl}
        alt={product.title}
        class="object-cover object-center w-full grayscale-[0.1] group-hover:grayscale-0 h-full rounded-md group-hover:scale-105 transition-all"
      />
    </div>
    <div class="p-4">
      <h3
        class="font-semibold truncate"
      >
        {product.title}
      </h3>
      <p
        class="text-gray-600 text-sm truncate"
      >
        {product.description}
      </p>
      <div class="text-right mt-4">
        <span class="font-semibold">${product.value}</span>
      </div>
    </div>
  </article>
</a>

The ProductCard expects a product prop and renders an <article> inside an <a> tag for navigation. In Astro, navigation will be merely achieved by utilizing an <a> tag with the href attribute pointing to the specified web page.

Product Web page

The Product web page is a dynamic web page named [slug] that corresponds to the product’s slug outlined in our merchandise information.

astro shop product page

The Product web page will likely be positioned at src/pages/product/[slug]/index.astro.

---
import { sort Product, merchandise } from "../../../information";
import Structure from "../../../layouts/Structure.astro";
import ProductCard from "../../../parts/ProductCard.astro";
import Icon from "astro-icon";
const { slug } = Astro.params;

export operate getStaticPaths() {
  return [
    ...products.map((product) => ({
      params: {
        slug: product.slug,
      },
    })),
  ];
}

const product = merchandise.discover((product) => product.slug === slug) as Product;
---

<Structure
  title={product.title}
  description={product.description}
>
  <div class="max-w-5xl mx-auto relative">
    <a
      href="/"
      class="absolute xl:-left-14 top-8 xl:-top-1 xl:bg-none bg-gradient-to-br from-gray-100 rounded p-2 z-10"
      ><Icon title="mdi:chevron-left" class="h-6 w-6" /></a
    >
    <div class="flex gap-2 pb-2 items-center text-gray-500">
      <a category="after:content-['/'] after:pl-2 capitalize" href="/">dwelling</a>
      <span class="after:content-['/'] after:pl-2 capitalize"
        >{product.class}</span
      >
      <span>{product.title}</span>
    </div>
    <div class="flex flex-col md:flex-row sm sm:gap-8">
      <div class="max-w-[450px] w-full h-full max-h-[450px]">
        <img
          src={product.cowl}
          alt={product.title}
          class="w-full h-full object-cover rounded-xl shadow-2xl shadow-gray-200 border-b"
        />
      </div>
      <article class="py-4 flex justify-between flex-col">
        <div>
          <h1 class="text-3xl sm:text-5xl font-bold animate-in">
            {product.title}
          </h1>
          <p
            class="max-w-sm py-4 text-lg"
          >
            {product.description}
          </p>
        </div>
        <div class="pt-2 sm:pt-8 text-right">
          <div class="text-3xl font-semibold">
            ${product.value}
          </div>
          <div class="text-xs text-gray-500">* This can be a fictional value</div>
          <button
            sort="button"
            class="mt-4 px-5 py-2 bg-gray-900 hover:bg-gray-800 text-white font-semibold rounded-full"
            >Add to cart</button
          >
        </div>
      </article>
    </div>
    <div class="py-6 md:py-20 max-w-3xl">
      Lorem ipsum dolor sit, amet consectetur adipisicing elit. Incidunt magnam
      quia, explicabo dolor velit aut omnis natus consequatur possimus fuga illo
      commodi asperiores dignissimos. Consequuntur nam quae commodi quas, magni
    </div>
    <h4 class="font-bold text-lg">Comparable merchandise</h4>
    <div class="flex flex-wrap justify-center sm:justify-normal gap-4">
      {
        merchandise
          .filter((p) => p.class === product.class && p.id !== product.id)
          .map((pr) => <ProductCard product={pr} />)
      }
    </div>
  </div>
</Structure>

For this web page, we count on the slug prop from the navigation Params and export a operate named getStaticPaths. Astro makes use of this operate to generate static pages (SSG) for our web site, creating pages for each product within the /merchandise/[slug] format, resembling /product/haryo-setyadi-shirt.

Within the product title, we use the .animate-in class to animate the title when coming into the web page. On the backside, we fetch related merchandise based mostly on their class.

Notice that this instance makes use of SSG, so pages are generated at construct time. In the event you want information fetched at request time, you need to use SSR. Be taught extra about SSG and SSR within the Astro Docs.

Implementing View Transitions

We’re now going to implement View Transitions between the pages we’ve created. To do that, we have to add the transition:title attribute to the weather that we need to animate throughout transitions between pages. Let’s look at our format in additional element.

  • On the dwelling web page, every product card options an Picture, Title, Description, and Value.
  • Equally, the product web page additionally shows the Picture, Title, Description, and Value for every product.

To implement easy transitions between the 2 pages, we have to hyperlink the weather on each pages utilizing a novel Transition Identify. By doing so, Astro’s View Transitions will robotically deal with the animations throughout navigation.

Step 1: Assigning transition:title to Components within the Product Card

We’ll modify the weather inside our Product Card to make sure their transition names align with these on the Product Web page.

  • src/parts/ProductCard.astro

Product Card Picture

...
      <img
        src={product.cowl}
        alt={product.title}
        transition:title={`${product.slug} picture`} 
        class="object-cover object-center w-full grayscale-[0.1] group-hover:grayscale-0 h-full rounded-md group-hover:scale-105 transition-all"
      />
...

Product Card Title

...
      <h3
        class="font-semibold truncate"
        transition:title={`${product.slug} title`}
      >
        {product.title}
      </h3>
...

Product Card Description

...
      <p
        class="text-gray-600 text-sm truncate"
        transition:title={`${product.slug} description`}
      >
        {product.description}
      </p>
...

Product Card Value Tag

...
      <div class="text-right mt-4" transition:title={`${product.slug} value`}>
        <span class="font-semibold">${product.value}</span>
      </div>
...

It’s important to notice that we’ve assigned the transition title combining the product’s slug with the title of the component. This ensures that every transition title is exclusive inside the web page, permitting Astro to seamlessly hyperlink and animate between them throughout navigation.

Step 2: Hyperlink transition:title to Corresponding Components on the Product Web page

Following the identical process, we’ll affiliate the suitable transition names to the related parts on this web page, guaranteeing a easy transition expertise.

  • /src/pages/product/[slug]/index.astro

Product Web page Picture

...
        <img
          src={product.cowl}
          alt={product.title}
          class="w-full h-full object-cover rounded-xl shadow-2xl shadow-gray-200 border-b"
          transition:title={`${slug} picture`}
        />
...

Product Web page Title

...
          <h1 class="text-3xl sm:text-5xl font-bold animate-in">
            {product.title}
          </h1>
          <div transition:title={`${slug} title`}></div>
...

It’s value noting that we assigned the transition title to a <div> adjoining to the <h1> title component slightly than to the title itself. At occasions, View Transitions can exhibit uncommon slide behaviors with bigger title parts like <h1>. By assigning it to a neighboring component, we guarantee a smoother transition for the product card title. This workaround addresses present limitations, which can be addressed in future updates.

Product Web page Description

...
          <p
            class="max-w-sm py-4 text-lg"
            transition:title={`${slug} description`}
          >
            {product.description}
          </p>
...

Product Web page Value

...
          <div class="text-3xl font-semibold" transition:title={`${slug} value`}>
            ${product.value}
          </div>
...

We’ve used constant transition names, guaranteeing they reference the corresponding parts for a seamless transition.

And similar to that, it’s performed! Upon navigation, you’ll now expertise a fascinating slide animation between the pages.

Browser Help and Accessibility

View Transitions stays an experimental function and doesn’t get pleasure from widespread assist but. For a complete understanding, evaluation the browser compatibility chart.

Astro gives a fallback for browsers that lack assist for this function, and it additionally respects the prefers-reduced-motion setting.

Astro defaults to a fallback animation for unsupported browsers. In the event you observe uncommon conduct in these environments, you may think about deactivating the fallback.

For extra details about customizing the animation and configuring the fallback see the Astro View Transitions Documentation

Design Selections for View Transitions

On cell units, transitions typically seem extra refined because of the restricted display dimension. Conversely, on bigger screens, animations can come throughout as exaggerated or overly intense, which can result in a compromised person expertise. A great design strategy is to simplify and enlarge parts, as demonstrated on this instance. Thus, it’s important that your View Transitions align along with your design decisions.

The Astro staff is actively working to refine these transitions and supply better management over animations.

Efficiency

One other essential side to think about is efficiency. Whereas internet browsers frequently optimize for higher efficiency, it’s important to profile your web site to establish and handle any extreme animations.

Ultimate Concerns

View Transitions, mixed with Astro integration, are undeniably spectacular. Nevertheless, cautious consideration is required earlier than deploying them in manufacturing apps. The appropriateness of utilizing View Transitions hinges on the character of your utility and its goal customers. As an illustration, in case your web page has a fancy UI, this function won’t be the most effective match. Nonetheless, View Transitions maintain huge potential for enhancing the person expertise on quite a few web sites.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments