> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/withastro/astro/llms.txt
> Use this file to discover all available pages before exploring further.

# View Transitions

> Create smooth page transitions with Astro's View Transitions API for SPA-like navigation

View Transitions provide smooth, animated transitions between pages in your Astro site, creating a single-page application (SPA) experience while maintaining the benefits of multi-page architecture.

## Getting Started

Enable view transitions by adding the `<ViewTransitions />` component to your layout's `<head>`:

```astro src/layouts/Layout.astro theme={null}
---
import { ViewTransitions } from 'astro:transitions';
---

<html>
  <head>
    <meta charset="utf-8" />
    <title>My Site</title>
    <ViewTransitions />
  </head>
  <body>
    <slot />
  </body>
</html>
```

<Note>
  With just this one component, Astro handles all the complexity of page transitions, including routing, animation, and state management.
</Note>

## How It Works

When a user clicks a link:

<Steps>
  <Step title="Intercept Navigation">
    Astro intercepts the navigation and prevents the default page load
  </Step>

  <Step title="Fetch New Page">
    The new page is fetched in the background
  </Step>

  <Step title="Animate Transition">
    Elements fade out, move, or morph based on their transition names
  </Step>

  <Step title="Update Content">
    The DOM is updated with the new page content
  </Step>

  <Step title="Complete">
    Elements animate in and the transition completes
  </Step>
</Steps>

## Basic Transitions

By default, all pages get a cross-fade transition. Customize transitions using the `transition:*` directives:

```astro src/pages/index.astro theme={null}
---
import { fade, slide } from 'astro:transitions';
---

<html>
  <body>
    <header transition:persist>
      <nav>
        <a href="/">Home</a>
        <a href="/about">About</a>
      </nav>
    </header>

    <main transition:animate={slide({ duration: '0.3s' })}>
      <h1>Welcome</h1>
    </main>
  </body>
</html>
```

## Transition Directives

### transition:name

Persist or morph elements across pages by giving them the same name:

```astro src/pages/products/[id].astro theme={null}
---
const { id } = Astro.params;
const product = await getProduct(id);
---

<img 
  src={product.image} 
  alt={product.name}
  transition:name={`product-${id}`}
/>

<h1 transition:name={`title-${id}`}>
  {product.name}
</h1>
```

Elements with the same `transition:name` will smoothly morph between pages:

```astro src/pages/products/index.astro theme={null}
---
const products = await getProducts();
---

<div class="grid">
  {products.map(product => (
    <a href={`/products/${product.id}`}>
      <img
        src={product.image}
        alt={product.name}
        transition:name={`product-${product.id}`}
      />
      <h2 transition:name={`title-${product.id}`}>
        {product.name}
      </h2>
    </a>
  ))}
</div>
```

<Tip>
  When navigating from the grid to a product page, the image and title smoothly animate from their list position to their detail position.
</Tip>

### transition:persist

Keep elements in the DOM across page transitions:

```astro theme={null}
<video 
  src="/video.mp4" 
  controls
  transition:persist
/>

<audio 
  src="/music.mp3"
  controls
  transition:persist
/>
```

Persisted elements maintain their state (video position, form inputs, etc.).

### transition:animate

Customize animation styles:

```astro theme={null}
---
import { fade, slide } from 'astro:transitions';
---

<!-- Fade animation -->
<div transition:animate={fade({ duration: '0.5s' })}>
  Content
</div>

<!-- Slide animation -->
<div transition:animate={slide({ duration: '0.3s' })}>
  Slides in from the right
</div>

<!-- No animation -->
<div transition:animate="none">
  Instant swap
</div>
```

## Built-in Animations

