Skip to content
Twilight React Playground
ثيم رائدaren

Add a page

Beginner8 min

Write a route file, then list it in app/routes.ts, to serve a page of your own at /ar/about and /en/about.

Goal: a page of your own, such as an About page, served at /ar/about and /en/about inside the store's header and footer.

Mechanism: a page is a route: a URL path paired with a file that says how to load and draw it. The engine generates the route files of its built-in pages for you. For a new page you write the file yourself in app/routes/, then list it in app/routes.ts. When the dev server or a build starts, the engine merges your list with its own pages and hands the result to TanStack Router, which adds the language segment in front.

1. Try it

The page's component, drawn here with the demo store's name, logo and description. The route file in step 3 only loads its title and renders it.

About us

ثيم رائد

<p>This demo store allows you to explore the look and design of stores on the Salla platform. Browse the categories, try the shopping experience, and preview the features as if you were a real custome

2. Write the page component

app/components/AboutPage.tsx
import { useStore } from '@salla.sa/twilight-theme-engine/hooks/useStore';

/** What the About page draws. Its route file (app/routes/about.tsx) hands it the title. */
export function AboutPage({ title }: { title: string }) {
  const { name, description, logo } = useStore();

  return (
    <main className="container py-8">
      <h1 className="mb-4 text-2xl font-bold">{title}</h1>
      <div className="flex items-center gap-4">
        {logo && <img src={logo} alt={name} width={64} height={64} className="rounded-md" />}
        <p className="leading-7">{description || name}</p>
      </div>
    </main>
  );
}

3. Write the route file, before listing it

app/routes/about.tsx
import { createFileRoute } from '@tanstack/react-router';
import {
  getTwilightContext,
  withHead,
  type TwilightContext,
} from '@salla.sa/twilight-theme-engine/tanstack';
import { AboutPage } from '../components/AboutPage';

interface AboutData {
  // Engine pages all return `page`: route::changed and the Salla SDK read it after a navigation.
  page: { slug: string; title: string };
}

// The same shape as an engine route module's head, so withHead() can turn it into tags.
const About = {
  head: (_ctx: TwilightContext, data: AboutData) => ({ title: data.page.title }),
};

// '/{-$locale}' + the path you list in app/routes.ts
export const Route = createFileRoute('/{-$locale}/about')({
  loader: (): AboutData => {
    const { locale } = getTwilightContext();
    return { page: { slug: 'about', title: locale.startsWith('ar') ? 'من نحن' : 'About us' } };
  },
  head: withHead(About),
  component: AboutRoute,
});

function AboutRoute() {
  const { page } = Route.useLoaderData();
  return <AboutPage title={page.title} />;
}
  • loader returns the page's data. It runs on the server for the first visit and in the browser on later navigations.
  • head sets the <title> from that data.
  • component reads the data with Route.useLoaderData() and draws the page.

4. List it in app/routes.ts, then restart the dev server

app/routes.ts
import { route } from '@tanstack/virtual-file-routes';

// A flat list of route(path, file). The file is looked up in app/routes/.
export const routes = [route('/about', 'about.tsx')];
In engine terms

twilightReact() reads app/routes.ts once, when Vite creates its plugins: it transpiles the file with esbuild and evaluates it in Node with a require rooted at the project (loadRoutesFile in src/vite/adapters/tanstack.adapter.ts). Each route(path, file) goes into a map keyed by path on top of DEFAULT_BASE_ROUTES, so a new path adds a page and an existing one replaces the engine's. buildVirtualRouteConfig nests every route under {-$locale} (paths starting with /account/ under the account layout) and passes the tree to TanStack Start as virtualRouteConfig. The locale wrapper redirects /about to /ar/about on a multilingual store and /ar/about to /about on a single-language one.

Traps

Go deeper: route modules, withHead, getTwilightContext, the lesson URLs, routes and pages, and Fork a built-in page to take over a page the engine already has.