Home
Blog
Community

How to Create An Ecommerce App with Medusa and Ionic

Aug 25, 2022 by

Silvia O'Dwyer

Silvia avatar

Silvia O'Dwyer

Learn how to build a powerful ecommerce application with this step-by-step tutorial using Medusa's server and Ionic SDK.

How to Create An Ecommerce App with Medusa and Ionic - Featured image

Notice Medusa is under active development, so the content of this post may be outdated. Please check out our documentation instead.

Ionic is an open source toolkit that allows developers to create cross-platform apps that support a variety of mobile platforms, including Android and iOS. Developers can build with their frontend framework of choice, including Angular, Vue and React.

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.

In this tutorial, you’ll build an ecommerce app using Medusa and Ionic. This app can then be used on mobile phones such as Android, iOS and Windows phones, and can also be used as a progressive web app (PWA).

You can view the source code for the tutorial in this repository on GitHub.

Prerequisites

To use Medusa, you need Node.js (version 14+) installed on your machine. You can download it from the official Node.js website.

Set up the Medusa Server

First, install the Medusa CLI by running the following command in your terminal:

npm install -g @medusajs/medusa-cli

Next, run the following command to create a new Medusa server:

medusa new ecommerce-store-server --seed

The Copy to clipboard--seed flag populates the database with demo data that can then be used as part of the ecommerce store later on.

Finally, navigate to the Copy to clipboardecommerce-store-server directory and start the server:

cd ecommerce-store-server
medusa develop

If the server runs successfully, you should see in your terminal an output similar to this:

Medusa_server_console_output

Install Medusa Admin

Next up, it's time to setup and run the Medusa Admin dashboard. In a separate directory, run the following command:

git clone [https://github.com/medusajs/admin](https://github.com/medusajs/admin) medusa-admin

Navigate into the newly-created Copy to clipboardmedusa-admin directory and install the dependencies for the project:

cd medusa-admin
npm install

Then, to run the admin, execute the following command in your terminal:

npm run develop

This runs the Medusa admin on Copy to clipboardlocalhost:7000. Make sure the Medusa server is also still running.

If you open the Medusa Admin, you should see a login page.

Login_to_Medusa_Admin

Login screen for Medusa Admin

Since you created a Medusa server in the previous section with the Copy to clipboard--seed flag, a test user was created in addition to the demo data. So, you can use the email Copy to clipboardadmin@medusa-test.com and password Copy to clipboardsupersecret to log in.

The Medusa admin includes many functionalities such as viewing orders, managing products, configuring your store and regions, and much more!

You can try editing some of the existing demo products or adding new products in the Medusa admin.

Editing_products_in_Medusa_Admin

Editing a product’s metadata in Medusa Admin

Initialize An Ionic Project

In this section, you’ll start building the Ionic app.

First, install the Ionic CLI by running the following command:

npm install -g @ionic/cli

Then, in a separate directory, create a new Ionic app using the following command:

ionic start ecommerce-store blank --type=react

This tutorial uses React to create the Ionic app. This is specified as part of the command above with the Copy to clipboard--type flag.

It usually takes several minutes to install all the dependencies required for the project.

Once the installation is done, change to the Copy to clipboardecommerce-store directory and install the other dependencies required:

cd ecommerce-store
npm install axios

Copy to clipboardaxios is used to send asynchronous requests to the Medusa server. This will allow you to perform operations such as fetching products.

Testing the Ionic App

To test out the blank ionic app, run the following command in your terminal:

ionic serve --lab

This runs a development Ionic server on Copy to clipboardlocalhost:8100 and the Ionic Lab on Copy to clipboardlocalhost:8200. You can use the Ionic Lab to simulate how the app looks like on different devices such as iOS or Android.

Ionic_Lab

Change Store CORS Variable

Since the Ionic app runs on port 8100, you need to update the Store CORS settings on your Medusa server in the Copy to clipboardmedusa-config.js file to the following:

const STORE_CORS = process.env.STORE_CORS || "http://localhost:8100"

For more information, check out this official guide on updating your CORS settings.

Make sure to restart the Medusa server after making this change.

Create Product Item Cards

In this section, you’ll create a reusable component to display products as cards on the home page.

First, you need to create two interfaces, one for products and another for images. These interfaces will be used to define the structure of a product and an image.

To do that, create the file Copy to clipboardsrc/Interfaces.tsx with the following content:

export interface Product {
id: string;
title: string;
handle: string;
images: Image[];
description: string;
variants: any[];
}
export interface Image {
url: string;
}

Next, you’ll create the reusable product item card component.

Now that the interfaces are defined and exported, it’s time to create the UI for the product item cards.

Create a new file Copy to clipboardsrc/components/ProductItemCard/ProductItemCard.tsx with the following content:

import React, { useEffect } from 'react';
import { IonCard, IonCardHeader, IonCardSubtitle, IonImg, IonCardTitle } from '@ionic/react';
import { Product } from '../../Interfaces';
const ProductItemCard = ({ product }: { product: Product }) => {
return (
<div>
{product && (
<IonCard routerLink={"/product/" + product["id"]} className="product_card">
<IonImg src={product.images[0]["url"]} class="image" />
<IonCardHeader>
<IonCardTitle className="product_title"><b>{product["title"]}</b></IonCardTitle>
<IonCardSubtitle>{product["handle"]}</IonCardSubtitle>
<IonCardSubtitle>${product["variants"][0]["prices"][1]["amount"] / 100}</IonCardSubtitle>
</IonCardHeader>
</IonCard>
)}
</div>
);
};
export default ProductItemCard;

Each card displays the image, title, type and price of a product. A product prop will be passed to the component, and its corresponding metadata is then displayed. The Copy to clipboardProduct interface is used to enforce the type of the Copy to clipboardproduct prop.

Create the Home layout

Now that the component for individual product cards has been created, it’s time to fetch and render the products in the Home layout screen.

The Copy to clipboardHome.tsx and Copy to clipboardHome.css files are created by default in Copy to clipboardsrc/pages when you initialize an Ionic project. Create a new directory Copy to clipboardsrc/pages/Home and move Copy to clipboardHome.tsx and Copy to clipboardHome.css into the Copy to clipboardsrc/pages/Home directory.

Edit the Header

If you open the Copy to clipboardsrc/pages/Home/Home.tsx file and take a look at the returned JSX, you'll see a header has automatically been added for you. You can replace the text nested in the component Copy to clipboardIonTitle with the name of your ecommerce store. For example:

<IonHeader>
<IonToolbar>
<IonTitle>Medusa Ecommerce Store</IonTitle>
</IonToolbar>
</IonHeader>

Fetch Products from the Medusa Server

Create the file Copy to clipboardsrc/server-url.js with the following content:

const medusaServerBaseURL = "http://localhost:9000";
export default medusaServerBaseURL;

It’s useful to define the base URL of the Medusa server in one file. Then, if the port or URL needs to be updated, you only need to update the URL in this file.

If you’re testing on a mobile device, the URL should be changed to your machine’s IP.

Next, in Copy to clipboardsrc/pages/Home/Home.tsx, replace the imports at the beginning of the file with the following:

import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar, IonGrid, IonRow, IonCol, } from '@ionic/react';
import './Home.css';
import React, { useEffect, useState } from 'react';
import axios from "axios";
import ProductItemCard from '../../components/ProductItemCard/ProductItemCard';
import medusaServerBaseURL from "../../server-url";

