SEO redirects
How the engine keeps deprecated and old URLs working, so customers and search rankings are not lost.
Framework: TanStack Start
This page is written for TanStack Start, the default and currently supported framework. See what depends on the framework.
Links outlive routes. Customers keep old bookmarks, emails and shared links, and search engines keep old URLs indexed. When a route is deprecated or renamed, a link that lands on a 404 loses that customer and the page's ranking. The engine keeps a few routes only to send those visitors to the current page, so nothing breaks when the storefront's URLs change.
Read this page to see which URLs are redirected, and to add or change a redirect in your theme.
Keep a redirect when you retire a URL
If your theme drops or renames a route, add a redirect from the old path instead of deleting it. See Change or add a redirect.
Redirect routes
The brand redirect and /logout are route files the engine generates into your theme's app/routes/. The engine serves the others from its own .twilight/ folder, so your theme has no file for them. Their loaders live in @salla.sa/twilight-theme-engine/routes/seo-redirects.
| From | To | How |
|---|---|---|
/$slug/brand-{$id} | /{locale}/brands/{id} | slugBrandRedirectLoader throws a redirect. |
/pending-orders | /{locale}/account/orders?status=pending | pendingOrdersRedirectLoader throws a redirect. See Pending orders. |
/account/pending_orders | the same | The same loader. The Salla user menu links here. |
/logout | the store home | The LogoutRedirect component. See Logout. |
/account/rating | /{locale}/account/orders | ratingRedirectLoader. The loyalty "rate your orders" link points here. |
/profile, /orders, /wishlist, /wallet, /notifications | the same name under /account | accountRedirectLoader throws a 301 and keeps the query string. These are the deprecated short forms, and old links from other storefronts keep working. |
/latest-products, /most-sales-products, /offers | /products?source=latest, sales, offers | productsSourceRedirectLoader throws a 301 and keeps the rest of the query string. These are Twig's listing addresses. See Products. |
{locale} is the language in the visited URL, or ar when the URL had none. The five account shortcuts and the three listing addresses keep a missing language missing instead. Each route also exists with a language prefix, like every storefront path.
The generated file for the brand redirect shows the pattern. The others are the same shape:
// @auto-generated
import { createFileRoute } from '@tanstack/react-router';
import { slugBrandRedirectLoader } from '@salla.sa/twilight-theme-engine/routes/seo-redirects';
export const Route = createFileRoute('/{-$locale}/$slug/brand-{$id}')({
loader: ({ params }) =>
slugBrandRedirectLoader({
params: { slug: params.slug, id: params.id, locale: params.locale },
}),
});Product short links
A short link such as /ar/lvXvzln matches no route, so the router shows the not-found page. Before showing a 404, that page tries the code as a product:
- The path must be one segment, or a language plus one segment, and the code must be 5 to 10 letters or digits and not a language code. Anything else is a normal 404.
- While the lookup runs, the page renders nothing, so the 404 does not flash.
- The code is sent to the products API as if it were a product id.
- On a hit, the browser navigates (replacing the history entry) to
/{locale}/{slug}/p{id}, with the slug taken from the product'surl. - On a miss or a failed request, the 404 shows.
This happens in the browser only. A crawler that does not run JavaScript sees the 404.
Change or add a redirect
The engine has no redirect table. A redirect is a route whose loader throws one.
- Change a target. Write your own loader with
redirectfrom@tanstack/react-routerin a file of the same name: take over the generated brand or logout file by deleting// @auto-generated, or, for a redirect the engine serves, add the file (for exampleapp/routes/offers.tsx) toapp/routes/without that line. - Add a redirect. Declare the old path in
app/routes.tsand throw from its loader:
import { route } from '@tanstack/virtual-file-routes';
export const routes = [route('/old-sale', 'old-sale.tsx')];import { createFileRoute, redirect } from '@tanstack/react-router';
export const Route = createFileRoute('/{-$locale}/old-sale')({
loader: ({ params }) => {
throw redirect({ to: `/${params.locale ?? 'ar'}/products`, search: { source: 'offers' }, replace: true });
},
});See Add a custom route for how route files and app/routes.ts fit together.
Edge cases
- Slugs are not canonicalised.
/wrong-slug/p123still renders the product, and/wrong-slug/brand-77still redirects. Canonical tags, not redirects, keep search engines on one URL. - Query strings are dropped by the brand redirect. The account and listing redirects keep them.
- Language rules still apply. The locale layout redirects a bare path to
/ar/...on multilingual stores, and removes the prefix on single-language stores.