Packages
@thexjs/core
The rendering and routing engine behind x: file-based routing, SSR/SSG, islands, server functions, content collections, and a lightweight data layer.
bun add @thexjs/core
Requires Bun. You typically do not install this directly. @thexjs/cli depends on it and drives x dev / x build / x start.
Quick start
import { defineConfig } from "@thexjs/core";export default defineConfig({ pagesDir: "./src/pages", contentDir: "./src/content", port: 3000,});import type { RouteProps } from "@thexjs/core";export const mode = "static";export default function HomePage({}: RouteProps) { return <h1>Hello from x</h1>;}File-based routing
File Route────────────────────────────────────────────src/pages/index.tsx /src/pages/about.tsx /aboutsrc/pages/blog/[slug].tsx /blog/:slugsrc/pages/blog/[...rest].tsx /blog/* (catch-all)src/api/users.ts /api/userssrc/pages/_layout.tsx Wraps routes in directorysrc/pages/_middleware.ts Runs before matching routessrc/pages/_404.tsx Custom not-found pageFiles and directories prefixed with _ or . are never treated as routes.
Route modes
Every page defaults to server-rendered. Opt into build-time prerendering:
export const mode: "static" | "server" = "static";- static renders once at build time to HTML in .x/client/
- server renders per request via x start or x dev
Loaders
import type { RouteProps } from "@thexjs/core";export async function loader({ params }: { params: Record<string, string> }) { return { user: await getUser(params.id) };}export default function UserPage({ loaderData }: RouteProps) { const { user } = loaderData as { user: { name: string } }; return <p>{user.name}</p>;}Islands
Wrap interactive pieces in <Island> and register the component on the page or layout with export const islands. Only registered islands get a hydration bundle, so unregistered components never ship client JS. Full details on the Islands page.
import { Island } from "@thexjs/core";import { LikeButton } from "./like-button";export const islands = { LikeButton };export default function Page() { return ( <Island name="LikeButton" client="visible"> <LikeButton /> > </Island> );}client accepts "idle", "visible", or "load".
Typed routes
In dev, createApp writes src/x-routes.ts with a RouteMap type and a typed href() helper — routes can't drift from your file tree, and dynamic segments are checked at compile time. Do not edit the file by hand.
import { href } from "../x-routes";const url = href("/blog/[slug]", { slug: "hello-world" }); // "/blog/hello-world"Incremental static regeneration
Static pages can revalidate on a timer with export const revalidate = N, and you can bust the cache via POST /__x/revalidate. See the ISR page.
Client navigation & images
Plain <a> tags already get SPA-style navigation and hover prefetch on every page, with no router setup needed. <Link> is a typed convenience wrapper over the same behavior, and createImageProxyHandler streams allow-listed remote images through your own origin so a strict img-src 'self' CSP still works. Full details on the Client Navigation & Images page.
import { Link, createImageProxyHandler } from "@thexjs/core";<Link href="/docs" prefetch>Docs</Link>const imageProxy = createImageProxyHandler({ remoteHosts: ["cdn.example.com"] });Content collections
import { scanContent, renderMarkdown } from "@thexjs/core";const posts = scanContent("./src/content/blog");const html = renderMarkdown(posts[0].body);Data layer
import { connectSQLite, runSQLiteMigrations } from "@thexjs/core/data";const db = connectSQLite({ path: "./data/dev.db" });await runSQLiteMigrations(db, "./data/migrations");import { connectPostgres, runPostgresMigrations } from "@thexjs/core/data";const sql = connectPostgres({ url: process.env.DATABASE_URL! });await runPostgresMigrations(sql, "./data/migrations");Key exports
defineConfig, createApp, build App setup & buildrenderPage, renderStaticPage Lower-level renderingrenderStreamingPage Streaming SSR for SuspensescanRoutes, scanPages, scanApiDir Routing internalsscanLayouts, scanMiddleware, scanNotFound Layout/middleware/404 scanningfindLayoutChain, findMiddlewareChain Resolve chains from a routegenerateManifestSource, writeManifest Typed route map (src/x-routes.ts)Island, IslandProvider Selective hydrationLink, CLIENT_NAV_SCRIPT Client-side navigationDefaultNotFound, renderErrorOverlay 404 page & dev error overlaycreateImageProxyHandler Remote image proxy (/_x/image)scanContent, renderMarkdown, escapeHtml Markdown contentcomposeMiddleware, MiddlewareFn Route middlewareregisterServerFunctions, generateServerFunctionClient Server functionscreateRateLimiter, rateLimitMiddleware Rate limitingcreateRedisRateLimitStore Shared Redis rate-limit storecheckCsrf, verifyOrigin, verifyCsrfToken, generateCsrfToken, withCsrfCookie CSRFbuildSecurityHeaders, applySecurityHeaders Security response headersfindLeakedEnvKeys, assertNoEnvLeakage Build-time env isolationlogger, withRequestLogging Structured JSON loggingsetErrorReporter, reportException, combineReporters Error reportingcreateSentryReporter, createOtelReporter Sentry / OpenTelemetry adapterscreateHealthCheckHandler /healthz + /readyz probesconnectSQLite, connectPostgres Data layer (subpath @thexjs/core/data)runSQLiteMigrations, runPostgresMigrations File-based migrations