Region React Context in Storefront

In this guide, you'll learn how to create a region context in your React storefront.

Why Create a Region Context?#

Throughout your storefront, you'll need to access the customer's selected region to perform different actions, such as retrieve product's prices in the selected region.

Tip: To learn how to allow customers to select their region, refer to the Store Selected Region in Storefront guide.

So, if your storefront is React-based, create a region context and add it at the top of your components tree. Then, you can access the selected region anywhere in your storefront.


Create Region Context Provider#

For example, create the following file that exports a RegionProvider component and a useRegion hook:

Tip: Learn how to install and configure the JS SDK in the JS SDK documentation.
Code
1"use client" // include with Next.js 13+2
3import { 4  createContext, 5  useContext, 6  useEffect, 7  useState,8} from "react"9import { HttpTypes } from "@medusajs/types"10import { sdk } from "@/lib/sdk"11
12type RegionContextType = {13  region?: HttpTypes.StoreRegion14  setRegion: React.Dispatch<15    React.SetStateAction<HttpTypes.StoreRegion | undefined>16  >17}18
19const RegionContext = createContext<RegionContextType | null>(null)20
21type RegionProviderProps = {22  children: React.ReactNode23}24
25export const RegionProvider = ({ children }: RegionProviderProps) => {26  const [region, setRegion] = useState<27    HttpTypes.StoreRegion28  >()29
30  useEffect(() => {31    if (region) {32      // set its ID in the local storage in33      // case it changed34      localStorage.setItem("region_id", region.id)35      return36    }37
38    const regionId = localStorage.getItem("region_id")39    if (!regionId) {40      // retrieve regions and select the first one41      sdk.store.region.list()42      .then(({ regions }) => {43        setRegion(regions[0])44      })45    } else {46      // retrieve selected region47      sdk.store.region.retrieve(regionId)48      .then(({ region: dataRegion }) => {49        setRegion(dataRegion)50      })51    }52  }, [region])53
54  return (55    <RegionContext.Provider value={{56      region,57      setRegion,58    }}>59      {children}60    </RegionContext.Provider>61  )62}63
64export const useRegion = () => {65  const context = useContext(RegionContext)66
67  if (!context) {68    throw new Error("useRegion must be used within a RegionProvider")69  }70
71  return context72}

The RegionProvider handles retrieving the selected region from the Medusa application, and updating its ID in the localStorage.

The useRegion hook returns the value of the RegionContext. Child components of RegionProvider use this hook to access region or setRegion.


Use RegionProvider in Component Tree#

To use the region context's value, add the RegionProvider high in your component tree.

For example, if you're using Next.js, add it to the app/layout.tsx or src/app/layout.tsx file:

app/layout.tsx
13
14export default function RootLayout({15  children,16}: Readonly<{17  children: React.ReactNode;18}>) {19  return (20    <html lang="en">21      <body className={inter.className}>22        <RegionProvider>23          {/* Other providers... */}24          {children}25        </RegionProvider>26      </body>27    </html>28  )29}

Use useRegion Hook#

Now, you can use the useRegion hook in child components of RegionProvider.

For example:

Code
1"use client" // include with Next.js 13+2// ...3import { useRegion } from "@/providers/region"4
5export default function Products() {6  const { region } = useRegion()7  // ...8}

Managing Regions in Server Components#

For SEO and performance reasons, many components in your storefront may be server components that don't have access to client-side context. Here are patterns for managing region data in server components:

URL-Based Region Selection#

Store the selected region in the URL path or query parameters to make it available to server components:

app/[countryCode]/products/page.tsx
1import { sdk } from "@/lib/sdk"2import { cache } from "react"3
4// Server-side region retrieval5const getRegionByCountryCode = cache(async (countryCode: string) => {6  const { regions } = await sdk.store.region.list({7    fields: "*countries",8  })9  10  return regions.find((region) => 11    region.countries?.some((country) => 12      country.iso_2.toLowerCase() === countryCode.toLowerCase()13    )14  )15})16
17type Props = {18  params: { countryCode: string }19}20
21export default async function ProductsPage({ params }: Props) {22  const region = await getRegionByCountryCode(params.countryCode)23  24  if (!region) {25    // Handle region not found26    return <div>Region not found</div>27  }28  29  // Use region to fetch region-specific data30  const { products } = await sdk.store.product.list({31    region_id: region.id,32  })33  34  return (35    <div>36      <h1>Products for {region.name}</h1>37      {/* Server-rendered product list */}38    </div>39  )40}

Store region preferences in cookies to access them in server components:

lib/region-server.ts
1import { cookies } from "next/headers"2import { sdk } from "@/lib/sdk"3
4export async function getServerRegion() {5  const cookieStore = cookies()6  const regionId = cookieStore.get("region_id")?.value7  8  if (!regionId) {9    // Return default region10    const { regions } = await sdk.store.region.list({11      limit: 1,12    })13    return regions[0]14  }15  16  try {17    const { region } = await sdk.store.region.retrieve(regionId)18    return region19  } catch {20    // Fallback to default if region not found21    const { regions } = await sdk.store.region.list({22      limit: 1,23    })24    return regions[0]25  }26}

Then use it in server components:

components/ServerProductList.tsx
1import { getServerRegion } from "@/lib/region-server"2import { sdk } from "@/lib/sdk"3
4export default async function ServerProductList() {5  const region = await getServerRegion()6  7  const { products } = await sdk.store.product.list({8    region_id: region.id,9  })10  11  return (12    <div>13      {/* Server-rendered products with region-specific pricing */}14      {products.map((product) => (15        <div key={product.id}>16          <h3>{product.title}</h3>17          {/* Display prices in region currency */}18        </div>19      ))}20    </div>21  )22}

Hybrid Approach#

Combine server and client components for optimal performance and user experience:

app/products/page.tsx
1import { Suspense } from "react"2import ServerProductList from "@/components/ServerProductList"3import ClientRegionSelector from "@/components/ClientRegionSelector"4
5export default function ProductsPage() {6  return (7    <div>8      {/* Client component for region selection */}9      <ClientRegionSelector />10      11      {/* Server component for initial product rendering */}12      <Suspense fallback={<div>Loading products...</div>}>13        <ServerProductList />14      </Suspense>15    </div>16  )17}

This approach provides server-side rendering for SEO while maintaining client-side interactivity for region switching.

Was this page helpful?
Ask Bloom
For assistance in your development, use Claude Code Plugins or Medusa MCP server in Cursor, VSCode, etc...FAQ
What is Medusa?
How can I create a module?
How can I create a data model?
How do I create a workflow?
How can I extend a data model in the Product Module?
Recipes
How do I build a marketplace with Medusa?
How do I build digital products with Medusa?
How do I build subscription-based purchases with Medusa?
What other recipes are available in the Medusa documentation?
Chat is cleared on refresh
Line break