<Tabs>
  <Tab title="Fade">
    ```astro theme={null}
    ---
    import { fade } from 'astro:transitions';
    ---

    <div transition:animate={fade({ duration: '0.4s' })}>
      Fades in and out
    </div>
    ```
  </Tab>

  <Tab title="Slide">
    ```astro theme={null}
    ---
    import { slide } from 'astro:transitions';
    ---

    <div transition:animate={slide({ duration: '0.3s' })}>
      Slides from right to left
    </div>
    ```
  </Tab>

  <Tab title="Custom">
    ```astro theme={null}
    <div transition:animate={{
      old: {
        name: 'slideOut',
        duration: '0.3s',
        easing: 'ease-in'
      },
      new: {
        name: 'slideIn',
        duration: '0.3s',
        easing: 'ease-out'
      }
    }}>
      Custom animation
    </div>

    <style>
      @keyframes slideOut {
        from { opacity: 1; transform: translateX(0); }
        to { opacity: 0; transform: translateX(-100%); }
      }

      @keyframes slideIn {
        from { opacity: 0; transform: translateX(100%); }
        to { opacity: 1; transform: translateX(0); }
      }
    </style>
    ```
  </Tab>
</Tabs>

## Directional Animations

Create different animations for forward and backward navigation:

```astro theme={null}
---
import { slide } from 'astro:transitions';
---

<main transition:animate={slide({ duration: '0.3s' })}>
  <h1>Content</h1>
</main>
```

The `slide` animation automatically:

* Slides **left** when going forward
* Slides **right** when going back

## Lifecycle Events

Listen to transition events to run code during transitions:

```astro theme={null}
<script>
  document.addEventListener('astro:before-preparation', (event) => {
    console.log('About to prepare for transition');
  });

  document.addEventListener('astro:after-preparation', (event) => {
    console.log('Preparation complete');
  });

  document.addEventListener('astro:before-swap', (event) => {
    console.log('About to swap content');
  });

  document.addEventListener('astro:after-swap', (event) => {
    console.log('Content swapped');
  });

  document.addEventListener('astro:page-load', (event) => {
    console.log('Page load complete');
  });
</script>
```

### Event Reference

<ParamField path="astro:before-preparation" type="Event">
  Fires before fetching the new page
</ParamField>

<ParamField path="astro:after-preparation" type="Event">
  Fires after fetching and parsing the new page
</ParamField>

<ParamField path="astro:before-swap" type="Event">
  Fires before updating the DOM
</ParamField>

<ParamField path="astro:after-swap" type="Event">
  Fires after updating the DOM
</ParamField>

<ParamField path="astro:page-load" type="Event">
  Fires when the page is fully loaded and interactive
</ParamField>

## Preventing Transitions

Disable transitions for specific links:

```astro theme={null}
<!-- Regular link with transition -->
<a href="/page">Transitions enabled</a>

<!-- Link without transition -->
<a href="/page" data-astro-reload>No transition</a>

<!-- External links automatically skip transitions -->
<a href="https://example.com">External link</a>
```

## Fallback Behavior

View Transitions gracefully degrade in browsers that don't support the View Transitions API:

* Modern browsers: Smooth animated transitions
* Older browsers: Standard page navigation
* JavaScript disabled: Normal links work as expected

<Note>
  The View Transitions API is supported in Chrome, Edge, and other Chromium browsers. Safari and Firefox users get standard navigation.
</Note>

## Preserving State

### Form Inputs

Persist form state across navigation:

```astro theme={null}
<form transition:persist="search-form">
  <input 
    type="search" 
    name="q" 
    placeholder="Search..."
  />
  <button type="submit">Search</button>
</form>
```

### Media Playback

Keep videos and audio playing:

```astro theme={null}
<video 
  src="/background.mp4"
  autoplay
  loop
  muted
  transition:persist
/>
```

### Third-Party Scripts

Persist widgets that shouldn't reinitialize:

```astro theme={null}
<div id="chat-widget" transition:persist>
  <!-- Chat widget loads once and persists -->
</div>
```

## Advanced Patterns

### Loading Indicators

Show loading state during transitions:

