Storefront Production Optimization Tips
In this guide, you’ll find tips useful when optimizing a storefront for production.
Summary#
When building a storefront, you want to ensure that it loads quickly and efficiently for your users. You can achieve this by implementing many optimizations, including:
- Implement appropriate rendering strategies.
- Use fetching libraries optimized for performance and caching like TanStack Query.
- Optimize queries to fetch only the necessary data.
- Implement optimistic UI updates for cart operations.
This guide explains how to implement these optimizations. You can follow it regardless of the frontend framework you use.
Choose the Right Rendering Strategy#
A rendering strategy defines how your frontend framework renders pages. The most common strategies are:
- Server-Side Rendering (SSR): Pages are rendered on the server for each request. Ideal for dynamic content that changes frequently.
- Static Site Generation (SSG): Pages are pre-rendered at build time. Best suited for content that doesn't change often.
- Incremental Static Regeneration (ISR): A hybrid approach where pages are pre-rendered at build time but can be updated at runtime. Perfect for content that changes occasionally.
- Client-Side Rendering (CSR): Pages are rendered in the browser using JavaScript. Optimal for highly interactive applications.
For your storefront, we recommend using different strategies for different pages:
Page Type | Recommended Strategy |
|---|---|
Homepage | SSG above the fold, CSR below the fold. For example, render the hero section at build time and load product recommendations client-side. |
Product Listing Page (PLP) | ISR or CSR. |
Product Detail Page (PDP) | SSG for product content that doesn't change often, such as descriptions and images. Use CSR for dynamic content like stock availability and prices. |
Cart and Checkout Pages | CSR. |
Blog or Content Pages (About Us, Privacy Policy, etc...) | SSG or ISR. |
User Account Pages | CSR. |
How you implement these strategies depends on the frontend framework you use. Refer to the documentation of your framework for guidance.
Use TanStack Query#
When fetching data from your Medusa backend, consider using a fetching library optimized for performance and caching. There are many options available, but one of the most popular is TanStack Query.
TanStack Query is a powerful data-fetching library that provides features like caching, background updates, and optimistic updates.
By using TanStack Query, you can significantly improve your storefront's performance by reducing the number of network requests and ensuring that your UI is always up-to-date.
Learn how to get started with TanStack Query in their official documentation. Their documentation also has guidance on advanced usage and best practices.
Stale Time Configuration#
When configuring TanStack Query, you can set the staleTime option to control how long data is considered fresh. However, avoid setting staleTime for highly dynamic data like product prices and stock levels.
Set the staleTime globally, then override it to 0 for specific queries that fetch dynamic data.
For example:
1// queryClient.ts2import { QueryClient } from "@tanstack/react-query"3 4const queryClient = new QueryClient({5 defaultOptions: {6 queries: {7 staleTime: 1000 * 60 * 5, // 5 minutes8 },9 },10})11 12export default queryClient13 14// product.ts15// Used in components that are children of QueryClientProvider16import { useQuery } from "@tanstack/react-query"17import { sdk } from "../lib/sdk"18 19export const useProduct = (id: string) => {20 return useQuery(["product", id], async () => {21 const response = await sdk.store.products.retrieve(id)22 return response.data23 })24}25 26export const useProductPrice = (id: string) => {27 return useQuery(28 ["product-price", id],29 async () => {30 const response = await sdk.store.products.retrieve(id, {31 fields: "*variants.calculated_price",32 })33 return response.data.variants[0].price34 },35 {36 staleTime: 0, // Always fetch fresh data37 }38 )39}
Invalidate Queries#
When performing mutations that change cached data, invalidate the relevant queries to ensure that the UI reflects the latest data.
For example, when adding an item to the cart, you should invalidate the cart query:
1import { useMutation, useQueryClient } from "@tanstack/react-query"2import { sdk } from "../lib/sdk"3 4export const useAddToCart = () => {5 const queryClient = useQueryClient()6 7 return useMutation(8 async (item) => {9 await sdk.store.cart.createLineItem("cart_id", item)10 },11 {12 onSuccess: () => {13 queryClient.invalidateQueries(["cart"])14 },15 }16 )17}
Optimize Fetched Data#
When fetching data from your Medusa backend, optimize your queries to fetch only the necessary data in the context of a component or page. This reduces the response size and time, improving your storefront's performance.
Medusa's API routes accept a fields parameter that allows you to specify which fields and relations to include in the response.
For example, if you only need the product's id, title, and variants.calculated_price, you can specify this in the fields parameter:
This query will return only the specified fields, reducing the response size and improving performance.
If you're using TanStack Query, make sure to include the fields parameter in the query key to ensure that different field selections are cached separately:
Learn more about the fields parameter in the Store API reference.
Implement Optimistic UI Updates#
When performing mutations that may take some time, you can implement optimistic UI updates to provide a better user experience. This means updating the UI immediately, assuming the mutation will succeed, then rolling back if it fails.
For example, if you're using TanStack Query, you can implement the following optimistic cart-update utilities and then use them in cart mutations:
The following code snippets are not complete implementations. They are simplified for clarity. They assume that:
- You defined separate cart data functions that use the JS SDK to call the Medusa backend.
- You have a
query-keys.tsutility that defines consistent query keys for TanStack Query.
You can make changes as needed based on your implementation.
With this setup, your cart mutations will provide immediate UI feedback with optimistic updates, while ensuring that the cart data is always fresh and consistent with the backend.