newX 1.3 — islands to disk, server-mode islands, image proxy

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.

terminal
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

x.config.ts
import { defineConfig } from "@thexjs/core";export default defineConfig({  pagesDir: "./src/pages",  contentDir: "./src/content",  port: 3000,});
src/pages/index.tsx
import type { RouteProps } from "@thexjs/core";export const mode = "static";export default function HomePage({}: RouteProps) {  return <h1>Hello from x</h1>;}

File-based routing

route mapping
File                          Routesrc/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 page

Files and directories prefixed with _ or . are never treated as routes.

Route modes

Every page defaults to server-rendered. Opt into build-time prerendering:

src/pages/index.tsx
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

loader example
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.

page with an island
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.

typed href
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.

link + image proxy

Content collections

markdown
import { scanContent, renderMarkdown } from "@thexjs/core";const posts = scanContent("./src/content/blog");const html = renderMarkdown(posts[0].body);

Data layer

sqlite
import { connectSQLite, runSQLiteMigrations } from "@thexjs/core/data";const db = connectSQLite({ path: "./data/dev.db" });await runSQLiteMigrations(db, "./data/migrations");
postgres
import { connectPostgres, runPostgresMigrations } from "@thexjs/core/data";const sql = connectPostgres({ url: process.env.DATABASE_URL! });await runPostgresMigrations(sql, "./data/migrations");

Key exports

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