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

app/components/layout/CartShortcut.tsx
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

HookReturnsNotes
useNavigate()(to: string, opts?: { replace?: boolean }) => voidNavigates in the browser without a page reload. replace: true replaces the history entry instead of adding one.
useRouteId()stringThe id of the current route, such as 'product.single'. Compare it with the RouteId constants.
useLocation<TSearch>()LocationCurrent URL. Works during server rendering, unlike window.location.

Location has these fields:

FieldTypeDescription
pathnamestringPath, such as /ar/products.
searchTSearchQuery string already parsed into an object.
searchStrstringQuery string as text, such as ?q=shoes.
hashstringThe part after #.
hrefstringThe full address.
stateRecord<string, unknown>History state.

Page checks

Each returns true on the matching page, and re-renders when the page changes.

HookTrue 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.

app/routes/my-page.tsx
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()
FunctionResult
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 useNavigate or Link, never window.location. Assigning window.location reloads the whole page, and reading it breaks on the server.
  • useLocation().search is an object, not text. Use searchStr when you need the raw ?a=b.
  • Route ids are strings like product.single, not URL paths. Use RouteId rather than typing them.
  • A product list is not a product page. A category and every /products?source=… list are RouteId.PRODUCT_INDEX (product.index); search results are RouteId.SEARCH. The old PRODUCT_INDEX_LATEST, PRODUCT_INDEX_SALES and PRODUCT_INDEX_OFFERS ids are only the page.slug of those lists now, so tell them apart with page.slug or source.type.

Next

On this page