```astro src/components/LoadingBar.astro theme={null}
<div id="loading-bar" class="loading-bar"></div>

<style>
  .loading-bar {
    position: fixed;
    top: 0;
    left: 0;
    width: 0;
    height: 3px;
    background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);
    transition: width 0.3s ease;
    z-index: 9999;
  }

  .loading-bar.active {
    width: 100%;
  }
</style>

<script>
  const bar = document.getElementById('loading-bar');

  document.addEventListener('astro:before-preparation', () => {
    bar?.classList.add('active');
  });

  document.addEventListener('astro:after-swap', () => {
    setTimeout(() => {
      bar?.classList.remove('active');
    }, 300);
  });
</script>
```

### Scroll Restoration

Customize scroll behavior:

```astro theme={null}
<script>
  // Scroll to top on navigation
  document.addEventListener('astro:after-swap', () => {
    window.scrollTo({ top: 0, behavior: 'smooth' });
  });

  // Preserve scroll position
  let scrollPos = 0;

  document.addEventListener('astro:before-preparation', () => {
    scrollPos = window.scrollY;
  });

  document.addEventListener('astro:after-swap', () => {
    window.scrollTo({ top: scrollPos });
  });
</script>
```

### Page-Specific Transitions

Different animations for different pages:

```astro src/pages/index.astro theme={null}
---
import { fade } from 'astro:transitions';
---

<main transition:animate={fade({ duration: '0.5s' })}>
  <h1>Home</h1>
</main>
```

```astro src/pages/gallery.astro theme={null}
---
import { slide } from 'astro:transitions';
---

<main transition:animate={slide({ duration: '0.3s' })}>
  <h1>Gallery</h1>
</main>
```

## View Transition Scope

Create isolated transition contexts:

```astro theme={null}
---
import { createAnimationScope } from 'astro:transitions';

const scope = createAnimationScope('my-section');
---

<section data-transition-scope={scope}>
  <img 
    src="/image.jpg"
    transition:name="hero"
    transition:scope={scope}
  />
</section>
```

## Performance Tips

<Steps>
  <Step title="Use transition:name sparingly">
    Only add `transition:name` to elements that truly need morphing. Too many can impact performance.
  </Step>

  <Step title="Keep animations short">
    Transitions under 300ms feel snappy. Avoid durations over 500ms.
  </Step>

  <Step title="Optimize images">
    Use optimized images for smoother transitions, especially for morphing elements.
  </Step>

  <Step title="Test on slow connections">
    View transitions wait for the new page to load. Test on throttled connections.
  </Step>
</Steps>

## Accessibility

View Transitions respect user preferences:

```css theme={null}
/* Respect prefers-reduced-motion */
@media (prefers-reduced-motion: reduce) {
  :root {
    --animation-duration: 0.01ms !important;
  }
}
```

Astro automatically:

* Announces page changes to screen readers
* Updates the document title
* Manages focus appropriately
* Respects `prefers-reduced-motion`

## Troubleshooting

<AccordionGroup>
  <Accordion title="Transitions not working">
    * Ensure `<ViewTransitions />` is in your layout's `<head>`
    * Check that you're using the same layout across pages
    * Verify the View Transitions API is supported in your browser
  </Accordion>

  <Accordion title="Elements flashing">
    * Use `transition:persist` for elements that should maintain state
    * Ensure CSS is loaded before the transition starts
    * Check for conflicting animations
  </Accordion>

  <Accordion title="Scripts not running">
    * Use the `astro:page-load` event instead of `DOMContentLoaded`
    * Wrap script logic in event listeners that re-run on navigation
  </Accordion>
</AccordionGroup>

## Related Resources

<CardGroup cols={2}>
  <Card title="Islands" icon="island-tropical" href="/concepts/islands">
    Client-side interactivity
  </Card>

  <Card title="Routing" icon="route" href="/concepts/routing">
    Page routing and navigation
  </Card>

  <Card title="Layouts" icon="layout" href="/concepts/layouts">
    Shared page layouts
  </Card>

  <Card title="Performance" icon="gauge-high" href="/guides/performance">
    Optimization techniques
  </Card>
</CardGroup>
