Check this post in my web notes!
And the final result is over here!
We continue building our E-commerce platform with Nuxt.js. In a previous article, we finished with the main layout, header, and footer, so in this article, we will jump into crafting HTML/CSS templates for our pages like Shop, Cart, Product, Checkout, and so on. Grab a fresh cup of coffee because we're jumping on an extensive journey ahead.
- Building reusable components
- Crafting the Main (Landing) Page
- Expanding on the Shop Page: Implementing Products Table and Filters
- Developing the Dynamic Product Page
- Wrapping Up with the Cart Page and Checkout Process
As I mentioned before we will work on pushing our design template to life, so if you have your design you can skip this part, or just get the code here.
1. Building reusable components
First of all, we have to check the whole template and find some components that are used many times or on different pages. In my case "Breadcrumbs" and "Product Card" appear on every page, so I recommend you to build them first. For that purpose let's create a "common" folder inside the "components" folder, and first add the "Breadcrumbs.vue" file with the necessary code:
<template>
<div class="breadcrumbs">
<h1 class="breadcrumbs__title">Shop</h1>
<ul class="breadcrumbs__list">
<li class="breadcrumbs__list--item">
<NuxtLink to="/" class="breadcrumbs--link">
Home
</NuxtLink>
</li>
<li class="breadcrumbs__list--item"> / </li>
<li>
<NuxtLink to="/shop" class="breadcrumbs--link">
Shop
</NuxtLink>
</li>
<li class="breadcrumbs__list--item"> / </li>
<li>
<NuxtLink to="/shop" class="breadcrumbs--link">
Products
</NuxtLink>
</li>
</ul>
</div>
</template>
<script>
export default {
name: "Breadcrumbs"
}
</script>
It's a statical component because as you remember we will only move design to life without scripts.
Also, I can not add here whole CSS rules here because the article would be too large, I recommend you practice on your own, but if you need to check the project code you can get it over here.
Great, next with the same manner we will create a "ProductCard.vue" file with all HTML/CSS code. Our Product Card will contain with main image as a link to the "Product Page", product name, price, and buttons: add to cart, add to wishlist, and fast view (that will open a modal window with product description). Also, let's add a "product" prop, that will receive each product from the parent component.
<template>
<div class="product-card">
<div class="product-card__top">
<NuxtLink to="/shop/product" class="product-card__top--link">
<img src="../../assets/images/main/trend.jpg" alt="product image">
</NuxtLink>
<div class="product-card__top--overlay">
<ul class="overlay__list">
<li class="overlay__list--item icon-left">
<NuxtIcon name="heart-regular" size="20" class="overlay__list--icon"/>
</li>
<li class="overlay__list--item">
<button class="overlay__list--button">Add to cart</button>
</li>
<li class="overlay__list--item icon-right">
<NuxtIcon name="eye-regular" size="20" class="overlay__list--icon"/>
</li>
</ul>
</div>
</div>
<div class="product-card__bottom">
<NuxtLink to="/" class="product-card__bottom--link">
<h6 class="product-card__bottom--text">{{ product.name }}</h6>
<p class="product-card__bottom--price">{{ product.price }} $</p>
</NuxtLink>
</div>
</div>
</template>
<script>
export default {
name: "ProductCard",
props: {
product: {
type: Object,
required: true
}
}
}
</script>
Nice, we have both reusable components ready, it's okay if we would add new components over here while developing our e-commerce store.
2. Crafting the Main (Landing) Page
In this part we will build the Main Store Page, or welcome page, it's the first page that our customers will see when they visit our store, so we will add some banners and the most popular products.
We already have a header so let's visually separate our template into four (in my case) sections:
Hero banner, also could be a slider, that will call to action, like start browsing store or something like that;
Categories section, separated by our product categories, each category will be a link to our shop with specific already set filters;
Trending products, there will be popular products that our store would like to sell the most;
Subscribe section, where we will try to move our first visited users to permanent users.
In our template we will do the same, separate whole page into sections and build them separately:
<template>
<main class="main">
<section class="main__hero">
<div class="main__hero--left">
<p class="text">NEW INSPIRATION 2020</p>
<h1 class="title">20% OFF ON NEW SEASON</h1>
<NuxtLink to="/" class="link">Browse Collection</NuxtLink>
</div>
<div class="main__hero--right"></div>
</section>
<section class="main__categories">
<header class="main__categories--header">
<p class="text">CAREFULLY CREATED COLLECTIONS</p>
<h2 class="title">BROWSE OUR CATEGORIES</h2>
</header>
<div class="main__categories--content">
<div class="categories-item">
<NuxtLink to="/" class="categories-item--link">
<img src="../assets/images/main/cat-first-left.jpg" alt="category image" class="categories-item--image">
<p class="categories-item--text">Clothes</p>
</NuxtLink>
</div>
... // more categories
<div class="categories-item">
<NuxtLink to="/" class="categories-item--link">
<img src="../assets/images/main/cat-right.jpg" alt="category image" class="categories-item--image">
<p class="categories-item--text">Electronics</p>
</NuxtLink>
</div>
</div>
</section>
<section class="main__trends">
<header class="main__trends--header">
<p class="text">MADE THE HARD WAY</p>
<h2 class="title">TOP TRENDING PRODUCTS</h2>
</header>
<div class="main__trends--content">
<ProductCard v-for="product in trendProducts" :key="product.name" :product="product" />
</div>
</section>
<section class="main__friends">
<div class="main__friends--left">
<h6 class="title">LET'S BE FRIENDS!</h6>
<p class="text">Subscribe To Our Newsletter</p>
</div>
<div class="main__friends--right">
<div class="input-group">
<input v-model="email" type="email" class="input" id="Email" name="Email" placeholder="Enter your email" autocomplete="off">
<input class="button--submit" value="Subscribe" type="submit" @click.prevent="subscribe">
</div>
</div>
</section>
</main>
</template>
<script>
import ProductCard from "../components/common/ProductCard.vue";
export default {
name: "Main",
components: {
ProductCard
},
data() {
return {
trendProducts: [
{
name: "Product 1",
price: 100,
},
...
]
}
},
}
</script>
Now we can restart our dev server and check the result:
3. Expanding on the Shop Page: Implementing Products Table and Filters
I think that this will be the worst page in the future, because we will try to implement as many filters as we can imagine, and I hate filters ))))
But for now, we will create a new folder "shop" in the "pages" directory, shop also will be our new route, and inside that folder create an "index.vue" file. We need simply a separate page on the left side for filters, and the right side with a products table.
I'm offering you to use the "grid" system because it will allow you to build simple tables and make them responsive in the future:
<template>
<main class="shop">
<Breadcrumbs/>
<div class="shop__content">
<section class="shop__content--filters">
<h5 class="title">CATEGORIES</h5>
<ul class="list">
<li class="list--item">
<NuxtLink to="/shop" class="link">All</NuxtLink>
</li>
<li class="list--item">
<NuxtLink to="/shop" class="link">Clothes</NuxtLink>
</li>
<li class="list--item">
<NuxtLink to="/shop" class="link">Shoes</NuxtLink>
</li>
<li class="list--item">
<NuxtLink to="/shop" class="link">Watches</NuxtLink>
</li>
<li class="list--item">
<NuxtLink to="/shop" class="link">Electronics</NuxtLink>
</li>
</ul>
</section>
<section class="shop__content--products">
<div class="products__header">
<div class="products__header--text">Showing 1-12 of 24 results</div>
<div class="products__header--options">
<div class="products__header--filters">
<button class="products__filters--btn">
<NuxtIcon name="filter-solid" />
Filters
</button>
</div>
<div class="products__header--select">
<p>Sort by</p>
<select>
<option selected value="0">Popularity</option>
<option value="1">Price: Low to High</option>
<option value="2">Price: High to Low</option>
</select>
</div>
</div>
</div>
<div class="products__content">
<ProductCard v-for="product in trendProducts" :key="product.name" :product="product" />
</div>
</section>
</div>
</main>
</template>
<script>
import Breadcrumbs from "../../components/common/Breadcrumbs.vue";
import ProductCard from "../../components/common/ProductCard.vue";
export default {
name: "Shop",
components: {
Breadcrumbs,
ProductCard
},
data() {
return {
trendProducts: [
{
name: "Product 1",
price: 100,
},
...
]
}
}
}
</script>
4. Developing the Dynamic Product Page
Why Dynamic Page? Because we will not create a separate page for each product, we will create a dynamic page that will receive something like a product ID from the URL and Nuxt.js will fetch data about that product from the database and render separate pages to each product automatically. But it's not our topic for today, we will simply add a product page with static data and later add the functionality that we mentioned.
Create a new "[productId]" folder inside the shop folder and add the "index.vue" file. Those brackets needed to say Nuxt that it will be a dynamic route and we will store route params inside the "productId" key.
Our page will simply separate into two parts with images and product information, and under that, we will add tabs (simple two buttons that will rerender the div section) that will show additional product information and a section with trends.
<template>
<main class="product">
<section class="product-info">
<div class="product-info__image">
<div class="product-info__image--list">
<ul class="list">
<li class="list__item">
<img src="../../../assets/images/main/trend.jpg" alt="product image">
</li>
<li class="list__item">
<img src="../../../assets/images/main/trend.jpg" alt="product image">
</li>
<li class="list__item">
<img src="../../../assets/images/main/trend.jpg" alt="product image">
</li>
<li class="list__item">
<img src="../../../assets/images/main/trend.jpg" alt="product image">
</li>
</ul>
</div>
<div class="product-info__image--main">
<img src="../../../assets/images/main/trend.jpg" alt="product image">
</div>
</div>
<div class="product-info__content">
<h1 class="product-info__content--title">Product Title</h1>
<p class="product-info__content--price">100$</p>
<p class="product-info__content--description">Lorem ipsum dolor sit amet, consectetur adipiscing elit. In ut ullamcorper leo, eget euismod orci. Cum sociis natoque penatibus et magnis dis parturient montes nascetur ridiculus mus. Vestibulum ultricies aliquam convallis.</p>
<div class="product-info__content--quantity">
<div class="quantity-input">
<button class="quantity-button" @click="decreaseQuantity">-</button>
<input
type="number"
class="quantity-input__field"
v-model="quantity"
min="0"
/>
<button class="quantity-button" @click="increaseQuantity">+</button>
</div>
<button class="button--add-to-cart">Add to cart</button>
</div>
<div class="product-info__content--wishlist">
<button>
<NuxtIcon name="heart-regular" size="20" class="overlay__list--icon"/>
<p>Add to wishlist</p>
</button>
</div>
<div class="product-info__content--socials">
<h5>Check New Arrivals on:</h5>
<div class="socials">
<a class="socials__item" href="#">
<img src="../../../assets/images/product/instagram.png" alt="social image" class="socials__item--image">
<span>Instagram</span>
</a>
<a class="socials__item" href="#">
<img src="../../../assets/images/product/pinterest.png" alt="social image" class="socials__item--image">
<span>Pinterest</span>
</a>
</div>
</div>
</div>
</section>
<section class="product-details">
<div class="tabs">
<div class="tabs__header">
<button
v-for="(tab, index) in tabs"
:key="index"
class="tab-button"
:class="{ active: activeTab === index }"
@click="setActiveTab(index)"
>
{{ tab }}
</button>
</div>
<div class="tabs__content">
<div v-show="activeTab === 0">
<p>Lorem ipsum dolor, sit amet consectetur adipisicing elit. Perferendis, saepe? Blanditiis facilis quae cumque quo repellat excepturi aperiam dicta fugiat, cum ex atque. Impedit ipsum, saepe perferendis delectus omnis deserunt.</p>
</div>
<div v-show="activeTab === 1">
<p>Content for Tab 2...</p>
</div>
</div>
</div>
</section>
<section class="product-trends">
<ProductCard v-for="product in trendProducts" :key="product.name" :product="product" />
</section>
</main>
</template>
<script>
import ProductCard from "../components/common/ProductCard.vue";
export default defineNuxtComponent({
name: "ProductPage",
components: {
ProductCard
},
data() {
return {
quantity: 1,
tabs: ['Description', 'Reviews'],
activeTab: 0,
trendProducts: [
{
name: "Product 1",
price: 100,
},
...
]
}
},
methods: {
increaseQuantity() {
this.quantity++
},
decreaseQuantity() {
if (this.quantity > 0) {
this.quantity--
}
},
setActiveTab(index) {
this.activeTab = index;
},
}
})
</script>
And here how this plage will look like:
5. Wrapping Up with the Cart Page and Checkout Process
And the final part of today's article we will work on the Cart Page and talk about the Checkout Process.
So, what should be on the Cart Page? Probably items table with the list of products that we would like to buy and "total" window that will count and show the sum of money that we have to pay, and a button that will send us to the checkout page.
Let's create a new "cart" folder inside the "pages" directory and add an index.vue file. Inside that file start constructing the "Cart Page", we will separate the page on the left and right parts and render the table on the left side and sum window on the right side:
<template>
<main class="cart">
<Breadcrumbs />
<h2 class="cart__title">Shopping Cart</h2>
<section class="cart__content">
<div class="cart__content--table">
<div class="table-header">
<ul class="table-header__list">
<li class="table-header__list--item product">Product</li>
<li class="table-header__list--item price">Price</li>
<li class="table-header__list--item quantity">Quantity</li>
<li class="table-header__list--item total">Total</li>
<li class="table-header__list--item remove"></li>
</ul>
</div>
<div class="table-body">
<ul class="table-body__list">
<li class="table-body__list--item product">
<NuxtLink to="/shop/product" class="table-body__list--link">
<img src="../../assets/images/main/trend.jpg" alt="product image">
</NuxtLink>
<NuxtLink to="/shop/product" class="table-body__list--link">
Lorem ipsum dolor sit amet.
</NuxtLink>
</li>
<li class="table-body__list--item price">100$</li>
<li class="table-body__list--item quantity">1</li>
<li class="table-body__list--item total">100$</li>
<li class="table-body__list--item remove">
<button class="table-body__list--item--btn">
<NuxtIcon name="trash-can-solid" size="20" class="table-body__list--icon"/>
</button>
</li>
</ul>
</div>
</div>
<div class="cart__content--summary">
<h5>Cart Total</h5>
<p class="subtotal">Subtotal: <span>200$</span></p>
<p class="total">Total: <span>200$</span></p>
<button class="cart__content--summary--btn" @click="$router.push('/checkout')">Proceed to Checkout</button>
</div>
</section>
<section class="cart__footer">
<button class="cart__footer--btn">
<NuxtIcon name="arrow-left-long-solid" size="20" class="cart__footer--icon"/>
Continue Shopping
</button>
<button class="cart__footer--btn" @click="$router.push('/checkout')">
Checkout
<NuxtIcon name="arrow-right-long-solid" size="20" class="cart__footer--icon"/>
</button>
</section>
</main>
</template>
<script>
import Breadcrumbs from "../../components/common/Breadcrumbs.vue";
export default {
name: "Cart",
components: {
Breadcrumbs
}
}
</script>
Great, and restart our dev server and check the result:
Our checkout page will simply represent a list of inputs that will accept users' data before making a sale, so I think it will be better to add this page when we finish our selling process, or you can add this page on your own, it would be a great opportunity to get some practice.
In this article, we have successfully crafted the core pages of our e-commerce platform using Nuxt.js and HTML/CSS templates. We started by building reusable components like Breadcrumbs and ProductCard that can be utilized across multiple pages. Then, we designed and developed the Main (Landing) Page, the Shop Page with product listing and filters, the Dynamic Product Page with detailed information, and the Cart Page.
While these pages provide the essential structure and visual representation of our e-commerce store, they currently lack functionality and interactivity. In the upcoming articles, we will focus on integrating the necessary logic and functionality to bring these pages to life. This includes implementing features such as product filtering, sorting, adding to cart, the checkout process, and integrating with a backend for data management.
Additionally, we will explore further enhancements and optimizations to improve the overall user experience, performance, and responsiveness of our e-commerce platform. Stay tuned for more exciting developments as we continue to build a robust and feature-rich e-commerce solution with Nuxt.js.
The best variant to study something is to make it by your own, the same thing works with coding, but if you need a source code for this tutorial you can get it here.
Top comments (0)