Navigation and page detection
Navigate from code, read the current location and route id, and stop a loader with redirect, notFound or unauthorized.
Use these when a component needs to go somewhere, to know which page it is on, or when a loader needs to send the customer away or show a 404. For a plain link in markup, use Link.
All of them come from @salla.sa/twilight-theme-engine/providers.
Example
import { useIsCart, useLocation, useNavigate } from '@salla.sa/twilight-theme-engine/providers';
export function CartShortcut() {
const navigate = useNavigate();
const isCart = useIsCart();
const { pathname, search } = useLocation<{ q?: string }>();
if (isCart) return null;
return (
<button onClick={() => navigate('/cart')} data-from={pathname} data-query={search.q}>
Go to cart
</button>
);
}In components
| Hook | Returns | Notes |
|---|---|---|
useNavigate() | (to: string, opts?: { replace?: boolean }) => void | Navigates in the browser without a page reload. replace: true replaces the history entry instead of adding one. |
useRouteId() | string | The id of the current route, such as 'product.single'. Compare it with the RouteId constants. |
useLocation<TSearch>() | Location | Current URL. Works during server rendering, unlike window.location. |
Location has these fields:
| Field | Type | Description |
|---|---|---|
pathname | string | Path, such as /ar/products. |
search | TSearch | Query string already parsed into an object. |
searchStr | string | Query string as text, such as ?q=shoes. |
hash | string | The part after #. |
href | string | The full address. |
state | Record<string, unknown> | History state. |
Page checks
Each returns true on the matching page, and re-renders when the page changes.
| Hook | True on |
|---|---|
useIsHome() | The home page |
useIsProduct() | A product page (the detail page, not a product list) |
useIsCart() | The cart |
useIsCustomer() | Any customer account page |
useIsBlog() | The blog list and a blog article |
useIsBrands() | The brands list and a brand page |
useIsSearch() | Search results |
For any other page, compare useRouteId() with a constant:
import { RouteId, useRouteId } from '@salla.sa/twilight-theme-engine/providers';
const isWishlist = useRouteId() === RouteId.CUSTOMER_WISHLIST;In loaders
A loader runs before the page renders, outside React, so it cannot use hooks. Throw one of these instead. Each never returns.
import { notFound, orThrow, redirect, unauthorized } from '@salla.sa/twilight-theme-engine/providers';
if (!user) throw unauthorized();
if (moved) throw redirect('/ar/new-path', { statusCode: 301 });
const product = await orThrow(api.find(id)); // a 404 or 410 becomes notFound()| Function | Result |
|---|---|
notFound(message?) | The not-found page. |
unauthorized(message?) | Sends a signed-out customer to the login page, and back to this page after signing in. |
redirect(path, { replace?, statusCode? }) | A redirect. statusCode defaults to 307; use 301 for a URL that moved for good. |
orThrow(promise) | Awaits the promise. On a 404 or 410 response it calls notFound(); other errors pass through. |
orUnauthorized(promise) | Awaits the promise. On a 401 or 403 response it calls unauthorized(); other errors pass through. |
Gotchas
- Navigate with
useNavigateorLink, neverwindow.location. Assigningwindow.locationreloads the whole page, and reading it breaks on the server. useLocation().searchis an object, not text. UsesearchStrwhen you need the raw?a=b.- Route ids are strings like
product.single, not URL paths. UseRouteIdrather than typing them. - A product list is not a product page. A category and every
/products?source=…list areRouteId.PRODUCT_INDEX(product.index); search results areRouteId.SEARCH. The oldPRODUCT_INDEX_LATEST,PRODUCT_INDEX_SALESandPRODUCT_INDEX_OFFERSids are only thepage.slugof those lists now, so tell them apart withpage.slugorsource.type.
Next
Link: the markup version of navigation.- Loading data and Routes and pages show loaders in context.
- Salla SDK and events: the
route::changedevent for reacting to navigation.