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.

Routing Examples

Blog with Dynamic Routes

A complete blog implementation with dynamic routes:

1// src/pages/blog/[slug].tsx
2"use ssg";
3import { useRouter, Link } from "heliumts/client";
4import { GetStaticPaths, GetStaticProps } from "heliumts/rsc";
5
6interface BlogPost {
7 slug: string;
8 title: string;
9 content: string;
10 date: string;
11}
12
13// Static props for the page
14export const getStaticProps: GetStaticProps<{ post: BlogPost }> = async ({ params }) => {
15 const post = await fetchPost(params.slug);
16 return { props: { post } };
17};
18
19// Generate all blog post paths
20export const getStaticPaths: GetStaticPaths = async () => {
21 const posts = await fetchAllPosts();
22 return {
23 paths: posts.map((post) => ({ params: { slug: post.slug } })),
24 fallback: false,
25 };
26};
27
28export default function BlogPost({ post }: { post: BlogPost }) {
29 const router = useRouter();
30
31 return (
32 <article className="prose max-w-2xl mx-auto">
33 <Link href="/blog">← Back to Blog</Link>
34 <h1>{post.title}</h1>
35 <time>{post.date}</time>
36 <div dangerouslySetInnerHTML={{ __html: post.content }} />
37 </article>
38 );
39}

Search with Query Parameters

Handle search queries using URL parameters:

1// src/pages/search.tsx
2import { useRouter, Link } from "heliumts/client";
3import { useState, useEffect } from "react";
4
5export default function SearchPage() {
6 const router = useRouter();
7 const query = router.searchParams.get("q") || "";
8 const category = router.searchParams.get("category") || "all";
9 const [results, setResults] = useState([]);
10
11 useEffect(() => {
12 if (query) {
13 fetchResults(query, category).then(setResults);
14 }
15 }, [query, category]);
16
17 const handleSearch = (newQuery: string) => {
18 router.push(`/search?q=${encodeURIComponent(newQuery)}&category=${category}`);
19 };
20
21 const handleCategoryChange = (newCategory: string) => {
22 router.replace(`/search?q=${query}&category=${newCategory}`);
23 };
24
25 return (
26 <div className="p-8">
27 <form onSubmit={(e) => { e.preventDefault(); handleSearch(query); }}>
28 <input
29 type="text"
30 defaultValue={query}
31 placeholder="Search..."
32 className="border p-2 rounded"
33 />
34 <select
35 value={category}
36 onChange={(e) => handleCategoryChange(e.target.value)}
37 >
38 <option value="all">All</option>
39 <option value="docs">Documentation</option>
40 <option value="blog">Blog</option>
41 </select>
42 </form>
43
44 <div className="mt-4">
45 {results.map((result) => (
46 <Link
47 key={result.id}
48 href={result.url}
49 prefetch
50 className="block p-4 hover:bg-gray-50"
51 >
52 {result.title}
53 </Link>
54 ))}
55 </div>
56 </div>
57 );
58}

Authentication Guard

Protect routes with authentication using the Redirect component:

1// src/components/AuthGuard.tsx
2import { Redirect } from "heliumts/client";
3import { useAuth } from "../hooks/useAuth";
4
5interface AuthGuardProps {
6 children: React.ReactNode;
7 redirectTo?: string;
8}
9
10export default function AuthGuard({
11 children,
12 redirectTo = "/login"
13}: AuthGuardProps) {
14 const { user, isLoading } = useAuth();
15
16 if (isLoading) {
17 return <div>Loading...</div>;
18 }
19
20 if (!user) {
21 return <Redirect to={redirectTo} replace />;
22 }
23
24 return <>{children}</>;
25}
26
27// Usage in a protected page
28// src/pages/dashboard.tsx
29import AuthGuard from "../components/AuthGuard";
30
31export default function Dashboard() {
32 return (
33 <AuthGuard>
34 <div>Protected Dashboard Content</div>
35 </AuthGuard>
36 );
37}

Build breadcrumbs based on the current route:

