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].tsx2"use ssg";3import { useRouter, Link } from "heliumts/client";4import { GetStaticPaths, GetStaticProps } from "heliumts/rsc";56interface BlogPost {7 slug: string;8 title: string;9 content: string;10 date: string;11}1213// Static props for the page14export const getStaticProps: GetStaticProps<{ post: BlogPost }> = async ({ params }) => {15 const post = await fetchPost(params.slug);16 return { props: { post } };17};1819// Generate all blog post paths20export 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};2728export default function BlogPost({ post }: { post: BlogPost }) {29 const router = useRouter();3031 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.tsx2import { useRouter, Link } from "heliumts/client";3import { useState, useEffect } from "react";45export 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([]);1011 useEffect(() => {12 if (query) {13 fetchResults(query, category).then(setResults);14 }15 }, [query, category]);1617 const handleSearch = (newQuery: string) => {18 router.push(`/search?q=${encodeURIComponent(newQuery)}&category=${category}`);19 };2021 const handleCategoryChange = (newCategory: string) => {22 router.replace(`/search?q=${query}&category=${newCategory}`);23 };2425 return (26 <div className="p-8">27 <form onSubmit={(e) => { e.preventDefault(); handleSearch(query); }}>28 <input29 type="text"30 defaultValue={query}31 placeholder="Search..."32 className="border p-2 rounded"33 />34 <select35 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>4344 <div className="mt-4">45 {results.map((result) => (46 <Link47 key={result.id}48 href={result.url}49 prefetch50 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.tsx2import { Redirect } from "heliumts/client";3import { useAuth } from "../hooks/useAuth";45interface AuthGuardProps {6 children: React.ReactNode;7 redirectTo?: string;8}910export default function AuthGuard({11 children,12 redirectTo = "/login"13}: AuthGuardProps) {14 const { user, isLoading } = useAuth();1516 if (isLoading) {17 return <div>Loading...</div>;18 }1920 if (!user) {21 return <Redirect to={redirectTo} replace />;22 }2324 return <>{children}</>;25}2627// Usage in a protected page28// src/pages/dashboard.tsx29import AuthGuard from "../components/AuthGuard";3031export default function Dashboard() {32 return (33 <AuthGuard>34 <div>Protected Dashboard Content</div>35 </AuthGuard>36 );37}
Breadcrumb Navigation
Build breadcrumbs based on the current route:
1// src/components/Breadcrumbs.tsx2import { useRouter, Link } from "heliumts/client";34export default function Breadcrumbs() {5 const router = useRouter();6 const segments = router.path.split("/").filter(Boolean);78 const crumbs = segments.map((segment, index) => {9 const href = "/" + segments.slice(0, index + 1).join("/");10 const label = segment11 .replace(/-/g, " ")12 .replace(/\b\w/g, (char) => char.toUpperCase());1314 return { href, label };15 });1617 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 Home21 </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 <Link29 href={crumb.href}30 prefetch31 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.tsx2import { useRouter } from "heliumts/client";3import { useEffect } from "react";45export default function Analytics() {6 const router = useRouter();78 useEffect(() => {9 // Track initial page load10 trackPageView(router.path);1112 // Track subsequent navigations13 const unsubscribe = router.on("routeChange", ({ path }) => {14 trackPageView(path);15 });1617 return () => unsubscribe();18 }, []);1920 return null;21}2223function trackPageView(path: string) {24 // Send to your analytics service25 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.tsx2import Analytics from "../components/Analytics";3import { PageTransition } from "heliumts/client";45export 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}
Navigation Loading Indicator
Show a progress bar during page transitions:
1// src/components/NavigationProgress.tsx2import { useRouter } from "heliumts/client";3import { useEffect, useState } from "react";45export default function NavigationProgress() {6 const router = useRouter();7 const [progress, setProgress] = useState(0);8 const [visible, setVisible] = useState(false);910 useEffect(() => {11 if (router.isNavigating) {12 setVisible(true);13 setProgress(30);1415 const timer = setInterval(() => {16 setProgress((prev) => {17 if (prev >= 90) {18 clearInterval(timer);19 return prev;20 }21 return prev + 10;22 });23 }, 200);2425 return () => clearInterval(timer);26 } else {27 setProgress(100);28 const hideTimer = setTimeout(() => {29 setVisible(false);30 setProgress(0);31 }, 300);3233 return () => clearTimeout(hideTimer);34 }35 }, [router.isNavigating]);3637 if (!visible) return null;3839 return (40 <div className="fixed top-0 left-0 right-0 z-50 h-1 bg-gray-200">41 <div42 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 About4</Link>56// Preserve scroll position7<Link href="/about" scrollToTop={false}>8 About9</Link>1011// Programmatic navigation with scroll control12router.push("/about", { scrollToTop: true });13router.push("/settings", { scrollToTop: false });
Related
- Routing Overview - File-based routing basics
- Navigation - Link component and programmatic navigation
- useRouter Hook - Router API reference
- Page Transitions - Smooth navigation transitions