App-like page transitions (with zero JS)
Are you a freelancer struggling to secure your first few clients?
Landing clients is the hardest part of freelancing. Client Bytes has 37 tactics from two 7-figure agency owners to help.
Hey everyone,
We’ve all spent years trying to make websites feel as smooth as native mobile apps. Usually, that meant “The SPA Struggle”, using React or Next.js just to get a simple fade-in between pages.
It’s a lot of overhead just for some visual polish. The browser finally handles the page transitions for us.
The Modern Way: @view-transition
By adding a single rule to your CSS, you enable native cross-document view transitions. This means when a user clicks a link, the browser takes a snapshot of the old page and the new page, then animates between them.
@view-transition {
navigation: auto;
}
Customizing the Animation
The best part? You aren’t stuck with the default cross-fade. You can define exactly how the old page exits and the new page enters using standard CSS Keyframes.
@view-transition {
navigation: auto;
}
::view-transition-old(root) {
animation: fade-out 0.4s ease forwards;
}
::view-transition-new(root) {
animation: fade-in 0.4s ease forwards;
}
@keyframes fade-out {
to { opacity: 0; }
}
@keyframes fade-in {
from { opacity: 0; }
}
Turn your site into a slide show
You aren’t stuck with the default cross-fade. You can take control of the “old” and “new” states of the entire page (the root) to create some more creative effects.
Let’s create a slide show animation:
@view-transition {
navigation: auto;
}
::view-transition-old(root) {
animation: slide-out 0.4s ease forwards;
}
::view-transition-new(root) {
animation: slide-in 0.4s ease forwards;
}
@keyframes slide-out {
to {
transform: translateX(-100%);
opacity: 0;
}
}
@keyframes slide-in {
from {
transform: translateX(100%);
opacity: 0;
}
}
Animate specific elements
To animate individual slements we can use the view-transition-name property. When you assign a unique name to an element, the browser identifies it as a specific entity that exists in both the current and the next state of the document. Here’s an example:
.card {
view-transition-name: card;
}
/* Old element fades out */
::view-transition-old(card) {
animation: fade-out 0.35s ease forwards;
}
/* New element fades in */
::view-transition-new(card) {
animation: fade-in 0.35s ease forwards;
}
@keyframes fade-out {
to {
opacity: 0;
}
}
@keyframes fade-in {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
Why this is a win:
- Zero JavaScript: You don’t need a router or a complex state manager to handle the animation.
- Better Performance: The browser optimizes the transition, keeping the main thread free.
- UX Power-up: Your site instantly feels faster and more “premium” to the user.
Browser Support
It is currently supported in Chrome, Edge, and Safari, with Firefox support arriving behind a flag. Since it’s a “progressive enhancement,” it’s safe to add today – browsers that don’t support it will simply ignore it without breaking anything.
Happy coding!
Marko