Then, create a state variable inside the Copy to clipboardHome component to store the products:

const [products, setProducts] = useState([]);

And add the following after creating the state variable:

useEffect(() => {
axios
.get(`${medusaServerBaseURL}/store/products`)
.then((response) => {
if (response.data) {
let products = response.data.products;
setProducts(products);
}
})
.catch((err) => {
console.log("error", err)
});
}, []);

With Copy to clipboarduseEffect, the Home component will fetch the products from the server when the screen first opens. A request is sent with Copy to clipboardaxios to the List Products endpoint. Then, the result is used to set the Copy to clipboardproducts state variable.

Create A Grid of Products

Next up, it’s time to create a grid of product items using the Copy to clipboard<IonGrid> component.

Still in Copy to clipboardsrc/pages/Home/Home.tsx, add the following within the Copy to clipboard<IonContent> element in the returned JSX, replacing the Copy to clipboard<ExploreContainer> component:

<IonGrid class="ion-no-padding ion-no-margin">
<IonRow>
{products.map((product, i) =>
<IonCol size="6">
<ProductItemCard product={product} />
</IonCol>)}
</IonRow>
</IonGrid>

This grid renders each product using the Copy to clipboardProductItemCard component. Two products are displayed per row, but if you’d like to alter this to a single product per row, update the Copy to clipboardsize prop for the Copy to clipboardIonCol element to Copy to clipboard12. For more information on grids in Ionic, be sure to take a look at the official documentation.

Add the CSS

Change the content of Copy to clipboardsrc/pages/Home/Home.css to add some helpful styling:

.product_card {
cursor: pointer;
}
.product_title {
font-size: 1em;
}

Testing the Home Screen

Make sure that the Medusa server is still running and re-run the Ionic server if it’s not still running.

If you open the app now in Ionic lab, you should see on the Home screen the products fetched from your Medusa server.

Please note that the screenshot shown is in dark mode due to system preferences. If you use light mode, the screen will look different.

Home screen for the app, listing the products in a grid display

Create the ProductDetail Screen

In this section, you’ll create the Copy to clipboardProductDetail screen. This screen will display the individual product’s information and image.

Create the file Copy to clipboardsrc/pages/ProductDetailPage/ProductDetailPage.tsx with the following content:

import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/react';
import './ProductDetailPage.css';
import React, { useEffect, useState } from 'react';
import { IonCard, IonCardHeader, IonBackButton, IonButtons, IonCardSubtitle, IonToast, IonImg, IonCardTitle, IonCardContent, IonButton } from '@ionic/react';
import axios from "axios";
import { RouteComponentProps } from 'react-router-dom';
import { Product } from '../../Interfaces';
import medusaServerBaseURL from "../../server-url";
const ProductDetailPage: React.FC<RouteComponentProps<{ id: string }>> = (props) => {
const [product, setProduct] = useState<Product>();
useEffect(() => {
let product_id = props.match.params.id;
axios
.get(`${medusaServerBaseURL}/store/products/${product_id}`)
.then((response) => {
if (response.data.product) {
setProduct(response.data.product);
}
})
.catch((err) => {
console.log("error", err)
});
}, [props.match.params.id])
return (
<IonPage>
<IonHeader>
<IonToolbar>
<IonButtons slot="start">
<IonBackButton text="">
</IonBackButton>
</IonButtons>
<IonTitle>Medusa Ecommerce Store</IonTitle>
</IonToolbar>
</IonHeader>
<IonContent fullscreen>
{product && (
<IonCard mode="ios">
{product["images"] && (
<IonImg class="product_detail_img" src={product.images[0]["url"]} />
)}
<IonCardHeader>
<div className="metaInfo">
<IonCardTitle>{product["title"]}</IonCardTitle>
<IonCardSubtitle>{product["handle"]}</IonCardSubtitle>
<h3>${product["variants"][0]["prices"][1]["amount"] / 100}</h3>
</div>
</IonCardHeader>
<IonCardContent>
<h3>Description</h3>
{product["description"]}
<IonButton class="button" size="default" shape="round" expand="block">Add to Cart</IonButton>
</IonCardContent>
</IonCard>
)}
</IonContent>
</IonPage>
);
};
export default ProductDetailPage;

In this page, the product ID is retrieved from the route parameters. Then, the Copy to clipboardaxios library is used to send a request to the Retrieve Product endpoint on the Medusa server to retrieve the individual product’s data. Then, the Copy to clipboardproduct state variable is set using the response of the request.

Next, create the file Copy to clipboardsrc/pages/ProductDetailPage/ProductDetailPage.css with the following content:

.product_detail_img {
height: 30vh;
object-fit: cover;
}
@media (prefers-color-scheme: light) {
h3 {
color: black;
}
}
h3 {
font-weight: bold;
}
.button {
margin-top: 1em;
}
.metaInfo {
display: flex;
flex-direction: column;
flex-wrap: wrap;
}

Add a New Route

To actually use the new screen, it must be added as a new route in the app.

First, import the Copy to clipboardProductDetailPage component in Copy to clipboardsrc/App.tsx:

import ProductDetailPage from './pages/ProductDetailPage/ProductDetailPage';

Then, add the new route into the list of routes defined in Copy to clipboardApp:

const App: React.FC = () => (
<IonApp>
<IonReactRouter>
<IonRouterOutlet>
<Route exact path="/home">
<Home />
</Route>
<Route exact path="/">
<Redirect to="/home" />
</Route>
<Route path="/product/:id/" component={ProductDetailPage} />
</IonRouterOutlet>
</IonReactRouter>
</IonApp>
);

Test Product Details Screen

While the Medusa and Ionic development servers are still running, open the Ionic Lab in your browser and click on one of the products in the home screen. A new screen opens showing the product’s details.

Show Add to Cart Notification

In this section, you’ll add a simple toast notification when the Add to Cart button is clicked. This doesn’t actually add the product to cart but only simulates the functionality.

In the Copy to clipboardsrc/pages/ProductDetailPage/ProductDetailPage.tsx file, add the following after the creation of the Copy to clipboardproduct state variable to create a new state variable managing the visibility of the toast notification:

const [showToast, setShowToast] = useState(false);

Then, add an Copy to clipboardIonToast component in the returned JSX. It should be placed within Copy to clipboardIonContent and after the Copy to clipboardIonCard component:

<IonContent fullscreen>
{product && (
<IonCard mode="ios">
...
</IonCard>
)}
<IonToast
isOpen={showToast}
onDidDismiss={() => setShowToast(false)}
message="Product added to cart"
duration={800}
/>
</IonContent>

Finally, change the Add to Cart button to add an Copy to clipboardonClick event handler:

<IonButton class="button" size="default" shape="round" expand="block"
onClick={() => setShowToast(true)}>Add to Cart</IonButton>

Now, whenever the button is clicked, the value of Copy to clipboardshowToast is set to Copy to clipboardtrue to show the toast notification.

Testing the Notification

While the Medusa and Ionic development servers are still running, on the details screen of one of the products click the Add to Cart button. A toast notification will then be shown for a few seconds indicating that the product was added to cart.

What’s Next?

By following this tutorial, you’ve successfully connected your Ionic app to your Medusa server, and fetched products from the server.

More features can be added using your Medusa server in your Ionic app including:

  • Adding cart functionalities that allows the customer to add items to their carts and manage its content.
  • Implement the Checkout flow to allow customers to place an order
  • Integrating a payment provider such as Stripe
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

Try Medusa

Spin up your environment in a few minutes.