1// src/components/Breadcrumbs.tsx
2import { useRouter, Link } from "heliumts/client";
3
4export default function Breadcrumbs() {
5 const router = useRouter();
6 const segments = router.path.split("/").filter(Boolean);
7
8 const crumbs = segments.map((segment, index) => {
9 const href = "/" + segments.slice(0, index + 1).join("/");
10 const label = segment
11 .replace(/-/g, " ")
12 .replace(/\b\w/g, (char) => char.toUpperCase());
13
14 return { href, label };
15 });
16
17 return (
18 <nav className="flex items-center space-x-2 text-sm">
19 <Link href="/" prefetch className="text-gray-500 hover:text-gray-700">
20 Home
21 </Link>
22 {crumbs.map((crumb, index) => (
23 <span key={crumb.href} className="flex items-center">
24 <span className="mx-2 text-gray-400">/</span>
25 {index === crumbs.length - 1 ? (
26 <span className="text-gray-900 font-medium">{crumb.label}</span>
27 ) : (
28 <Link
29 href={crumb.href}
30 prefetch
31 className="text-gray-500 hover:text-gray-700"
32 >
33 {crumb.label}
34 </Link>
35 )}
36 </span>
37 ))}
38 </nav>
39 );
40}

Analytics Tracking

Track page views using router events:

1// src/components/Analytics.tsx
2import { useRouter } from "heliumts/client";
3import { useEffect } from "react";
4
5export default function Analytics() {
6 const router = useRouter();
7
8 useEffect(() => {
9 // Track initial page load
10 trackPageView(router.path);
11
12 // Track subsequent navigations
13 const unsubscribe = router.on("routeChange", ({ path }) => {
14 trackPageView(path);
15 });
16
17 return () => unsubscribe();
18 }, []);
19
20 return null;
21}
22
23function trackPageView(path: string) {
24 // Send to your analytics service
25 if (typeof window !== "undefined" && window.gtag) {
26 window.gtag("config", "GA_MEASUREMENT_ID", {
27 page_path: path,
28 });
29 }
30}

Add the Analytics component to your root layout:

1// src/pages/_layout.tsx
2import Analytics from "../components/Analytics";
3import { PageTransition } from "heliumts/client";
4
5export default function RootLayout({ children }: { children: React.ReactNode }) {
6 return (
7 <div>
8 <Analytics />
9 <PageTransition loadingClassName="opacity-60 transition-opacity">
10 {children}
11 </PageTransition>
12 </div>
13 );
14}

Show a progress bar during page transitions:

1// src/components/NavigationProgress.tsx
2import { useRouter } from "heliumts/client";
3import { useEffect, useState } from "react";
4
5export default function NavigationProgress() {
6 const router = useRouter();
7 const [progress, setProgress] = useState(0);
8 const [visible, setVisible] = useState(false);
9
10 useEffect(() => {
11 if (router.isNavigating) {
12 setVisible(true);
13 setProgress(30);
14
15 const timer = setInterval(() => {
16 setProgress((prev) => {
17 if (prev >= 90) {
18 clearInterval(timer);
19 return prev;
20 }
21 return prev + 10;
22 });
23 }, 200);
24
25 return () => clearInterval(timer);
26 } else {
27 setProgress(100);
28 const hideTimer = setTimeout(() => {
29 setVisible(false);
30 setProgress(0);
31 }, 300);
32
33 return () => clearTimeout(hideTimer);
34 }
35 }, [router.isNavigating]);
36
37 if (!visible) return null;
38
39 return (
40 <div className="fixed top-0 left-0 right-0 z-50 h-1 bg-gray-200">
41 <div
42 className="h-full bg-teal-500 transition-all duration-200"
43 style={{ width: `${progress}%` }}
44 />
45 </div>
46 );
47}

Scroll Restoration

Control scroll behavior when navigating between pages:

1// Scroll to top on navigation (default behavior)
2<Link href="/about" scrollToTop>
3 About
4</Link>
5
6// Preserve scroll position
7<Link href="/about" scrollToTop={false}>
8 About
9</Link>
10
11// Programmatic navigation with scroll control
12router.push("/about", { scrollToTop: true });
13router.push("/settings", { scrollToTop: false });