Note:
HeliumTS is under pre-beta and active development. Expect bugs and breaking changes. If you find any issues, please report them in our GitHub
A stable release is planned for early December 2025.
Page Transitions
Overview
Helium provides built-in support for smooth page transitions using React 18+ concurrent features. This prevents UI freezing when navigating to heavy pages.
How It Works:
- Lazy Loading: Pages are automatically code-split and lazy-loaded
- Prefetching: Link components prefetch pages on hover/focus
- Deferred Rendering: React renders new pages in the background
- Visual Feedback: Old content fades while new content loads
- No Blocking: UI remains responsive during heavy page renders
PageTransition Component
The PageTransition component handles all navigation transition complexity with a simple API:
1import { PageTransition } from "heliumts/client";23export default function RootLayout({ children }: { children: React.ReactNode }) {4 return (5 <div>6 <Header />7 <PageTransition8 loadingClassName="opacity-50 transition-opacity"9 fallback={<LoadingSpinner />}10 >11 {children}12 </PageTransition>13 <Footer />14 </div>15 );16}
Props
children(ReactNode): Content to wraploadingClassName(string, optional): CSS class applied during transitionsloadingStyle(CSSProperties, optional): Inline styles applied during transitionsfallback(ReactNode, optional): Suspense fallback for lazy-loaded pages
With Tailwind CSS
1<PageTransition2 loadingClassName="opacity-60 transition-opacity duration-150"3 fallback={<div className="animate-pulse">Loading...</div>}4>5 {children}6</PageTransition>
With Inline Styles
1<PageTransition2 loadingStyle={{ opacity: 0.6, transition: 'opacity 150ms ease' }}3>4 {children}5</PageTransition>
useDeferredNavigation Hook
For more control, the useDeferredNavigation hook integrates useDeferredValue and useTransition with the router:
1import { useDeferredNavigation } from "heliumts/client";23export default function Layout({ children }: { children: React.ReactNode }) {4 const { isStale, isPending, isTransitioning } = useDeferredNavigation();56 return (7 <div style={{ opacity: isTransitioning ? 0.7 : 1, transition: 'opacity 150ms' }}>8 {children}9 </div>10 );11}
Returned Values
path(string): Current path being navigated todeferredPath(string): Deferred path (may lag behind during transitions)isStale(boolean): True when showing old content while new page rendersisPending(boolean): True when a navigation transition is in progressisTransitioning(boolean): True when either navigating or showing stale content
Global Loading Indicator
Create a top loading bar that shows during navigation:
1// src/components/NavigationLoader.tsx2import { useRouter } from "heliumts/client";34export default function NavigationLoader() {5 const router = useRouter();67 if (!router.isNavigating) {8 return null;9 }1011 return (12 <div className="fixed top-0 left-0 right-0 z-50">13 <div className="h-1 bg-teal-500 animate-pulse" />14 </div>15 );16}
Use it in your root layout:
1// src/pages/_layout.tsx2import NavigationLoader from "../components/NavigationLoader";3import { PageTransition } from "heliumts/client";45export default function RootLayout({ children }: { children: React.ReactNode }) {6 return (7 <div>8 <NavigationLoader />9 <header>Global Header</header>10 <PageTransition loadingClassName="opacity-60 transition-opacity duration-150">11 {children}12 </PageTransition>13 <footer>Global Footer</footer>14 </div>15 );16}
Using isPending for Visual Feedback
Use router.isPending to show visual feedback when content is stale:
1import { useRouter } from "heliumts/client";23export default function Layout({ children }: { children: React.ReactNode }) {4 const router = useRouter();56 return (7 <div8 className="transition-opacity duration-150"9 style={{ opacity: router.isPending ? 0.7 : 1 }}10 >11 {children}12 </div>13 );14}
Best Practices
- Use PageTransition in root layout: Wrap your main content area for consistent transitions
- Keep transitions subtle: 150-200ms opacity transitions work well
- Provide a fallback: Show a loading skeleton for initial page loads
- Don't over-animate: Simple opacity changes are usually sufficient