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 clipboard
yarn 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 Copy to clipboardstrapi-medusa
in your project. Once the installation is complete, the Strapi development server will start on port Copy to clipboardlocalhost:1337
. 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.
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 Copy to clipboard--seed
flag creates an SQLite database and seeds it with some demo data.
Change to the Copy to clipboardmedusa-server
directory and go to Copy to clipboardmedusa.config.js
. Change the exported object at the end to enable Redis:
1234567module.exports = {projectConfig: {redis_url: REDIS_URL,//...}//...};
The default Redis connection string is Copy to clipboardredis://localhost:6379
but if you have made changes to it, go to the Copy to clipboard.env
file and add the following:
1REDIS_URL=<YOUR_REDIS_URL>
Where Copy to clipboard<YOUR_REDIS_URL>
is your connection string.
Additionally, since the Remix storefront runs on Copy to clipboardlocalhost:3000
, you have to add an environment variable Copy to clipboardSTORE_CORS
that sets the URL of the storefront.
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:
- Copy to clipboard
<STRAPI_IDENTIFIER>
is either the email address or username of the user you created in the previous step. - Copy to clipboard
<STRAPI_PASSWORD>
is the password of the user you created in the previous step. - Copy to clipboard
<STRAPI_PROTOCOL>
is the protocol of your Strapi server. Since, you’re using a local Strapi server, set this to Copy to clipboardhttp
. The default value is Copy to clipboardhttps
. - Copy to clipboard
<STRAPI_URL>
is the URL of your Strapi server. By default, the URL is Copy to clipboardlocalhost
. - Copy to clipboard
<STRAPI_PORT>
is the port the Strapi server runs on. By default, the port is Copy to clipboard1337
.
Finally, open Copy to clipboardmedusa-config.js
and add the following new item to the Copy to clipboardplugins
array:
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 Copy to clipboardlocalhost:9000
. You’ll see that Copy to clipboardproduct.created
events have been triggered along with similar events.
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 Copy to clipboardhero_text
in the next step and click Finish.
Go to the Hero Text component under Homepage in components and create three text fields named Copy to clipboardstart_text
, Copy to clipboardmid_text
and 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 Copy to clipboardmid_text
to highlight it.
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 Copy to clipboardheading_2
. Save your changes in the homepage content type.
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 Copy to clipboardmedusa-server
and Copy to clipboardstrapi-medusa
), run the following command:
1npx create-remix@latest my-storefront
It will ask you a few questions. Choose Copy to clipboardJust the basics
, then choose your preferred hosting platform (you can choose Remix App Server if you are unsure), choose typescript, and no for Copy to clipboardnpm install
if you wish to use Copy to clipboardyarn
.
Then, change to the Copy to clipboardmy-storefront
directory and install dependencies with yarn:
12cd my-storefrontyarn install
Configure Tailwind CSS
Install Tailwind CSS to design the UI element:
1yarn add -D tailwindcss postcss autoprefixer concurrently
Run Copy to clipboardnpx tailwindcss init
to create your Copy to clipboardtailwind.config.js
file. Then, set its content to the following:
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 Copy to clipboardstyles/app.css
with the following content:
123@tailwind base;@tailwind components;@tailwind utilities;
Lastly, add this to your Copy to clipboardapp/root.tsx
after the list of imports:
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 Copy to clipboardmedusa-react
library uses react-query as a solution for server-side state management and lists the library as a peer dependency.
In order to use the hooks exposed by Copy to clipboardmedusa-react
, you will need to include the Copy to clipboardMedusaProvider
somewhere up in your component tree. The Copy to clipboardMedusaProvider
takes a Copy to clipboardbaseUrl
prop which should point to your Medusa server. Under the hood, Copy to clipboardmedusa-react
uses the Copy to clipboardmedusa-js
client library (built on top of Copy to clipboardaxios
) to interact with your server.
In addition, because medusa-react is built on top of react-query, you can pass an object representing react-query's QueryClientProvider props, which Copy to clipboardMedusaProvider
will pass along.
You also need to wrap your app in a Copy to clipboardCartProvider
since that will let you use the cart functionalities provided by Medusa, which you will do later.
Create a file Copy to clipboardapp/lib/config.ts
. This file will contain your Copy to clipboardmedusaClient
which will let you use Medusa’s Javascript client in your app.
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 Copy to clipboardapp/root.tsx
and import the required packages:
12import { MedusaProvider, CartProvider } from 'medusa-react';import { MEDUSA_BACKEND_URL, queryClient } from './lib/config';
You can also edit the Copy to clipboardmeta
here to change your metadata
12345export const meta: MetaFunction = () => ({charset: 'utf-8',title: 'New Remix App',viewport: 'width=device-width,initial-scale=1',});
Below this, you will see the Copy to clipboardApp
component. In the returned JSX add the Copy to clipboardMedusaProvider
and Copy to clipboardCartProvider
with some base styles to the 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: Copy to clipboardlocalhost:1337/api/home-page
(add Copy to clipboard?populate=*
to also show the nested products). It returns an object with the Copy to clipboarddata
& Copy to clipboardmeta
arrays. You don't have to care about the Copy to clipboardmeta
, what you should really care about is Copy to clipboarddata
, it contains all the content you entered in your Strapi Dashboard.
First, create the file Copy to clipboardapp/types/StrapiResponse.ts
with the following content:
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 Copy to clipboardapp/models/home.server.ts
with the following content:
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 Copy to clipboardgetHomePageData
function, you should only return the data you need on your home page.
In the above code sample, you will notice that in the import statement Copy to clipboard~
is used, this is because it is the alias set for the Copy to clipboardapp
directory set in the Copy to clipboardtsconfig
by default in Remix, if you wish you can change it at your convenience.
All files inside the Copy to clipboardapp/routes
directory will be a route. For example, Copy to clipboardapp/routes/store.tsx
will contain the Copy to clipboard/store
route.
Next, go to Copy to clipboardapp/routes/index.tsx
and create a loader function:
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 Copy to clipboarduseLoaderData
hook from Remix inside the Copy to clipboardIndex
component:
12345678import { useLoaderData } from '@remix-run/react';export default function Index() {const { heroText, products, smallHeading } =useLoaderData<homePageDataType>();...}
Here, Copy to clipboardhomePageData
was destructured and brought in using Copy to clipboarduseLoaderData
, now you can use it on your page.
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>);}
Copy to clipboardheroText.start_text
brings data from the Copy to clipboardstart_text
text field under the Copy to clipboardhero_text
component you made in Strapi. Similarly, Copy to clipboardheroText.mid_text
and Copy to clipboardheroText.end_text
are from Copy to clipboardmid_text
and Copy to clipboardend_text
fields from Strapi respectively.
Then Copy to clipboardmid_text
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.
To display your products, create the file Copy to clipboardapp/components/productCard.tsx
with the following content:
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 Copy to clipboardLink
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.
Now getting back to your Copy to clipboardapp/routes/index.tsx
, you will map your Strapi response (products) to the page.
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 Copy to clipboardyarn dev
(make sure that your Medusa and Strapi servers are already running).
Your app is ready at Copy to clipboardlocalhost:3000
and it will look like the following:
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 Copy to clipboardapp/components/outletContainer.tsx
with the following content:
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 Copy to clipboardmedusa-react
's Copy to clipboarduseCart
hook, Copy to clipboardsetCart
will set your cart globally. You can then use it anywhere in your app. The Copy to clipboardoutletContainer
will also save cart localStorage so that the added items persist even when the user returns.
You will also need to show toast notifications when a product is added to the cart. Install Copy to clipboardreact-hot-toast
to do this:
1yarn add react-hot-toast
Now, go back to your Copy to clipboardapp/root.tsx
and wrap your Copy to clipboard<Outlet />
with Copy to clipboardOutletContainer
. Also, add Copy to clipboard<Toaster />
from Copy to clipboardreact-hot-toast
that will let you show notifications:
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 Copy to clipboardhandle
. In Remix you will name your dynamic pages as Copy to clipboard$slug.tsx
.
You will need to get the Copy to clipboardhandle
from the URL of your page, you can do that with a loader function but it’s much simpler to use Copy to clipboarduseParams
hook.
Create the file Copy to clipboardapp/routes/products/$slug.tsx
with the following content:
123456import { useParams } from '@remix-run/react';import { useCart, useCreateLineItem, useProducts } from 'medusa-react';export default function ProductSlug() {const { slug } = useParams();}
Copy to clipboardslug
is getting your page’s slug from your URL, for example, in Copy to clipboardlocalhost:3000/products/sweatshirt
the slug is Copy to clipboardsweatshirt
(remember you passed in the Copy to clipboardhandle
in your Copy to clipboardProductCard
component).
Next, fetch your product from Medusa using the Copy to clipboarduseProducts
hook and add it to the UI:
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 Copy to clipboarduseProducts
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).
Finally, you use the first item returned by the Copy to clipboarduseProducts
hook which is the product that has the handle in the page’s URL.
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 Copy to clipboardformatVariantPrice
function here from Copy to clipboardmedusa-react
. This formats the price according to your user’s region and the product variant selected.
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 Copy to clipboarduseCreateLineItem
hook lets you add items. It requires a cart ID. The Copy to clipboardaddItem
function will add the product to the cart and then show a toast notification.
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 Copy to clipboardapp/components/topNavigator.tsx
with the following content:
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 Copy to clipboardTopNavigator
component to your Copy to clipboardroot.tsx
so it appears on all pages. Add it just above the 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 Copy to clipboardapp/routes/cart.tsx
with the following content:
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>);}
Copy to clipboardcart.items
is an array of all the items in the customer’s cart. You display each item with its thumbnail, title, and quantity.
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