He
HeliumTS
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:

  1. Lazy Loading: Pages are automatically code-split and lazy-loaded
  2. Prefetching: Link components prefetch pages on hover/focus
  3. Deferred Rendering: React renders new pages in the background
  4. Visual Feedback: Old content fades while new content loads
  5. 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";
2
3export default function RootLayout({ children }: { children: React.ReactNode }) {
4 return (
5 <div>
6 <Header />
7 <PageTransition
8 loadingClassName="opacity-50 transition-opacity"
9 fallback={<LoadingSpinner />}
10 >
11 {children}
12 </PageTransition>
13 <Footer />
14 </div>
15 );
16}

Props

  • children (ReactNode): Content to wrap
  • loadingClassName (string, optional): CSS class applied during transitions
  • loadingStyle (CSSProperties, optional): Inline styles applied during transitions
  • fallback (ReactNode, optional): Suspense fallback for lazy-loaded pages

With Tailwind CSS

1<PageTransition
2 loadingClassName="opacity-60 transition-opacity duration-150"
3 fallback={<div className="animate-pulse">Loading...</div>}
4>
5 {children}
6</PageTransition>

With Inline Styles

1<PageTransition
2 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";
2
3export default function Layout({ children }: { children: React.ReactNode }) {
4 const { isStale, isPending, isTransitioning } = useDeferredNavigation();
5
6 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 to
  • deferredPath (string): Deferred path (may lag behind during transitions)
  • isStale (boolean): True when showing old content while new page renders
  • isPending (boolean): True when a navigation transition is in progress
  • isTransitioning (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.tsx
2import { useRouter } from "heliumts/client";
3
4export default function NavigationLoader() {
5 const router = useRouter();
6
7 if (!router.isNavigating) {
8 return null;
9 }
10
11 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.tsx
2import NavigationLoader from "../components/NavigationLoader";
3import { PageTransition } from "heliumts/client";
4
5export 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";
2
3export default function Layout({ children }: { children: React.ReactNode }) {
4 const router = useRouter();
5
6 return (
7 <div
8 className="transition-opacity duration-150"
9 style={{ opacity: router.isPending ? 0.7 : 1 }}
10 >
11 {children}
12 </div>
13 );
14}

Best Practices

  1. Use PageTransition in root layout: Wrap your main content area for consistent transitions
  2. Keep transitions subtle: 150-200ms opacity transitions work well
  3. Provide a fallback: Show a loading skeleton for initial page loads
  4. Don't over-animate: Simple opacity changes are usually sufficient