How I Built a Vue.js Ecommerce Store with a Node.js Backend
Ecommerce is no small undertaking. Aside from building a great customer experience on the frontend, you’ll also need to have the right setup to handle all cart, customer and order data, product information, etc…
In this tutorial, you’ll get a sneak peek into how it can be done!
In this tutorial, you’ll get set up with the necessary prerequisites for a great ecommerce experience. Vue.js is a great choice for a frontend framework because it’s open source, it has a component-based architecture, and it is reactive.
For the backend, you’ll learn how to use Medusa, an open source Node.js commerce engine that ships with all necessary ecommerce functionality including an easily navigatable admin system.
The full code for this Vue.js project is located in this GitHub repo. Below is a quick sneak peek of the final result.
What is Vue.js
Vue.js is an open source progressive JavaScript framework. It can be used to build a variety of types of apps, including websites, mobile apps, or desktop apps. This can be done by using Vue.js along with other platforms like Electron or Ionic.
Vue.js has been rising in popularity since it was first released due to its many advantages. It is easy to learn and use, with concise documentation and a smooth learning curve. It also has a tiny size, a reactive system, and a component-based, reusable architecture.
What is Medusa
The open-source Shopify alternative ⚡️
Medusa is the #1 open source, Node.js commerce platform on GitHub. Its composable and headless architecture allows it to be incorporated with any tech stack to build cross-platform ecommerce stores, ranging from web to android and iOS applications.
Medusa allows developers to build scalable and maintainable ecommerce stores. It ships with many advanced ecommerce features such as an admin store dashboard, product configurations, manual orders, multi-currency support, and much more. Likewise, it easily integrates with different payment, CMS, and shipping options.
Please star if you like the tool 🌟
Prerequisites
Before you start, be sure you have Node.js version 14 or above.
Create a Server with Medusa Commerce Engine
To set up the Medusa server on your local machine, follow the steps outlined in the sections below.
Install Medusa CLI Tool
Medusa CLI can be installed using npm
or yarn
, but this tutorial uses npm
. Run the following command to install the Medusa CLI:
1 2 |
npm <span class="nb">install</span> @medusajs/medusa-cli <span class="nt">-g</span> |
Create a New Medusa Store Server
To create a new Medusa store server, run the command below:
1 2 |
medusa new my-medusa-store <span class="nt">--seed</span> |
Note: my-medusa-store
represents the project name; you can change yours to your preferred project name.
If you created a new Medusa project successfully, you should get a result similar to the screenshot below.
Test Your Medusa Server
To test your Medusa server, change to the newly created directory and run the develop
command using Medusa’s CLI:
1 2 3 |
<span class="nb">cd </span>my-medusa-store medusa develop |
You can test it by sending a request to localhost:9000/store/products/
which lists the available products in your store.
Medusa Admin Installation
To set up your Medusa Admin, follow the steps below.
- Clone the Medusa Admin repository:
1 2 3 |
git clone https://github.com/medusajs/admin medusa-admin <span class="nb">cd </span>medusa-admin |
- Run the command below to install all necessary dependencies:
1 2 |
npm <span class="nb">install</span> |
- Test it:
1 2 |
npm start |
By default, Medusa Admin runs on port 7000
. You can go to localhost:7000
on your browser to access your admin page.
Since you included the --seed
option while installing the Medusa server, a dummy admin user has been created. The email is [email protected]
, and the password is supersecret
.
With the Medusa Admin, you can create new products and collections for your store. You can also edit, unpublish, duplicate and delete products from the admin.
You can visit the User Guide to learn more about Medusa Admin.
Create a New Vue.js Project
The next thing is to create and set up a new Vue.js project for the ecommerce project. You can run this command to set up a new Vue.js project:
1 2 |
npm init vue@latest |
This command will install and execute create-vue, the official Vue project scaffolding tool. You will be presented with prompts for a number of optional features such as TypeScript and testing support.
Ensure you choose the same option as the ones in the above screenshot.
Once the project is created, use the following commands to install the necessary dependencies:
1 2 3 |
<span class="nb">cd </span>vuejs-ecommerce npm <span class="nb">install</span> |
Tailwind CSS Installation
Tailwind CSS is a CSS framework that allows you to effortlessly style your ecommerce storefront. In this tutorial, you’ll use Tailwind CSS in your Vue.js ecommerce storefront.
In the vuejs-ecommerce
directory, run the following command to install Tailwind CSS:
1 2 3 |
npm <span class="nb">install</span> <span class="nt">-D</span> tailwindcss postcss autoprefixer npx tailwindcss init <span class="nt">-p</span> |
The above command will generate both the tailwind.config.js
and postcss.config.js
files in your Vue.js project.
Configure Your Template Paths
In your tailwind.config.js
file, replace the content
array with the following snippet:
1 2 |
<span class="nx">content</span><span class="p">:</span> <span class="p">[</span> <span class="dl">"</span><span class="s2">./index.html</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">./src/**/*.{js,ts,jsx,tsx,vue}</span><span class="dl">"</span><span class="p">,</span> <span class="p">],</span> |
Add the Tailwind Directives to your CSS
Go to src/assets/main.css
and replace the entire code with the snippet below:
1 2 3 4 |
<span class="k">@tailwind</span> <span class="n">base</span><span class="p">;</span> <span class="k">@tailwind</span> <span class="n">components</span><span class="p">;</span> <span class="k">@tailwind</span> <span class="n">utilities</span><span class="p">;</span> |
- The
base
layer handles things like reset rules or default styles applied to plain HTML elements. - The
components
layer handles class-based styles that you want to be able to override with utilities. - The
utilities
layer handles small, single-purpose classes that should always take precedence over any other styles.
You can visit Tailwind CSS documentation to learn more.
Integrate Vue.js Ecommerce Storefront with Medusa
In this section, you’ll prepare to integrate the Vue.js ecommerce storefront with the Medusa server to interact with APIs later on.
Create a Base URL Environment Variable
The baseURL
environment variable is the URL of your Medusa server. Create a .env
file in the vuejs-ecommerce
directory with the following content:
1 2 |
VITE_baseUrl=http://localhost:9000 |
Note: If for any reason, you changed the default port number of your Medusa Server, you must change the 9000
to your port number here.
Install Axios in the Vue.js Project
You’ll use Axios to send requests to your Medusa server. You can install Axios by running the following command:
1 2 |
npm i axios |
Now that Axios is installed, the next step is integrating Medusa API into your project.
Creating Components for Your Vue.js Project
In this section, you will create components such as Header
, Footer
, and Products
components. These components will be used on different pages of your storefront.
In some components images are used to implement a better design for the storefront. You can find all images used for this project in the GitHub repository.
Page Header Component
Create the file src/components/PageHeader.vue
and add the following code to it:
1 2 3 4 5 6 7 |
<span class="p"><</span><span class="nt">template</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"fixed z-50 bg-white topNav w-full top-0 p-3 md:bg-opacity-0"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"max-w-6xl relative flex mx-auto flex-col md:flex-row"</span><span class="p">></span> <span class="p"><</span><span class="nt">a</span> <span class="na">href</span><span class="p">=</span><span class="s">""</span> <span class="na">class</span><span class="p">=</span><span class="s">"md:hidden absolute top-1 right-14"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"relative"</span><span class="p">></span> <span class="p"><</span><span class="nt">img</span> <span class="na">src</span><span class="p">=</span><span class="s">"/src/images/icon.png"</span> <span class="na">class</span><span class="p">=</span><span class="s">"bottom-1 cursor-pointer relative"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"absolute px-1 bg-red-500 -top-1 -right-1 rounded-full border-2 border-white text-white"</span> <span class="na">id</span><span class="p">=</span><span class="s">"cart2"</span> <span class="na">style</span><span class="p">=</span><span class="s">"font-size: 10px"</span><span class="p">></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">a</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"absolute h-10 flex justify-center bars items-center w-10 text-white right-1 -top-2 rounded-lg shadow-lg md:hidden cursor-pointer border-2 border-white bg-red-500"</span><span class="p">></span> <span class="p"><</span><span class="nt">i</span> <span class="na">class</span><span class="p">=</span><span class="s">"fa fa-bars"</span><span class="p">></</span><span class="nt">i</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"flex-grow font-bold text-lg"</span><span class="p">></span> <span class="p"><</span><span class="nt">router</span><span class="err">-</span><span class="na">link</span> <span class="err">:</span><span class="na">to</span><span class="p">=</span><span class="s">"{ name: 'home'}"</span> <span class="p">></span> <span class="p"><</span><span class="nt">span</span> <span class="na">class</span><span class="p">=</span><span class="s">""</span><span class="p">></span>Noble's Shop<span class="p"></</span><span class="nt">span</span><span class="p">></span> <span class="p"></</span><span class="nt">router</span><span class="err">-</span><span class="na">link</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"menu hidden md:flex flex-col md:flex-row mt-5 md:mt-0 gap-16"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"flex flex-col md:flex-row gap-12 capitalize"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">""</span><span class="p">></span> <span class="p"><</span><span class="nt">router</span><span class="err">-</span><span class="na">link</span> <span class="err">:</span><span class="na">to</span><span class="p">=</span><span class="s">"{ name: 'home'}"</span> <span class="p">></span> <span class="p"><</span><span class="nt">span</span> <span class="na">class</span><span class="p">=</span><span class="s">"text-red-400 font-bold border-b border-red-400"</span><span class="p">></span>home<span class="p"></</span><span class="nt">span</span><span class="p">></span> <span class="p"></</span><span class="nt">router</span><span class="err">-</span><span class="na">link</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">""</span><span class="p">></span> <span class="p"><</span><span class="nt">router</span><span class="err">-</span><span class="na">link</span> <span class="err">:</span><span class="na">to</span><span class="p">=</span><span class="s">"{ name: 'products'}"</span> <span class="p">></span> <span class="p"><</span><span class="nt">span</span> <span class="na">class</span><span class="p">=</span><span class="s">""</span><span class="p">></span>products<span class="p"></</span><span class="nt">span</span><span class="p">></span> <span class="p"></</span><span class="nt">router</span><span class="err">-</span><span class="na">link</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"flex gap-12"</span><span class="p">></span> <span class="p"><</span><span class="nt">a</span> <span class="na">href</span><span class="p">=</span><span class="s">"#"</span> <span class="na">class</span><span class="p">=</span><span class="s">"hidden md:block"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"relative"</span><span class="p">></span> <span class="p"><</span><span class="nt">img</span> <span class="na">src</span><span class="p">=</span><span class="s">"/src/images/icon.png"</span> <span class="na">class</span><span class="p">=</span><span class="s">"bottom-1 cursor-pointer relative"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">v-if</span><span class="p">=</span><span class="s">"productId.length > 0"</span> <span class="na">class</span><span class="p">=</span><span class="s">"absolute px-1 bg-red-500 -top-1 -right-1 rounded-full border-2 border-white text-white"</span> <span class="na">id</span><span class="p">=</span><span class="s">"cart"</span> <span class="na">style</span><span class="p">=</span><span class="s">"font-size: 10px"</span><span class="p">></span><span class="si">{</span><span class="p">{</span><span class="nx">productId</span><span class="p">.</span><span class="nx">length</span><span class="p">}</span><span class="si">}</span><span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">a</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">template</span><span class="p">></span> <span class="p"><</span><span class="nt">script</span><span class="p">></span> export default <span class="si">{</span> <span class="nx">props</span><span class="p">:{</span> <span class="nl">productId</span><span class="p">:</span><span class="nb">Array</span> <span class="p">}</span> <span class="si">}</span> <span class="p"></</span><span class="nt">script</span><span class="p">></span> |
The above code block covers the header page of this project. It includes the page routes, website name, and shopping cart icon.
This component will be added later in src/App.vue
of this project to add a header on every page of this storefront.
Footer Component
Create the file src/components/FooterComponent.vue
with the following content:
1 2 3 |
<span class="p"><</span><span class="nt">template</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"bg-purple-300 py-32 px-4 gap-y-20 justify-center items-center"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"font-extrabold text-4xl text-center space-y-3"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">""</span><span class="p">></span>Join Our Wait List And Get<span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"text-red-custom"</span><span class="p">></span>Discount Up to 50%<span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">""</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"py-1 flex relative max-w-xl mx-auto"</span><span class="p">></span> <span class="p"><</span><span class="nt">input</span> <span class="na">type</span><span class="p">=</span><span class="s">"text"</span> <span class="na">placeholder</span><span class="p">=</span><span class="s">"Enter Your Email Here"</span> <span class="na">class</span><span class="p">=</span><span class="s">"text-sm border w-full pr-52 focus:ring-red-400 focus:border-red-400 relative px-5 placeholder-gray-400 py-6 -right-1 border-red-400 rounded-lg flex-grow"</span><span class="p">></span> <span class="p"><</span><span class="nt">span</span> <span class="na">class</span><span class="p">=</span><span class="s">"bg-btn-color absolute text-base -right-1 uppercase rounded-lg z-10 text-white px-16 py-6 bg-opacity-25"</span> <span class="na">style</span><span class="p">=</span><span class="s">"top: 5px"</span><span class="p">></span> sign in <span class="p"></</span><span class="nt">span</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"bg-purple-800 py-32 PX-4"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"max-w-6xl gap-6 mx-auto grid grid-cols-1 md:grid-cols-9"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"md:col-span-3 py-3 space-y-4"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"text-2xl font-bold text-gray-100"</span><span class="p">></span>Noble's Shop<span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"text-gray-300 w-60 pr-0"</span><span class="p">></span>We sell only but quality and first grade Hoddies, Joggers, Shorts and lot more.<span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"md:col-span-2 py-3 space-y-4"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"text-2xl font-bold text-gray-100"</span><span class="p">></span>Information<span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"text-gray-300 w-60 space-y-2 pr-0"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">""</span><span class="p">></span>About Us<span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">""</span><span class="p">></span>More Search<span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">""</span><span class="p">></span>Online Order<span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">""</span><span class="p">></span>Support<span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"md:col-span-2 py-3 space-y-4"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"text-2xl font-bold text-gray-100"</span><span class="p">></span>Our Services<span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"text-gray-300 w-60 space-y-2 pr-0"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">""</span><span class="p">></span>Clothing<span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">""</span><span class="p">></span>Fashion<span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">""</span><span class="p">></span>Design<span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">""</span><span class="p">></span>Privacy<span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"md:col-span-2 py-3 space-y-4"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"text-2xl font-bold text-gray-100"</span><span class="p">></span>Contact Us<span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"text-gray-300 w-60 space-y-2 pr-0"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">""</span><span class="p">></span>+234 098-897-8888<span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">""</span><span class="p">></span>info@domain-name.com<span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">""</span><span class="p">></span>Terms <span class="err">&</span> Condition<span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">template</span><span class="p">></span> |
In the above component, you covered everything about the footer page of this ecommerce app. You’ll use it later across your pages where you want to add a footer.
Products Component
Create the file src/components/Products.vue
with the following content:
1 2 3 4 5 6 7 |
<span class="p"><</span><span class="nt">template</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"py-28"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"max-w-6xl mx-auto py-4 space-y-5"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"flex"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"flex-grow text-4xl font-extrabold"</span><span class="p">></span>Special Qualities For You<span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"grid gap-20 grid-cols-1 md:grid-cols-2 lg:grid-cols-3 px-3"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">v-for</span><span class="p">=</span><span class="s">"(products, i) in fetchData"</span> <span class="err">:</span><span class="na">key</span><span class="p">=</span><span class="s">"i"</span> <span class="na">class</span><span class="p">=</span><span class="s">"rounded-lg shadow-xl"</span> <span class="na">style</span><span class="p">=</span><span class="s">""</span><span class="p">></span> <span class="p"><</span><span class="nt">router</span><span class="err">-</span><span class="na">link</span> <span class="err">:</span><span class="na">to</span><span class="p">=</span><span class="s">"{ name: 'single-product', params: { id: products.id }}"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">" bg-white w-full flex justify-center items-center"</span><span class="p">></span> <span class="p"><</span><span class="nt">img</span> <span class="err">:</span><span class="na">src</span><span class="p">=</span><span class="s">"products.thumbnail"</span> <span class="na">alt</span><span class="p">=</span><span class="s">""</span> <span class="na">srcset</span><span class="p">=</span><span class="s">""</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"bg-purple-100 py-8 relative font-bold text-xl w-full flex flex-col justify-center px-6"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">""</span><span class="p">></span><span class="si">{</span><span class="p">{</span> <span class="nx">products</span><span class="p">.</span><span class="nx">title</span><span class="p">}</span><span class="si">}</span><span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">""</span><span class="p">></span> <span class="ni">&euro;</span> <span class="si">{</span><span class="p">{</span> <span class="nx">products</span><span class="p">.</span><span class="nx">variants</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">prices</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">amount</span> <span class="o">/</span> <span class="mi">100</span> <span class="p">}</span><span class="si">}</span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">router</span><span class="err">-</span><span class="na">link</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">template</span><span class="p">></span> <span class="p"><</span><span class="nt">script</span><span class="p">></span> import axios from 'axios' import <span class="si">{</span> <span class="nx">RouterLink</span> <span class="si">}</span> from 'vue-router'; export default (<span class="si">{</span> <span class="nx">data</span><span class="p">(){</span> <span class="k">return</span><span class="p">{</span> <span class="na">fetchData</span><span class="p">:[]</span> <span class="p">}</span> <span class="p">},</span> <span class="nx">mounted</span><span class="p">(){</span> <span class="c1">// calling the fetchProducts method when the page has loaded</span> <span class="k">this</span><span class="p">.</span><span class="nx">fetchProducts</span><span class="p">();</span> <span class="p">},</span> <span class="nx">methods</span><span class="p">:{</span> <span class="nx">fetchProducts</span><span class="p">(){</span> <span class="nx">axios</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="s2">`</span><span class="p">${</span><span class="k">import</span><span class="p">.</span><span class="nx">meta</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">VITE_baseUrl</span><span class="p">}</span><span class="s2">/store/products`</span><span class="p">)</span> <span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">data</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="nx">fetchData</span> <span class="o">=</span> <span class="nx">data</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">products</span> <span class="p">}).</span><span class="k">catch</span><span class="p">(</span><span class="nx">err</span> <span class="o">=></span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">err</span><span class="p">.</span><span class="nx">products</span><span class="p">));</span> <span class="p">}</span> <span class="si">}</span> }) <span class="p"></</span><span class="nt">script</span><span class="p">></span> |
The product component is where the products are fetched using Medusa’s APIs. fetchProducts
method is used to fetch products from the Medusa Server. Notice how you use the baseURL
defined in the .env
file.
In addition, the Vue router is being used to route each selected product to a single product page which you’ll add later.
Creating Vue.js Router File for Routing Pages
The next thing to do is to create a router file for routing our pages. By default, a route file is always generated when creating the vue.js project. Find the src/router/index.js
file and replace the code inside with the below content:
1 2 3 4 5 6 |
<span class="k">import</span> <span class="p">{</span> <span class="nx">createRouter</span><span class="p">,</span> <span class="nx">createWebHistory</span> <span class="p">}</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">vue-router</span><span class="dl">'</span> <span class="k">import</span> <span class="nx">HomeView</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../views/HomeView.vue</span><span class="dl">'</span> <span class="k">import</span> <span class="nx">SingleProductView</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../views/SingleProductView.vue</span><span class="dl">'</span> <span class="k">import</span> <span class="nx">ProductView</span> <span class="k">from</span> <span class="dl">'</span><span class="s1">../views/ProductView.vue</span><span class="dl">'</span> <span class="kd">const</span> <span class="nx">router</span> <span class="o">=</span> <span class="nx">createRouter</span><span class="p">({</span> <span class="na">history</span><span class="p">:</span> <span class="nx">createWebHistory</span><span class="p">(</span><span class="k">import</span><span class="p">.</span><span class="nx">meta</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">BASE_URL</span><span class="p">),</span> <span class="na">routes</span><span class="p">:</span> <span class="p">[</span> <span class="p">{</span> <span class="na">path</span><span class="p">:</span> <span class="dl">'</span><span class="s1">/</span><span class="dl">'</span><span class="p">,</span> <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">home</span><span class="dl">'</span><span class="p">,</span> <span class="na">component</span><span class="p">:</span> <span class="nx">HomeView</span> <span class="p">},</span> <span class="p">{</span> <span class="na">path</span><span class="p">:</span> <span class="dl">'</span><span class="s1">/products</span><span class="dl">'</span><span class="p">,</span> <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">products</span><span class="dl">'</span><span class="p">,</span> <span class="na">component</span><span class="p">:</span> <span class="nx">ProductView</span> <span class="p">},</span> <span class="p">{</span> <span class="na">path</span><span class="p">:</span> <span class="dl">'</span><span class="s1">/single-product/:id</span><span class="dl">'</span><span class="p">,</span> <span class="na">name</span><span class="p">:</span> <span class="dl">'</span><span class="s1">single-product</span><span class="dl">'</span><span class="p">,</span> <span class="na">component</span><span class="p">:</span> <span class="nx">SingleProductView</span> <span class="p">}</span> <span class="p">]</span> <span class="p">})</span> <span class="k">export</span> <span class="k">default</span> <span class="nx">router</span> |
In the above component, you specified all your routes, including the route path, name, and components. The routes you added are for the homepage, products page, and single product page.
Create Storefront Pages
In this section, you’ll create pages for your storefront. Before you start creating these pages, go to src/App.vue
and replace the code with the following:
1 2 3 4 5 6 7 8 9 |
<span class="p"><</span><span class="nt">script</span> <span class="na">setup</span><span class="p">></span> import <span class="si">{</span> <span class="nx">RouterLink</span><span class="p">,</span> <span class="nx">RouterView</span> <span class="si">}</span> from 'vue-router' import PageHeader from './components/PageHeader.vue' <span class="p"></</span><span class="nt">script</span><span class="p">></span> <span class="p"><</span><span class="nt">template</span><span class="p">></span> <span class="p"><</span><span class="nt">header</span><span class="p">></span> <span class="p"><</span><span class="nt">page</span><span class="err">-</span><span class="na">header</span> <span class="err">:</span><span class="na">productId</span><span class="p">=</span><span class="s">"products"</span> <span class="p">/></span> <span class="p"></</span><span class="nt">header</span><span class="p">></span> <span class="p"><</span><span class="nc">RouterView</span> <span class="err">@</span><span class="na">addp</span><span class="p">=</span><span class="s">"addP($event)"</span> <span class="p">/></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"fixed top-12 bg-green-600 text-white z-50 border text-md uppercase shadow-lg py-1 px-4 rounded-lg transistion-all duration-700"</span> <span class="err">:</span><span class="na">class</span><span class="p">=</span><span class="s">"notify"</span><span class="p">></span>item added successfully<span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">template</span><span class="p">></span> <span class="p"><</span><span class="nt">script</span><span class="p">></span> import axios from 'axios' export default <span class="si">{</span> <span class="nx">data</span><span class="p">(){</span> <span class="k">return</span><span class="p">{</span> <span class="na">products</span><span class="p">:[],</span> <span class="na">notify</span><span class="p">:</span><span class="dl">'</span><span class="s1">-right-64</span><span class="dl">'</span><span class="p">,</span> <span class="na">cartId</span><span class="p">:</span><span class="dl">''</span><span class="p">,</span> <span class="na">product_variant_id</span><span class="p">:</span><span class="kc">null</span> <span class="p">}</span> <span class="p">},</span> <span class="nx">mounted</span><span class="p">(){</span> <span class="k">this</span><span class="p">.</span><span class="nx">checkCartID</span><span class="p">()</span> <span class="p">},</span> <span class="nx">methods</span><span class="p">:{</span> <span class="nx">getCartID</span><span class="p">(){</span> <span class="nx">axios</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="s2">`</span><span class="p">${</span><span class="k">import</span><span class="p">.</span><span class="nx">meta</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">VITE_baseUrl</span><span class="p">}</span><span class="s2">/store/carts`</span><span class="p">).</span><span class="nx">then</span><span class="p">((</span><span class="nx">res</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span> <span class="nx">localStorage</span><span class="p">.</span><span class="nx">cart_id</span> <span class="o">=</span> <span class="nx">res</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">cart</span><span class="p">.</span><span class="nx">id</span><span class="p">;</span> <span class="p">});</span> <span class="p">},</span> <span class="nx">checkCartID</span><span class="p">(){</span> <span class="k">this</span><span class="p">.</span><span class="nx">cartId</span> <span class="o">=</span> <span class="nx">localStorage</span><span class="p">.</span><span class="nx">cart_id</span><span class="p">;</span> <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="k">this</span><span class="p">.</span><span class="nx">cartId</span><span class="p">)</span> <span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="nx">getCartID</span><span class="p">();</span> <span class="p">}</span> <span class="si">}</span>, addP(data)<span class="si">{</span> <span class="k">this</span><span class="p">.</span><span class="nx">cartId</span> <span class="o">=</span> <span class="nx">localStorage</span><span class="p">.</span><span class="nx">cart_id</span><span class="p">;</span> <span class="nx">axios</span><span class="p">.</span><span class="nx">post</span><span class="p">(</span><span class="s2">`</span><span class="p">${</span><span class="k">import</span><span class="p">.</span><span class="nx">meta</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">VITE_baseUrl</span><span class="p">}</span><span class="s2">/store/carts/</span><span class="p">${</span><span class="k">this</span><span class="p">.</span><span class="nx">cartId</span><span class="p">}</span><span class="s2">/line-items`</span><span class="p">,</span> <span class="p">{</span> <span class="na">variant_id</span><span class="p">:</span> <span class="nx">data</span><span class="p">,</span> <span class="na">quantity</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="p">})</span> <span class="p">.</span><span class="nx">then</span><span class="p">(({</span> <span class="nx">data</span> <span class="p">})</span> <span class="o">=></span> <span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="nx">products</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="nx">localStorage</span><span class="p">.</span><span class="nx">cart</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">products</span> <span class="k">this</span><span class="p">.</span><span class="nx">notify</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">right-3</span><span class="dl">'</span> <span class="kd">var</span> <span class="nx">inter</span> <span class="o">=</span> <span class="nx">setInterval</span><span class="p">(()</span> <span class="o">=></span><span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="nx">notify</span><span class="o">=</span><span class="dl">'</span><span class="s1">-right-64</span><span class="dl">'</span> <span class="nx">clearInterval</span><span class="p">(</span><span class="nx">inter</span><span class="p">)</span> <span class="p">},</span><span class="mi">1000</span><span class="p">)</span> <span class="p">})</span> <span class="si">}</span> } } <span class="p"></</span><span class="nt">script</span><span class="p">></span> |
Here, you import the header component so that it appears on all pages.
You also add some methods related to the cart. The checkCartID
method checks on page load if there’s a cart ID in the local storage, and if so calls the getCartID
method. The getCartID
method is used to fetch cart and its items from the Medusa server.
The addP
method is used to add products to your store cart. It will be called later on the Single Product page.
You can learn more about Medusa carts in the documentation.
Homepage View
Create the file src/views/HomeView.vue
with the following content:
1 2 3 4 5 6 7 8 9 10 |
<span class="p"><</span><span class="nt">script</span> <span class="na">setup</span><span class="p">></span> import PageHeader from '../components/PageHeader.vue' import Products from '../components/Products.vue' import FooterComponent from '../components/FooterComponent.vue' <span class="p"></</span><span class="nt">script</span><span class="p">></span> <span class="p"><</span><span class="nt">template</span><span class="p">></span> <span class="p"><</span><span class="nt">main</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"bg-purple-300 w-full"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">""</span> <span class="na">style</span><span class="p">=</span><span class="s">"height: 92vh"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"max-w-6xl mx-auto h-full flex items-center"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"grid gap-1 px-3 grid-cols-1 mt-8 md:grid-cols-5"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"md:col-span-2 order-2 flex flex-col justify-center space-y-12"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"space-y-5"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"font-extrabold w-full text-6xl"</span><span class="p">></span> Nothing But Qualities <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"text-lg leading-7 max-w-md"</span><span class="p">></span> We sell quality Hoddies, Joggers, Shorts and lot more <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">router</span><span class="err">-</span><span class="na">link</span> <span class="err">:</span><span class="na">to</span><span class="p">=</span><span class="s">"{ name: 'products'}"</span><span class="p">></span> <span class="p"><</span><span class="nt">span</span> <span class="na">class</span><span class="p">=</span><span class="s">'w-fit cursor-pointer transistion-all duration-300 hover:text-purple-400 text-white px-3 py-3 bg-gradient-to-r from-purple-500 to-pink-500 rounded flex justify-center items-center'</span><span class="p">></span> Explore More <span class="p"></</span><span class="nt">span</span><span class="p">></span> <span class="p"></</span><span class="nt">router</span><span class="err">-</span><span class="na">link</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"md:col-span-3 flex justify-center md:order-2"</span><span class="p">></span> <span class="p"><</span><span class="nt">img</span> <span class="na">src</span><span class="p">=</span><span class="s">"/src/images/hoddie.png"</span> <span class="na">class</span><span class="p">=</span><span class="s">"bottom-1 relative h-11/12"</span> <span class="na">style</span><span class="p">=</span><span class="s">""</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"flex justify-center bg-purple-300 py-4 pb-10 w-full"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"h-14 rounded-2xl relative"</span> <span class="na">style</span><span class="p">=</span><span class="s">"border: 1px solid black;padding:3px 6px 3px"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"bg-black rounded-full relative top-10"</span> <span class="na">style</span><span class="p">=</span><span class="s">"padding: 3px"</span><span class="p">></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">products</span><span class="p">/></span> <span class="p"><</span><span class="nt">footer</span><span class="err">-</span><span class="na">component</span><span class="p">/></span> <span class="p"></</span><span class="nt">main</span><span class="p">></span> <span class="p"></</span><span class="nt">template</span><span class="p">></span> <span class="p"><</span><span class="nt">script</span><span class="p">></span> export default <span class="si">{</span> <span class="nx">mounted</span><span class="p">(){</span> <span class="k">this</span><span class="p">.</span><span class="nx">handlePageScroll</span><span class="p">()</span> <span class="p">},</span> <span class="nx">methods</span><span class="p">:{</span> <span class="nx">handlePageScroll</span><span class="p">(){</span> <span class="nb">window</span><span class="p">.</span><span class="nx">scrollTo</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="k">this</span><span class="p">.</span><span class="nx">scrollposition</span><span class="p">);</span> <span class="k">this</span><span class="p">.</span><span class="nx">scrollposition</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">pageYOffset</span><span class="p">;</span> <span class="p">}</span> <span class="si">}</span> } <span class="p"></</span><span class="nt">script</span><span class="p">></span> |
You import the Products
and FooterComponent
components inside this file and display them on the Homepage.
Products Page View
Create the file src/views/ProductView.vue
with the following content:
1 2 3 4 5 6 7 |
<span class="p"><</span><span class="nt">script</span> <span class="na">setup</span><span class="p">></span> import PageHeader from '../components/PageHeader.vue' import Products from '../components/Products.vue' import FooterComponent from '../components/FooterComponent.vue' <span class="p"></</span><span class="nt">script</span><span class="p">></span> <span class="p"><</span><span class="nt">template</span><span class="p">></span> <span class="p"><</span><span class="nt">main</span><span class="p">></span> <span class="p"><</span><span class="nt">products</span><span class="p">/></span> <span class="p"><</span><span class="nt">footer</span><span class="err">-</span><span class="na">component</span><span class="p">/></span> <span class="p"></</span><span class="nt">main</span><span class="p">></span> <span class="p"></</span><span class="nt">template</span><span class="p">></span> |
You also import the Products
and FooterComponent
components inside this file to display them in the Products listing page.
Single Product Page
Create the file src/views/SingleProductView.vue
with the following content:
1 2 3 4 5 6 7 8 |
<span class="p"><</span><span class="nt">script</span> <span class="na">setup</span><span class="p">></span> import FooterComponent from '../components/FooterComponent.vue' <span class="p"></</span><span class="nt">script</span><span class="p">></span> <span class="p"><</span><span class="nt">template</span><span class="p">></span> <span class="p"><</span><span class="nt">main</span> <span class="na">ref</span><span class="p">=</span><span class="s">"addProd"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">'py-20 px-4'</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">'text-white max-w-6xl mx-auto py-2'</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">'grid md:grid-cols-2 gap-20 grid-cols-1'</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">''</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"relative"</span><span class="p">></span> <span class="p"><</span><span class="nt">img</span> <span class="err">:</span><span class="na">src</span><span class="p">=</span><span class="s">"imgArr[currentImg]"</span> <span class="na">alt</span><span class="p">=</span><span class="s">"no image"</span> <span class="p">/></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"absolute overflow-x-scroll w-full bottom-0 right-0 p-4 flex flex-nowrap gap-4"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">v-for</span><span class="p">=</span><span class="s">"(er,i) in imgArr.length"</span> <span class="err">:</span><span class="na">key</span><span class="p">=</span><span class="s">"i"</span> <span class="na">class</span><span class="p">=</span><span class="s">"flex w-full flex-nowrap gap-4 rounded-lg"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="err">@</span><span class="na">click</span><span class="p">=</span><span class="s">"currentImg = i"</span> <span class="err">:</span><span class="na">title</span><span class="p">=</span><span class="s">"imgArr[i]"</span> <span class="na">class</span><span class="p">=</span><span class="s">"w-16 h-24 flex-none"</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">"h-full w-full rounded-lg cursor-pointer shadow-lg border overflow-hidden"</span><span class="p">></span> <span class="p"><</span><span class="nt">img</span> <span class="err">:</span><span class="na">src</span><span class="p">=</span><span class="s">"imgArr[i]"</span> <span class="na">alt</span><span class="p">=</span><span class="s">""</span> <span class="na">class</span><span class="p">=</span><span class="s">"h-full w-full"</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">''</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">'flex md:flex-col flex-col space-y-7 justify-center'</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">'text-black space-y-3'</span><span class="p">></span> <span class="p"><</span><span class="nt">h2</span> <span class="na">class</span><span class="p">=</span><span class="s">'font-bold text-xl text-black'</span><span class="p">></span><span class="si">{</span><span class="p">{</span><span class="nx">product</span><span class="p">.</span><span class="nx">title</span><span class="p">}</span><span class="si">}</span><span class="p"></</span><span class="nt">h2</span><span class="p">></span> <span class="p"><</span><span class="nt">p</span> <span class="na">class</span><span class="p">=</span><span class="s">'text-sm'</span><span class="p">></span><span class="si">{</span><span class="p">{</span><span class="nx">product</span><span class="p">.</span><span class="nx">description</span><span class="p">}</span><span class="si">}</span><span class="p"></</span><span class="nt">p</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">'space-y-3'</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">'font-bold text-md text-black'</span><span class="p">></span>Select Size<span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">'flex flex-row flex-wrap gap-4'</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">v-for</span><span class="p">=</span><span class="s">"(size,i) in sizes"</span> <span class="err">:</span><span class="na">key</span><span class="p">=</span><span class="s">"i"</span> <span class="na">class</span><span class="p">=</span><span class="s">""</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="err">@</span><span class="na">click</span><span class="p">=</span><span class="s">"currentSize = size ; currentPrice = priceList[i] ; variants_id = product.variants[i].id"</span> <span class="err">:</span><span class="na">class</span><span class="p">=</span><span class="s">"currentSize == size ? 'border-purple-300 bg-purple-100':'border-gray-100' "</span> <span class="na">contenteditable</span><span class="p">=</span><span class="s">"false"</span> <span class="na">class</span><span class="p">=</span><span class="s">'border-2 rounded-md cursor-pointer flex justify-center py-3 px-5'</span><span class="p">></span> <span class="p"><</span><span class="nt">span</span> <span class="na">class</span><span class="p">=</span><span class="s">' text-black text-sm'</span><span class="p">></span><span class="si">{</span><span class="p">{</span><span class="nx">size</span><span class="p">}</span><span class="si">}</span><span class="p"></</span><span class="nt">span</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="p">=</span><span class="s">'flex flex-col space-y-3'</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">span</span> <span class="na">class</span><span class="p">=</span><span class="s">'text-gray-700 text-2xl font-san'</span><span class="p">></span><span class="ni">&euro;</span> <span class="si">{</span><span class="p">{</span> <span class="nx">currentPrice</span> <span class="p">}</span><span class="si">}</span><span class="p"></</span><span class="nt">span</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">div</span> <span class="err">@</span><span class="na">click</span><span class="p">=</span><span class="s">"addProduct()"</span> <span class="na">class</span><span class="p">=</span><span class="s">'bg-gray-900 cursor-pointer text-white w-full text-sm font-semibold py-3 flex justify-center cursor pointer hover:bg-white hover:border hover:border-gray-900 hover:text-black '</span><span class="p">></span> ADD TO CART <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"></</span><span class="nt">div</span><span class="p">></span> <span class="p"><</span><span class="nt">footer</span><span class="err">-</span><span class="na">component</span><span class="p">/></span> <span class="p"></</span><span class="nt">main</span><span class="p">></span> <span class="p"></</span><span class="nt">template</span><span class="p">></span> <span class="p"><</span><span class="nt">script</span><span class="p">></span> import axios from 'axios' export default <span class="si">{</span> <span class="nx">activated</span><span class="p">()</span> <span class="p">{</span> <span class="nb">window</span><span class="p">.</span><span class="nx">scrollTo</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span> <span class="p">},</span> <span class="nx">data</span><span class="p">(){</span> <span class="k">return</span><span class="p">{</span> <span class="na">currentImg</span> <span class="p">:</span> <span class="mi">0</span><span class="p">,</span> <span class="na">imgArr</span><span class="p">:[],</span> <span class="na">productData</span><span class="p">:[],</span> <span class="na">product</span><span class="p">:[],</span> <span class="na">productId</span><span class="p">:</span><span class="dl">''</span><span class="p">,</span> <span class="na">sizes</span><span class="p">:[],</span> <span class="na">currentSize</span><span class="p">:</span><span class="dl">'</span><span class="s1">S</span><span class="dl">'</span><span class="p">,</span> <span class="na">priceList</span><span class="p">:[],</span> <span class="na">currentPrice</span><span class="p">:</span> <span class="dl">''</span><span class="p">,</span> <span class="na">variants_id</span><span class="p">:</span> <span class="mi">0</span> <span class="p">}</span> <span class="p">},</span> <span class="nx">mounted</span><span class="p">(){</span> <span class="nb">window</span><span class="p">.</span><span class="nx">scrollTo</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">);</span> <span class="p">},</span> <span class="nx">beforeMount</span><span class="p">(){</span> <span class="k">this</span><span class="p">.</span><span class="nx">productId</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">$route</span><span class="p">.</span><span class="nx">params</span><span class="p">.</span><span class="nx">id</span> <span class="nx">axios</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="s2">`</span><span class="p">${</span><span class="k">import</span><span class="p">.</span><span class="nx">meta</span><span class="p">.</span><span class="nx">env</span><span class="p">.</span><span class="nx">VITE_baseUrl</span><span class="p">}</span><span class="s2">/store/products/</span><span class="p">${</span><span class="k">this</span><span class="p">.</span><span class="nx">productId</span><span class="p">}</span><span class="s2">`</span><span class="p">)</span> <span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">productData</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="nx">product</span> <span class="o">=</span> <span class="nx">productData</span><span class="p">.</span><span class="nx">data</span><span class="p">.</span><span class="nx">product</span><span class="p">;</span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nx">product</span><span class="p">);</span> <span class="k">this</span><span class="p">.</span><span class="nx">product</span><span class="p">.</span><span class="nx">images</span><span class="p">.</span><span class="nx">forEach</span><span class="p">(</span><span class="nx">data</span> <span class="o">=></span> <span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="nx">imgArr</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">url</span><span class="p">)</span> <span class="p">});</span> <span class="k">this</span><span class="p">.</span><span class="nx">currentPrice</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">product</span><span class="p">.</span><span class="nx">variants</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">prices</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">amount</span> <span class="o">/</span> <span class="mi">100</span> <span class="k">this</span><span class="p">.</span><span class="nx">variants_id</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">product</span><span class="p">.</span><span class="nx">variants</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">id</span> <span class="k">this</span><span class="p">.</span><span class="nx">product</span><span class="p">.</span><span class="nx">variants</span><span class="p">.</span><span class="nx">forEach</span><span class="p">(</span><span class="nx">data</span> <span class="o">=></span> <span class="p">{</span> <span class="k">this</span><span class="p">.</span><span class="nx">sizes</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">title</span><span class="p">)</span> <span class="k">this</span><span class="p">.</span><span class="nx">priceList</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">data</span><span class="p">.</span><span class="nx">prices</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="nx">amount</span> <span class="o">/</span> <span class="mi">100</span><span class="p">)</span> <span class="p">});</span> <span class="p">}).</span><span class="k">catch</span><span class="p">(</span><span class="nx">err</span> <span class="o">=></span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">err</span><span class="p">));</span> <span class="p">},</span> <span class="nx">methods</span><span class="p">:{</span> <span class="nx">addProduct</span><span class="p">(){</span> <span class="k">this</span><span class="p">.</span><span class="nx">$emit</span><span class="p">(</span><span class="dl">'</span><span class="s1">addP</span><span class="dl">'</span><span class="p">,</span><span class="k">this</span><span class="p">.</span><span class="nx">variants_id</span><span class="p">)</span> <span class="p">}</span> <span class="si">}</span> } <span class="p"></</span><span class="nt">script</span><span class="p">></span> |
In the above code block, you are fetching a single product from the Medusa server using its id. Then, you display the product’s information including its price, size, and description.
When the Add to Cart button is clicked, the addProduct
method is executed. This method calls the addP
method defined in src/App.vue
passing it the selected variant ID.
Change Vue.js Port
In the vite.config.js
file, add this code snippet inside the object parameter passed todefineConfig
:
1 2 3 |
<span class="nx">server</span><span class="p">:</span> <span class="p">{</span> <span class="nl">port</span><span class="p">:</span><span class="mi">8000</span> <span class="p">},</span> |
In the above code block, you change the port of the Vue.js ecommerce storefront to 8000
. This allows you to avoid cors
issues when sending requests to the Medusa server.
Test the Storefront
You can now test your Vue.js ecommerce storefront with the following steps:
- Run the Medusa server:
1 2 |
medusa develop |
- Run the Vue.js app:
1 2 |
npm run dev |
When you open the storefront on localhost:8000
, you’ll see the following home page:
If you scroll down or click on the Products item in the navigation bar, you’ll see products populated from your Medusa server.
Try clicking on any of the products, a new page will open showing the products details.
You can add the product to the cart by clicking the “Add to Cart” button.
What’s Next?
In this tutorial, you learned how to create a Vue.js Ecommerce storefront with the help of Medusa. This storefront only implements the product listing and add-to-cart functionality, but there’s much more to add to your storefront.
Check out the following documentation pages for help on how to move forward:
Should you have any issues or questions related to Medusa, then feel free to reach out to the Medusa team via Discord
Source: https://dev.to/medusajs/how-i-created-a-vuejs-ecommerce-store-with-medusa-plf
