Remix loader system (loader, defer, Suspense)

FrontWing leverages Remix loaders for data fetching, enabling server-side rendering (SSR) and smooth client hydration. The architecture is built around performance-first SSR with progressive data loading.

Benefits

  • 🧠 SSR-first architecture for fast TTFB

  • πŸ’€ Lazy loading non-critical data (e.g. associations, reviews)

  • 🧼 Clean separation of server and client logic

  • πŸ”„ Built-in caching (browser/server)

Example:

export async function loader({ params }: LoaderFunctionArgs) {
  const product = await fetchProduct(params.slug);
  return json({ product });
}

This data is then accessed on the client with:

const { product } = useLoaderData<typeof loader>();

defer() and lazy loading with <Await>

For improved performance, FrontWing uses defer() and <Await> to load secondary data lazily after the initial HTML is rendered.

Use case: On the Product Page, we fetch primary product data synchronously, while associations, attributes, and reviews are loaded asynchronously in the background.

export async function loader({ params }: LoaderFunctionArgs) {
  return defer({
    product: await fetchProduct(params.slug),
    reviews: fetchProductReviews(params.slug), // no await
    associations: fetchAssociations(params.slug),
  });
}

On the client:

<Suspense fallback={<SkeletonReviews />}>
  <Await resolve={data.reviews}>
    {(reviews) => <ReviewList reviews={reviews} />}
  </Await>
</Suspense>

Where to look in code

  • routes/product.tsx – loader using defer() and multiple fetches

  • ProductPage.tsx – uses <Await> with <Suspense>

  • utils/api.server.ts – functions fetching Sylius data

  • components/product/ – components that render async sections

Last updated

Was this helpful?