Server-Side Rendering (SSR)

HeliumTS supports server-side rendering for pages that need fully rendered HTML on first load. Use SSR when content depends on per-request data, authenticated state, or SEO-sensitive markup that cannot wait for client hydration.

Quick Start

Add the "use ssr"; directive at the top of a page file:

1"use ssr";
2
3export default function DashboardPage() {
4 return <h1>Dashboard</h1>;
5}

On every request, HeliumTS renders the page on the server, injects the markup into the HTML response, then hydrates it in the browser.

When to Use SSR

  • Authenticated dashboards that depend on request-time session state.
  • User-specific or geo-specific pages.
  • SEO-critical pages where the HTML must include real content on first response.
  • Pages that need server-side props from a database or API before render.

Server-Side Props

Export getServerSideProps from a sidecar file to fetch data per request without mixing server and client concerns in one module.

1// src/pages/profile.server.ts
2import type { GetServerSideProps } from "heliumts/server";
3
4export const getServerSideProps: GetServerSideProps = async (req, ctx) => {
5 const user = await db.users.findById(req.params.id as string);
6 return { user };
7};
1// src/pages/profile.tsx
2"use ssr";
3
4export default function ProfilePage({ user }: { user: { name: string } }) {
5 return <h1>Hello, {user.name}</h1>;
6}

Using a sidecar file keeps Fast Refresh clean and avoids the common React warning caused by exporting both components and server-only helpers from the same page module.

Layouts with SSR

Layouts wrap SSR pages the same way they wrap SPA and SSG pages, but client-only auth guards need special handling to avoid rendering empty HTML on the server.

1import { isSSR, useRouter } from "heliumts/client";
2import { useEffect } from "react";
3
4export default function AppLayout({ children }: { children: React.ReactNode }) {
5 const { isPending, data } = useSession();
6 const router = useRouter();
7
8 useEffect(() => {
9 if (!data?.session && !isPending) {
10 router.push("/login");
11 }
12 }, [data, isPending, router]);
13
14 if (!isSSR()) {
15 if (isPending) return null;
16 if (!data?.session) return null;
17 }
18
19 return <main>{children}</main>;
20}

SSR vs SSG vs SPA

ModeBest ForHTML Timing
SSRDynamic, request-aware pagesRendered per request
SSGStatic content and marketing pagesGenerated at build time
SPAClient-only flows where HTML is not required upfrontRendered after hydration

Limitations

  • Browser-only libraries that touch window or document during module evaluation must be loaded lazily.
  • Providers used during SSR need to be safe to execute on the server.
  • HeliumTS currently uses synchronous render-to-string, not streaming SSR.
HeliumTS
Note:

HeliumTS is under 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 2026.

Server-Side Rendering (SSR)

HeliumTS supports server-side rendering for pages that need fully rendered HTML on first load. Use SSR when content depends on per-request data, authenticated state, or SEO-sensitive markup that cannot wait for client hydration.

Quick Start

Add the "use ssr"; directive at the top of a page file:

1"use ssr";
2
3export default function DashboardPage() {
4 return <h1>Dashboard</h1>;
5}

On every request, HeliumTS renders the page on the server, injects the markup into the HTML response, then hydrates it in the browser.

When to Use SSR

  • Authenticated dashboards that depend on request-time session state.
  • User-specific or geo-specific pages.
  • SEO-critical pages where the HTML must include real content on first response.
  • Pages that need server-side props from a database or API before render.

Server-Side Props

Export getServerSideProps from a sidecar file to fetch data per request without mixing server and client concerns in one module.

1// src/pages/profile.server.ts
2import type { GetServerSideProps } from "heliumts/server";
3
4export const getServerSideProps: GetServerSideProps = async (req, ctx) => {
5 const user = await db.users.findById(req.params.id as string);
6 return { user };
7};
1// src/pages/profile.tsx
2"use ssr";
3
4export default function ProfilePage({ user }: { user: { name: string } }) {
5 return <h1>Hello, {user.name}</h1>;
6}

Using a sidecar file keeps Fast Refresh clean and avoids the common React warning caused by exporting both components and server-only helpers from the same page module.

Layouts with SSR

Layouts wrap SSR pages the same way they wrap SPA and SSG pages, but client-only auth guards need special handling to avoid rendering empty HTML on the server.

1import { isSSR, useRouter } from "heliumts/client";
2import { useEffect } from "react";
3
4export default function AppLayout({ children }: { children: React.ReactNode }) {
5 const { isPending, data } = useSession();
6 const router = useRouter();
7
8 useEffect(() => {
9 if (!data?.session && !isPending) {
10 router.push("/login");
11 }
12 }, [data, isPending, router]);
13
14 if (!isSSR()) {
15 if (isPending) return null;
16 if (!data?.session) return null;
17 }
18
19 return <main>{children}</main>;
20}

SSR vs SSG vs SPA

ModeBest ForHTML Timing
SSRDynamic, request-aware pagesRendered per request
SSGStatic content and marketing pagesGenerated at build time
SPAClient-only flows where HTML is not required upfrontRendered after hydration

Limitations

  • Browser-only libraries that touch window or document during module evaluation must be loaded lazily.
  • Providers used during SSR need to be safe to execute on the server.
  • HeliumTS currently uses synchronous render-to-string, not streaming SSR.