Page skeletons
componentBeginnerserverbrowserlive demo
Loading placeholders shaped like the home, product, cart, blog and account pages, shown while the next page loads its data.
import { PageSkeleton, ProductDetailSkeleton, BlogSkeleton, CartSkeleton, HomeSkeleton, CustomerPageSkeleton, CustomerLayoutSkeleton, PageSkeletonProps } from '@salla.sa/twilight-theme-engine/skeleton';In plain words
When a shopper clicks a link, the next page's data takes a moment to arrive. A skeleton fills that moment: grey shapes where the title, pictures and prices will appear, so the store feels quick instead of frozen.
The engine has one for each kind of page, and its generated routes already use them. You pick one when you write a route of your own.
Signature
const PageSkeleton: MemoExoticComponent<(props: PageSkeletonProps) => JSX.Element>
const ProductDetailSkeleton: MemoExoticComponent<() => JSX.Element>
const BlogSkeleton: MemoExoticComponent<() => JSX.Element>
const CartSkeleton: MemoExoticComponent<() => JSX.Element>
const HomeSkeleton: MemoExoticComponent<() => JSX.Element>
const CustomerPageSkeleton: MemoExoticComponent<() => JSX.Element>
const CustomerLayoutSkeleton: MemoExoticComponent<() => JSX.Element>
interface PageSkeletonProps {
children?: ReactNode; // rendered instead of the default skeleton
}Try it live
The loading placeholders the engine puts on its routes. Each is drawn from the real storefront classes, so it has the shape of the page it stands in for.Try this: switch to CartSkeleton, then open the real cart in Built-in pages and compare.
Storefront canvas · ar · RTL
Shown while loading: every route without its own (the router default).
Controls
What a theme writes
import { createFileRoute } from '@tanstack/react-router';
import { PageSkeleton } from '@salla.sa/twilight-theme-engine/skeleton';
import { loadMyPage, MyPage } from '../components/MyPage';
export const Route = createFileRoute('/{-$locale}/my-page')({
loader: () => loadMyPage(),
pendingComponent: () => <PageSkeleton />,
component: MyPage,
});
Example
app/routes/cart.tsx
import { createFileRoute } from '@tanstack/react-router';
import { Cart } from '@salla.sa/twilight-theme-engine/routes/cart';
import type { CartPageProps } from '@salla.sa/twilight-theme-engine/routes/cart';
import { CartSkeleton } from '@salla.sa/twilight-theme-engine/skeleton';
import { withHead } from '@salla.sa/twilight-theme-engine/tanstack';
export const Route = createFileRoute('/{-$locale}/cart')({
loader: ({ params }): Promise<CartPageProps> => Cart.loader({ locale: params.locale }),
head: withHead(Cart),
pendingComponent: () => <CartSkeleton />,
component: CartComponent,
});
function CartComponent() {
const data: CartPageProps = Route.useLoaderData();
return <Cart.Component {...data} />;
}
How it behaves
They come from
@salla.sa/twilight-components-react, exceptHomeSkeleton, which is the engine's own. All are eager and memoised, so they render at once with no<Suspense>: that is the point of a loading state.createRouter()showsPageSkeletonfor any route without its ownpendingComponent; itsdefaultPendingComponentoption replaces it, andfalseturns it off.Generated routes use
HomeSkeleton(home),ProductDetailSkeleton(product),BlogSkeleton(blog, article, author, category and tag pages),CartSkeleton(cart) andCustomerPageSkeleton(each account page). The account layout showsCustomerLayoutSkeleton, unless a component is registered underaccount:layout-pending.A pending component shows when a navigation's loader takes longer than
defaultPendingMs(100 ms), and then stays at leastdefaultPendingMinMs(200 ms).Their
s-skeleton-*classes are styled by Salla's Tailwind plugin (@salla.sa/twilight-tailwind-theme, loaded by the theme'stailwind.config), so each has the grid of the page it stands in for.HomeSkeletonuses plain Tailwind utilities.All but
CartSkeletonandHomeSkeletonare also exported from@salla.sa/twilight-theme-engine/components/common.
Gotchas
The example is the reference theme's
app/routes/cart.tsx, which the engine generates with// @auto-generatedas its first line. A file that starts with that line is rewritten on every dev and build, so apendingComponentyou change there is lost. Delete that first line to keep your version.PageSkeletonwithchildrenrenders only those children. It does not add them to the default skeleton.Without the Salla Tailwind plugin in the theme build, the
s-skeleton-*classes have no styles: no grey, no pulse and no size, so nothing visible appears while the page loads.