The sane mans method to getting those silky route transitions.
23 October 2019
Animating between routes using react-router, for me, has always been a pain in the backside. The awful react-transition-group documentation, coupled with out-of-date blog posts and difficulty in debugging issues, at times left me wanting to take a hammer to my monitor.
I've not liked some of the existing solutions because they use absolute positioning to achieve cross-fades between routes (which have caused more headaches e.g. FOUC), or they do not cater for the fact that content may be lazy loaded. I'm not proclaiming that my solution trumps all, but it has worked for me in both personal and client projects.
This post assumes that you are:
In your App.tsx or similar entry point for your routes, enclose your Switch component inside the TransitionGroup and CSSTransition components like so:
export const App: React.FC = () => {
const location = useLocation();
return (
<div className="App">
<TransitionGroup>
<CSSTransition
key={location.pathname}
classNames="fade"
timeout={{ enter: 1000, exit: 200 }}>
<Suspense fallback={<Loading />}>
<Switch location={location}>
<Route
path="/" exact
component={React.lazy(() => import('./Home/HomeRoute'))}
/>
<Route
path="/test"
component={React.lazy(() => import('./Test/TestRoute'))}
/>
</Switch>
</Suspense>
</CSSTransition>
</TransitionGroup>
</div>
);
};
Note that we have set the timeout prop, which is defining how long we want the 'enter' and 'exit' animations to take. In this case, entering will take 1s long and exiting 0.2s. This will need to match up to the animation-duration that you will define in your SCSS later.
I usually keep global styles in a root-level styles folder (component styles live alongside the component). This contains basic site setup, SCSS variables, mixins, and utility/helper classes. I also like to prefix any animation utility classes with 'a-'.
.a-routeFadeIn {
animation: a-routeFadeIn 0.75s ease-in-out 0.25s;
animation-fill-mode: both;
}
@keyframes a-routeFadeIn {
0% {
opacity: 0;
transform: translateY(30px);
height: 0;
overflow: hidden;
}
0.01% {
height: auto;
overflow: visible;
}
100% {
opacity: 1;
transform: translateY(0);
}
}
.a-routeFadeIn.fade-exit {
animation: none;
animation-fill-mode: none;
opacity: 1;
transition: opacity 0.2s ease-in-out;
}
.a-routeFadeIn.fade-exit-active {
opacity: 0;
}
The reason why animation is used over transition is threefold:
Finally, you can see the fade-in animation takes 1s in total to complete (0.75s transition + 0.25s delay). We delay by 0.25s to give the exit animation (0.2s duration) enough time to complete, before we start our entry animation.
All that is left to do now is apply our animation utility class to each of your routes.
export const HomeRoute: React.FC<RouteProps> = (props) => {
return (
<div className="HomeRoute a-routeFadeIn">
<h1>Home</h1>
</div>
);
};
This animation will execute as soon as your route component has been lazy loaded and mounted.
I hope this helps somebody out - let me know on Twitter (@romiem) if you found this helpful or are using a similar solution!
The easy way to add social media link previews to a serverless web app.
Devon (UK) is always a great place for photography in the summer; these were taken on the southern coast, close to Exeter.