Pages
Pages & loaders
x supports two page modes, static prerendering and server-side rendering, both powered by loaders.
Page modes
By default, pages are server-rendered (SSR). Export mode = "static" to prerender at build time.
Static pages
Static pages are rendered at build time and exported as HTML. Use this for marketing pages, blog posts, or any content that doesn't need per-request rendering.
import type { RouteProps } from "@thexjs/core";export const mode = "static";export default function About({}: RouteProps) { return ( <div className="max-w-2xl mx-auto py-12"> <h1 className="text-4xl font-bold">About</h1> <p className="mt-4 text-muted-foreground"> This page is prerendered at build time. </p> </div> );}Server pages with loaders
Server pages (the default) run a loader function on every request. The loader can fetch data, query a database, or call an external API.
import type { RouteProps, LoaderArgs } from "@thexjs/core";export async function loader({ request }: LoaderArgs) { const res = await fetch("https://api.example.com/products"); const products = await res.json(); return { products };}export default function Products({ loaderData }: RouteProps) { const { products } = loaderData as { products: Array<{ id: string; name: string; price: number }> }; return ( <div> <h1 className="text-3xl font-bold">Products</h1> <ul className="mt-6 space-y-4"> {products.map((p) => ( <li key={p.id} className="rounded-xl border border-border bg-card p-4"> <h2 className="font-semibold">{p.name}</h2> <p className="text-sm text-muted-foreground">{p.price}</p> </li> ))} </ul> </div> );}Loader with dynamic params
Combined with dynamic routing, loaders receive params parsed from the URL path.
import type { RouteProps, LoaderArgs } from "@thexjs/core";export async function loader({ params }: LoaderArgs) { const product = await db.query( "SELECT * FROM products WHERE id = ?", [params.id] ); if (!product) return new Response(null, { status: 404 }); return { product };}export default function ProductDetail({ loaderData }: RouteProps) { const { product } = loaderData as { product: { name: string; description: string; price: number } }; return ( <div> <h1 className="text-3xl font-bold">{product.name}</h1> <p className="mt-2 text-muted-foreground">{product.description}</p> <p className="mt-4 text-2xl font-bold text-primary">${product.price}</p> </div> );}RouteProps type
Page components receive RouteProps with params (a Record<string, string> of dynamic segments) and loaderData (the loader's return value). It is a plain non-generic type — cast loader data to its shape if you want inline types.
import type { RouteProps } from "@thexjs/core";interface Product { id: string; name: string; description: string; price: number;}export default function ProductDetail({ loaderData,}: RouteProps) { const { product } = loaderData as { product: Product }; return ( <div> <h1 className="text-3xl font-bold">{product.name}</h1> <p className="mt-2 text-muted-foreground">{product.description}</p> <p className="mt-4 text-2xl font-bold text-primary">${product.price}</p> </div> );}