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

Routing

File-based routing

x uses the file system as your route table. Drop a file in src/pages/, get a route.

How it works

Every .tsx file in your pages directory becomes a route. The file path determines the URL pattern.

routing table
pages/index.tsx         -> /pages/about.tsx        -> /aboutpages/contact.tsx      -> /contactpages/blog/index.tsx   -> /blogpages/blog/[slug].tsx  -> /blog/:slugpages/dashboard/  settings.tsx         -> /dashboard/settings  profile.tsx          -> /dashboard/profilepages/_404.tsx          -> catch-all 404

Static routes

Simple files map to exact URL paths. pages/about.tsx becomes /about.

src/pages/about.tsx
export default function About() {  return <h1 className="text-3xl font-bold">About us</h1>;}

Dynamic segments

Wrap a filename in square brackets to create a dynamic segment. The value is available via the params object in loaders.

src/pages/blog/[slug].tsx
import type { RouteProps, LoaderArgs } from "@thexjs/core";export async function loader({ params }: LoaderArgs) {  const post = await getPost(params.slug);  return { title: post.title, content: post.content };}export default function BlogPost({ loaderData }: RouteProps) {  const { title, content } = loaderData as { title: string; content: string };  return (    <article>      <h1 className="text-3xl font-bold">{title}</h1>      <div>{content}</div>    </article>  );}

Multiple dynamic segments work too: pages/product/[category]/[id].tsx/product/:category/:id.

Catch-all routes

Prefix a dynamic segment with ... to match any number of remaining path segments. The full remaining path arrives as a single params value.

src/pages/docs/[...slug].tsx
import type { RouteProps, LoaderArgs } from "@thexjs/core";export async function loader({ params }: LoaderArgs) {  return { slug: params.slug };}export default function DocsPage({ loaderData }: RouteProps) {  const { slug } = loaderData as { slug: string };  return <div>Viewing docs for: {slug}</div>;}

/docs/routingparams.slug === "routing"; /docs/guides/overview"guides/overview". Catch-alls match one or more segments, so they don't capture the parent route itself.

Nested routes with folders

Organize routes in folders for nested URL structures. Each folder can have its own index.tsx.

file tree
pages/dashboard/
index.tsx -> /dashboard
settings.tsx -> /dashboard/settings
profile.tsx -> /dashboard/profile
billing/
index.tsx -> /dashboard/billing
history.tsx -> /dashboard/billing/history

Catch-all 404 page

Create pages/_404.tsx to show a custom not-found page for unmatched routes.

src/pages/_404.tsx
export default function NotFound() {  return (    <div className="text-center py-20">      <h1 className="text-6xl font-bold text-muted-foreground">404</h1>      <p className="mt-3 max-w-[56ch] text-[15px] leading-relaxed text-fg-muted">Page not found</p>      <a href="/docs" className="mt-6 inline-block text-primary hover:underline">        Go home      </a>    </div>  );}