Medusa is an open source composable ecommerce platform that allows developers to create their own customizable and extendable online store. Medusa aims to provide developers with a great experience creating unique ecommerce stores.
Combining Medusa and Strapi allows you to create a powerful ecommerce store. Strapi is an open source headless CMS platform that is fully customizable.
With Medusa, you can perform ecommerce functionalities while using Strapi to control the content displayed on your store.
To top it off, with Remix you can create awesome and fast UI. Remix is an open source react framework focused on web standards and modern web app UX. You can also easily fetch data from Medusa and Strapi to your storefront.
In this tutorial, you will be building an ecommerce storefront with Medusa, Strapi, and Remix.
You can find the source code for this article in this repository.
Prerequisites
- Node v14 or above
- Yarn is recommended, but you can also follow along with npm.
- Redis
- Medusa CLI: To install the CLI, run
.Copy to clipboardyarn global add @medusajs/medusa-cli
Set Up Strapi
Install the Template
1npx create-strapi-app strapi-medusa --template shahednasser/strapi-medusa-template
This creates a folder named
in your project. Once the installation is complete, the Strapi development server will start on portCopy to clipboardstrapi-medusa
. A new page will also open in your default browser to create a new admin user and log in. After you have logged in, you can access your Strapi Dashboard.Copy to clipboardlocalhost:1337
Change Authorization Settings for the User
Your Medusa sever will require the credentials of a Strapi User in order to seed Strapi with mock data. To create a new user, go to Content Manager, then choose User under Collection Types.
Click on the Create new entry button at the top right. This opens a new form to enter the user’s details.
Enter the user’s username, email, and password. Once you’re done, click on the Save button at the top right.
Next, go to Settings → Roles → Authenticated and select all the permissions, and hit save.
Set up Medusa
To initiate your Medusa server, run the following command:
1medusa new medusa-server --seed
The
flag creates an SQLite database and seeds it with some demo data.Copy to clipboard--seed
Change to the
directory and go toCopy to clipboardmedusa-server
. Change the exported object at the end to enable Redis:Copy to clipboardmedusa.config.js
1234567module.exports = {projectConfig: {redis_url: REDIS_URL,//...}//...};
The default Redis connection string is
but if you have made changes to it, go to theCopy to clipboardredis://localhost:6379
file and add the following:Copy to clipboard.env
1REDIS_URL=<YOUR_REDIS_URL>
Where
is your connection string.Copy to clipboard<YOUR_REDIS_URL>
Additionally, since the Remix storefront runs on
, you have to add an environment variableCopy to clipboardlocalhost:3000
that sets the URL of the storefront.Copy to clipboardSTORE_CORS
Add the following in
:Copy to clipboard.env
1STORE_CORS=http://localhost:3000
Install Strapi Plugin
To install the Strapi plugin, run the following command in your Medusa server’s directory:
1yarn add medusa-plugin-strapi
Then, add the following environment variables:
12345STRAPI_USER=<STRAPI_IDENTIFIER>STRAPI_PASSWORD=<STRAPI_PASSWORD>STRAPI_PROTOCOL=httpSTRAPI_URL=<STRAPI_URL> # OptionalSTRAPI_PORT=<STRAPI_PORT> # Optional
Where:
is either the email address or username of the user you created in the previous step.Copy to clipboard<STRAPI_IDENTIFIER>
is the password of the user you created in the previous step.Copy to clipboard<STRAPI_PASSWORD>
is the protocol of your Strapi server. Since, you’re using a local Strapi server, set this toCopy to clipboard<STRAPI_PROTOCOL>
. The default value isCopy to clipboardhttp
.Copy to clipboardhttps
is the URL of your Strapi server. By default, the URL isCopy to clipboard<STRAPI_URL>
.Copy to clipboardlocalhost
is the port the Strapi server runs on. By default, the port isCopy to clipboard<STRAPI_PORT>
.Copy to clipboard1337
Finally, open
and add the following new item to theCopy to clipboardmedusa-config.js
array:Copy to clipboardplugins
12345678910111213const plugins = [//...{resolve: `medusa-plugin-strapi`,options: {strapi_medusa_user: process.env.STRAPI_USER,strapi_medusa_password: process.env.STRAPI_PASSWORD,strapi_url: process.env.STRAPI_URL, //optionalstrapi_port: process.env.STRAPI_PORT, //optionalstrapi_protocol: process.env.STRAPI_PROTOCOL //optional}}];
Test Integration
Make sure the Strapi server is still running. If not, you can run the following command to run the Strapi server in the directory of the Strapi project:
1yarn develop
Make sure your Redis server is up and running as well.
Then, in the directory of your Medusa server, run the following command to start the Medusa server:
1yarn start
This will start your Medusa server on
. You’ll see thatCopy to clipboardlocalhost:9000
events have been triggered along with similar events.Copy to clipboardproduct.created
This will update Strapi with the demo products you seeded.
Add CMS Pages in Strapi
You will now use Strapi to manage content on your storefront’s homepage. You will be able to control three things from Strapi after this implementation: the hero text that will appear at the top of the storefront; the subheading below the hero text; and the list of products shown on the homepage.
On your Strapi dashboard, go to Content-Type Builder under Plugins in your Strapi Dashboard. This is where you can define the model/schema for your content.
Click on “Create new single type” under “Single Types”.
Enter the display name as “Home Page” (if you have used another, you will have to use the appropriate API ID for it later) and hit continue.
Next, select the component field and give it the display name “Hero Text”, and a category homepage (click create “homepage” under the category). Then, click on configure the component.
Then give it the name
in the next step and click Finish.Copy to clipboardhero_text
Go to the Hero Text component under Homepage in components and create three text fields named
,Copy to clipboardstart_text
andCopy to clipboardmid_text
.Copy to clipboardend_text
Here, the three text fields have been added because later on in the article you will give a special underline to the
to highlight it.Copy to clipboardmid_text
Go back to the Home Page type under single types and add a relation field to products. The relation should be “homepage has many products”. Give it a field name
.Copy to clipboardproducts_list
Finally, add a text field
. Save your changes in the homepage content type.Copy to clipboardheading_2
This is what your homepage content type should look like:
Next, go to Settings → Users & Permissions Plugin → Roles → Public, and enable find permission for the homepage and product type. Hit save.
Now, go to the content manager and under the Home Page add your hero text and the products you wish to display under the relations section to the right. Hit save and then publish.
Set up the Remix Storefront
In this section, you’ll set up the ecommerce storefront with Remix.
Remix has three official pre-built templates for you to use depending on your needs, but you can also start with a basic one or create your own.
Set up Remix
To setup a Remix app (do this in a separate directory from
andCopy to clipboardmedusa-server
), run the following command:Copy to clipboardstrapi-medusa
1npx create-remix@latest my-storefront
It will ask you a few questions. Choose
, then choose your preferred hosting platform (you can choose Remix App Server if you are unsure), choose typescript, and no forCopy to clipboardJust the basics
if you wish to useCopy to clipboardnpm install
.Copy to clipboardyarn
Then, change to the
directory and install dependencies with yarn:Copy to clipboardmy-storefront
12cd my-storefrontyarn install
Configure Tailwind CSS
Install Tailwind CSS to design the UI element:
1yarn add -D tailwindcss postcss autoprefixer concurrently
Run
to create yourCopy to clipboardnpx tailwindcss init
file. Then, set its content to the following:Copy to clipboardtailwind.config.js
12345678910/** @type {import('tailwindcss').Config} */module.exports = {content: ["./app/**/*.{js,ts,jsx,tsx}",],theme: {extend: {},},plugins: [],}
Also, change the scripts in your package.json:
12345678{"scripts": {"build": "npm run build:css && remix build","build:css": "tailwindcss -m -i ./styles/app.css -o app/styles/app.css","dev": "concurrently \"npm run dev:css\" \"remix dev\"","dev:css": "tailwindcss -w -i ./styles/app.css -o app/styles/app.css"}}
Then, create the file
with the following content:Copy to clipboardstyles/app.css
123@tailwind base;@tailwind components;@tailwind utilities;
Lastly, add this to your
after the list of imports:Copy to clipboardapp/root.tsx
12345import styles from "./styles/app.css"export function links() {return [{ rel: "stylesheet", href: styles }]}
You can now use Tailwind CSS in your app.
Connect Storefront to Medusa Server
Once this is done let’s connect your storefront to your Medusa server.
First, you need to install a few packages with the following command:
1yarn add medusa-react react-query @medusajs/medusa
The
library uses react-query as a solution for server-side state management and lists the library as a peer dependency.Copy to clipboardmedusa-react
In order to use the hooks exposed by
, you will need to include theCopy to clipboardmedusa-react
somewhere up in your component tree. TheCopy to clipboardMedusaProvider
takes aCopy to clipboardMedusaProvider
prop which should point to your Medusa server. Under the hood,Copy to clipboardbaseUrl
uses theCopy to clipboardmedusa-react
client library (built on top ofCopy to clipboardmedusa-js
) to interact with your server.Copy to clipboardaxios
In addition, because medusa-react is built on top of react-query, you can pass an object representing react-query's QueryClientProvider props, which
will pass along.Copy to clipboardMedusaProvider
You also need to wrap your app in a
since that will let you use the cart functionalities provided by Medusa, which you will do later.Copy to clipboardCartProvider
Create a file
. This file will contain yourCopy to clipboardapp/lib/config.ts
which will let you use Medusa’s Javascript client in your app.Copy to clipboardmedusaClient
1234567891011121314151617181920import Medusa from '@medusajs/medusa-js';import { QueryClient } from 'react-query';const MEDUSA_BACKEND_URL = 'http://localhost:9000';const STRAPI_API_URL = 'http://127.0.0.1:1337/api';const queryClient = new QueryClient({defaultOptions: {queries: {refetchOnWindowFocus: false,staleTime: 1000 * 60 * 60 * 24,retry: 1,},},});const medusaClient = new Medusa({ baseUrl: MEDUSA_BACKEND_URL, maxRetries: 3 });export { MEDUSA_BACKEND_URL, STRAPI_API_URL, queryClient, medusaClient };
Now go to your
and import the required packages:Copy to clipboardapp/root.tsx
12import { MedusaProvider, CartProvider } from 'medusa-react';import { MEDUSA_BACKEND_URL, queryClient } from './lib/config';
You can also edit the
here to change your metadataCopy to clipboardmeta
12345export const meta: MetaFunction = () => ({charset: 'utf-8',title: 'New Remix App',viewport: 'width=device-width,initial-scale=1',});
Below this, you will see the
component. In the returned JSX add theCopy to clipboardApp
andCopy to clipboardMedusaProvider
with some base styles to theCopy to clipboardCartProvider
:Copy to clipboardbody
123456789101112131415161718192021return (<html lang="en"><head><Meta /><Links /></head><body className="bg-black text-slate-400 overflow-x-hidden justify-center flex"><MedusaProviderqueryClientProviderProps={{ client: queryClient }}baseUrl={MEDUSA_BACKEND_URL}><CartProvider><Outlet /><ScrollRestoration /><Scripts /><LiveReload /></CartProvider></MedusaProvider></body></html>);
Display Home Page from Strapi
The data for your home page is available on the Strapi endpoint:
(addCopy to clipboardlocalhost:1337/api/home-page
to also show the nested products). It returns an object with theCopy to clipboard?populate=*
&Copy to clipboarddata
arrays. You don't have to care about theCopy to clipboardmeta
, what you should really care about isCopy to clipboardmeta
, it contains all the content you entered in your Strapi Dashboard.Copy to clipboarddata
First, create the file
with the following content:Copy to clipboardapp/types/StrapiResponse.ts
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748// StrapiResponse.tsexport type StrapiResponseType = {data: {id: number;attributes: {createdAt: Date;updatedAt: Date;publishedAt: Date;hero_text: {id: number;start_text: string;mid_text: string;end_text: string;};products_list: {data: Array<StrapiProductData>;};heading_2: string;};};meta: {};};export type StrapiProductData = {id: number;attributes: {medusa_id: string;title: string;subtitle: string | null;description: string;handle: string;is_giftcard: boolean;status: 'draft' | 'proposed' | 'published' | 'rejected';thumbnail: string;discountable: boolean;weight: number;product_length: null;width: null;height: null;hs_code: null;origin_country: null;mid_code: null;material: string | null;createdAt: Date;updatedAt: Date;};};
This is the format in which your data is returned from the Strapi API.
Next, create a utility function to fetch your content from the Strapi API. Create a file
with the following content:Copy to clipboardapp/models/home.server.ts
1234567891011121314151617181920212223242526272829303132333435// home.server.tsimport { STRAPI_API_URL } from "~/lib/config";import type {StrapiProductData,StrapiResponseType,} from "~/types/StrapiResponse";export const getHomePageData = async () => {const homePage: StrapiResponseType = await (await fetch(`${STRAPI_API_URL}/home-page?populate=*`)).json();const { data } = homePage;const { attributes } = data;const heroText = attributes.hero_text;const products = attributes.products_list.data;const smallHeading = attributes.heading_2;const homePageData = { heroText, products, smallHeading };return homePageData;};export type homePageDataType = {heroText: {id: number;start_text: string;mid_text: string;end_text: string;};products: StrapiProductData[];smallHeading: string;};
In the
function, you should only return the data you need on your home page.Copy to clipboardgetHomePageData
In the above code sample, you will notice that in the import statement
is used, this is because it is the alias set for theCopy to clipboard~
directory set in theCopy to clipboardapp
by default in Remix, if you wish you can change it at your convenience.Copy to clipboardtsconfig
All files inside the
directory will be a route. For example,Copy to clipboardapp/routes
will contain theCopy to clipboardapp/routes/store.tsx
route.Copy to clipboard/store
Next, go to
and create a loader function:Copy to clipboardapp/routes/index.tsx
1234567import { getHomePageData, homePageDataType } from '~/models/home.server';export const loader = async () => {const homePageData = await getHomePageData();return homePageData;};
To use the response you received from the loader function you will use the
hook from Remix inside theCopy to clipboarduseLoaderData
component:Copy to clipboardIndex
12345678import { useLoaderData } from '@remix-run/react';export default function Index() {const { heroText, products, smallHeading } =useLoaderData<homePageDataType>();...}
Here,
was destructured and brought in usingCopy to clipboardhomePageData
, now you can use it on your page.Copy to clipboarduseLoaderData
Then, change the returned JSX to the following:
1234567891011121314151617181920212223export default function Index() {//...return (<div className="px-10 sm:px-20 md:px-44 pt-44 max-w-[100rem] flex-grow w-screen">{/* Hero Section */}<div><h1 className="text-[2.5rem] sm:text-5xl lg:text-6xl xl:text-8xl relative font-medium lg:leading-[1.15] xl:leading-[1.2]">{heroText.start_text}{' '}{heroText.mid_text.split(' ').map((text) => (<span key={text} className="text-gray-50"><span className="relative">{text}<div className="h-1 bg-emerald-200 w-full absolute bottom-0 left-0 inline-block" /></span>{' '}</span>))}{heroText.end_text}</h1></div></div>);}
brings data from theCopy to clipboardheroText.start_text
text field under theCopy to clipboardstart_text
component you made in Strapi. Similarly,Copy to clipboardhero_text
andCopy to clipboardheroText.mid_text
are fromCopy to clipboardheroText.end_text
andCopy to clipboardmid_text
fields from Strapi respectively.Copy to clipboardend_text
Then
has been split so that each word gets a uniform underline in case there are multiple words, you will see it happen a bit later in the homepage UI.Copy to clipboardmid_text
To display your products, create the file
with the following content:Copy to clipboardapp/components/productCard.tsx
123456789101112131415161718import { Link } from '@remix-run/react';interface ProductCardType {image: string;title: string;handle: string;}export default function ProductCard({ image, title, handle }: ProductCardType) {return (<Link to={`/products/${handle}`}><div className="flex flex-col space-y-1 p-2 hover:bg-slate-400 hover:bg-opacity-25 cursor-pointer active:scale-95 transition ease-in-out duration-75"><img src={image} alt="" /><h3 className="pt-2 text-white text-xl">{title}</h3></div></Link>);}
The
comes from Remix and will help you redirect to the products page. The handle prop which is available in Medusa products will be used as a slug.Copy to clipboardLink
Now getting back to your
, you will map your Strapi response (products) to the page.Copy to clipboardapp/routes/index.tsx
Do this just below your hero section:
1234567891011121314151617181920212223242526import ProductCard from '~/components/productCard';export default function Index() {...return (<div className="px-10 sm:px-20 md:px-44 pt-44 max-w-[100rem] flex-grow w-screen">...<div className="flex flex-col items-center pt-40 pb-44"><h2 className="text-2xl sm:text-3xl lg:text-4xl pb-10 text-white">{smallHeading}</h2><div className="grid grid-cols-2 xl:grid-cols-4 gap-x-6">{products.map(({ attributes }) => (<ProductCardkey={attributes.medusa_id}image={attributes.thumbnail}handle={attributes.handle}title={attributes.title}/>))}</div></div></div>)
Test Homepage
To test out your homepage, start your Remix development server with
(make sure that your Medusa and Strapi servers are already running).Copy to clipboardyarn dev
Your app is ready at
and it will look like the following:Copy to clipboardlocalhost:3000
Implement Add to Cart Functionality with Medusa
To add your products to the cart, you first need to associate a cart with the customer. To do this, you can create a wrapper around your app that checks if a cart has already been initialized or need to be created, and does the needful.
Create the file
with the following content:Copy to clipboardapp/components/outletContainer.tsx
1234567891011121314151617181920212223242526import { useCart } from 'medusa-react';import { ReactNode, useEffect } from 'react';import { medusaClient } from '~/lib/config';interface OutletContainerType {children: ReactNode;}export default function OutletContainer({ children }: OutletContainerType) {const { setCart } = useCart();useEffect(() => {const localCartId = localStorage.getItem('cart_id');localCartId? medusaClient.carts.retrieve(localCartId).then(({ cart }) => {setCart(cart);}): medusaClient.carts.create().then(({ cart }) => {localStorage.setItem('cart_id', cart.id);setCart(cart);});}, []);return <div>{children}</div>;}
You are using
'sCopy to clipboardmedusa-react
hook,Copy to clipboarduseCart
will set your cart globally. You can then use it anywhere in your app. TheCopy to clipboardsetCart
will also save cart localStorage so that the added items persist even when the user returns.Copy to clipboardoutletContainer
You will also need to show toast notifications when a product is added to the cart. Install
to do this:Copy to clipboardreact-hot-toast
1yarn add react-hot-toast
Now, go back to your
and wrap yourCopy to clipboardapp/root.tsx
withCopy to clipboard<Outlet />
. Also, addCopy to clipboardOutletContainer
fromCopy to clipboard<Toaster />
that will let you show notifications:Copy to clipboardreact-hot-toast
12345678910111213141516import OutletContainer from './components/outletContainer';import { Toaster } from 'react-hot-toast';export default function App() {return (...<CartProvider><OutletContainer><Outlet /></OutletContainer>...<Toaster /></CartProvider>...);}
Create Product Page
In this section, you’ll create a product page. When you are deploying to production, you can’t make a separate page for each of your products, so you will create a dynamic page that will run according to your product’s
. In Remix you will name your dynamic pages asCopy to clipboardhandle
.Copy to clipboard$slug.tsx
You will need to get the
from the URL of your page, you can do that with a loader function but it’s much simpler to useCopy to clipboardhandle
hook.Copy to clipboarduseParams
Create the file
with the following content:Copy to clipboardapp/routes/products/$slug.tsx
123456import { useParams } from '@remix-run/react';import { useCart, useCreateLineItem, useProducts } from 'medusa-react';export default function ProductSlug() {const { slug } = useParams();}
is getting your page’s slug from your URL, for example, inCopy to clipboardslug
the slug isCopy to clipboardlocalhost:3000/products/sweatshirt
(remember you passed in theCopy to clipboardsweatshirt
in yourCopy to clipboardhandle
component).Copy to clipboardProductCard
Next, fetch your product from Medusa using the
hook and add it to the UI:Copy to clipboarduseProducts
1234567891011121314151617181920212223242526export default function ProductSlug() {...const { products } = useProducts({handle: slug,},{});if (!products) {return <div></div>; // you can use skeleton loader here instead.}const product = products[0];return (<div className="flex flex-col items-center lg:justify-between lg:flex-row px-10 sm:px-20 md:px-44 pt-44 max-w-[100rem] flex-grow w-screen"><img src={product.thumbnail!} className="h-96 w-auto" /><div><h1 className="text-4xl pb-10 text-white">{product.title}</h1><p className="w-72">{product.description}</p></div></div>);}
Here, the
hook was used and passed the slug. While the product is being loaded you show an empty div (you can use a skeleton loader instead).Copy to clipboarduseProducts
Finally, you use the first item returned by the
hook which is the product that has the handle in the page’s URL.Copy to clipboarduseProducts
Please notice that the title and description are used here from the Medusa server since the Strapi plugin supports two-way sync. So, whenever you make changes to the products in Strapi, they’re reflected on the Medusa server as well. You can alternatively show the CMS data for the product from Strapi instead.
You also need to show the prices for your customers according to their region. To do this, create the file
:Copy to clipboardapp/lib/formatPrice.ts
1234567891011import { formatVariantPrice } from 'medusa-react';import type { Cart } from "medusa-react/dist/types";import type { ProductVariant } from '@medusajs/medusa';export const formatPrice = (variant: ProductVariant, cart: Cart) => {if (cart)return formatVariantPrice({variant: variant,region: cart.region,});};
You use the
function here fromCopy to clipboardformatVariantPrice
. This formats the price according to your user’s region and the product variant selected.Copy to clipboardmedusa-react
Then, use it in
:Copy to clipboardapp/routes/products/$slug.tsx
12345678910111213141516171819202122import { formatPrice } from '~/lib/formatPrice';export default ProductSlug() {...const { cart } = useCart();return (<div className="flex flex-col items-center lg:justify-between lg:flex-row px-10 pb-44 sm:px-20 md:px-44 pt-44 max-w-[100rem] flex-grow w-screen"><img src={product.thumbnail!} className="h-96 w-auto" /><div><h1 className="text-4xl pt-5 lg:pt-0 pb-5 lg:pb-10 text-white">{product.title}</h1><p className="w-72">{product.description}</p><p className="text-xl text-white pt-5">{formatPrice(product.variants[0], cart)}</p></div></div>)}
Next, create a function to add to the cart and push notifications.
1234567891011121314151617181920212223import toast from 'react-hot-toast';export default function ProductSlug() {...const { mutate } = useCreateLineItem(cart?.id!);const addItem = () => {mutate({variant_id: products?.slice(0, 1)[0].variants[0].id!,quantity: 1,},{onSuccess: () => {toast('Added to Cart!');},});};...}
The
hook lets you add items. It requires a cart ID. TheCopy to clipboarduseCreateLineItem
function will add the product to the cart and then show a toast notification.Copy to clipboardaddItem
Add the button that will run this function on click in the returned JSX:
12345678910111213141516171819202122export default function ProductSlug() {...return (<div className="flex flex-col items-center lg:justify-between lg:flex-row px-10 sm:px-20 md:px-44 pt-44 max-w-[100rem] flex-grow w-screen"><img src={product.thumbnail!} className="h-96 w-auto" /><div><h1 className="text-4xl pb-10 text-white">{product.title}</h1><p className="w-72">{product.description}</p><p className="text-xl text-white pt-5">{formatPrice(product.variants[0])}</p><buttonclassName="p-5 rounded-md w-full bg-slate-400 bg-opacity-25 mt-10 cursor-pointer active:scale-95 transition ease-in-out duration-75"onClick={() => addItem()}>Add item</button></div></div>);}
The last step is to add a navigation bar to make it easy to navigate to the cart.
Create the file
with the following content:Copy to clipboardapp/components/topNavigator.tsx
123456789101112import { Link } from '@remix-run/react';export default function TopNavigator() {return (<nav className="flex w-screen fixed top-0 right-0 left-0 items-center py-4 flex-row justify-between px-10 sm:px-20 md:px-44 z-10 bg-black"><Link to="/" className="text-xl">MRS</Link><Link to="/cart">Cart</Link></nav>);}
Add the
component to yourCopy to clipboardTopNavigator
so it appears on all pages. Add it just above theCopy to clipboardroot.tsx
:Copy to clipboardOutlet
123456789101112131415import TopNavigator from './components/topNavigator';export default function App() {return (...<CartProvider><OutletContainer><TopNavigator /><Outlet /></OutletContainer>...</CartProvider>...);}
Test Product Page
To test out your product page, restart your Remix server (make sure Strapi and Medusa servers are already running).
Click on any of the products on your homepage and you will be able to see the details.
Create Cart Page
Now, you will create your very final cart page.
Create the file
with the following content:Copy to clipboardapp/routes/cart.tsx
123456789101112131415161718192021222324252627282930313233import { useState, useEffect } from "react";import { medusaClient } from "~/lib/config";import type { Cart as CartType } from "medusa-react/dist/types";export default function Cart() {const [cart, setCart] = useState<CartType>();useEffect(() => {medusaClient.carts.retrieve(localStorage.getItem("cart_id")!).then(({ cart }) => {setCart(cart);});}, [cart]);return (<div className="px-10 sm:px-20 md:px-44 pt-44 max-w-[100rem] flex-grow w-screen">{cart?.items.map((variant) => (<divkey={variant.id}className="flex flex-col xl:flex-row h-64 my-10 space-x-8 space-y-4 items-center"><img className="h-full" src={variant.thumbnail!} /><div><h3 className="pt-2 text-white text-xl">{variant.title}</h3><p className="text-slate-400">{variant.quantity}</p></div></div>))}</div>);}
is an array of all the items in the customer’s cart. You display each item with its thumbnail, title, and quantity.Copy to clipboardcart.items
Test Cart Page
Restart your Remix server (make sure Strapi and Medusa servers are already running). When you add an item to the cart it will show on the cart page.
Conclusion
There’s still much more that can be done to improve your storefront such as:
- Improve the UI
- Improve Cart page to add functionalities such as removing or updating items.
- Implement Checkout Flow
- Integrate Stripe Plugin for payment.
- Install Medusa Admin to manage orders, products, and much more.
- Check out Medusa’s Storefront API Reference to learn what more you can do in your storefront.
Should you have any issues or questions related to Medusa, then feel free to reach out to the Medusa team via Discord.
Share this post