One of the super-powers of modern e-commerce applications is a meaningful user experience that allows customers to demand tailor-made products and the flexibility to customize such products.
This post will discuss how to build a customizable product component using Nuxt.js and Cloudinary. At the end of this tutorial, we will learn how to use Cloudinary robust transformation properties to enhance product customization and experience.
Nuxt.js is a vue-based frontend development framework that enables functionalities like server-side rendering, static site generation, file-system routing, components auto-import, and API endpoints for backend features.
Cloudinary is a visual media service we use to upload, store, manage, transform, and deliver images and videos for websites and applications. The platform also offers a vast collection of software development kits (SDKs) for frontend frameworks and libraries.
We completed this project in a CodeSandbox, and you can fork it to run the code.
Github link here.
The following steps in this post require JavaScript and Vue.js experience. Experience with Nuxt.js isn’t a requirement, but it’s nice to have.
We also need a Cloudinary account to store and transform media assets. Signup is completely free.
We need to create a Nuxt.js starter project by navigating to the desired directory and running the command below in our terminal.
npx create-nuxt-app e_corm_product && cd e_corm_product
This command will ask us some questions on how to configure our application. We can answer the questions as shown below:
Ok to proceed? (y) < PRESS "y" and hit ENTER>
project name: <PRESS ENTER>
programming langauge: <JAVASCRIPT>
package manager: <NPM>
UI framework: <TAILWIND CSS>
Nuxt.js modules: <AXIOS - PROMISE BASED HTTP CLIENT>
Linting tools: <ESLINT, PRETTIER>
Testing framework: <NONE>
Rendering mode: <UNIVERSAL (SSR/STATIC)
Deployment target: <STATIC/JAMSTACK HOSTING>
Deployment tools: <JSCONFIG.JSON>
Continous integration: <NONE>
Version control system: <GIT>
Code language: PHP (php)
The command creates a Nuxt.js project with TailwindCSS called e_corm_product
, and navigates into the project directory.
TailwindCSS is a utility-first CSS framework packed with classes to help us style our web page.
We proceed to install the @nuxtjs/cloudinary dependency with:
npm install @nuxtjs/cloudinary
Configuring Cloudinary in Nuxt.js
First, we need to modify the nuxt.config.js
file by adding @nuxtjs/cloudinary
as a module in the modules
section:
modules: [
'@nuxtjs/cloudinary', //add this
],
Code language: JavaScript (javascript)
Next, we need to configure Cloudinary by adding a cloudinary
section below the modules
section as shown below:
modules: [
'@nuxtjs/cloudinary',
],
//add this
cloudinary: {
cloudName: '<your-cloud-name>',
useComponent: true,
},
Code language: JavaScript (javascript)
The useComponent
flag set to true
lets us use the built-in Cloudinary components. Our cloud name is obtained from our Cloudinary dashboard.
Next, we need to upload sample images for products and overlays to create product customization.
Product Image
Overlay Images
- https://res.cloudinary.com/dtgbzmpca/image/upload/v1642367747/cloudinary-logo.jpg
- https://res.cloudinary.com/dtgbzmpca/image/upload/v1642367682/fire.png
In our Cloudinary dashboard, we uploaded the images by clicking on the Media Library tab, clicking on Upload, selecting Web Address option, input the url, and clicking on the Arrow Button to upload.
After uploading all the images, we will see them displayed on the console with their publicId
. The IDs will come in handy when creating product customization.
Next, we need to create a utils
folder in the project root directory. In this folder, we create availableColors.json
, fontList.json
, and imageList.json
. These files contain data for colors, fonts, and images respectively.
Here is the JSON data for each file.
availableColors.json
["FFFFFF", "47E8D2", "DCA381", "702C3C", "E9C660", "A11D1F", "897115", "598DE6"]
fontList.json
[
{ "title": "Arial", "value": "Arial" },
{ "title": "Georgia", "value": "Georgia" },
{ "title": "Sacremento", "value": "Sacramento" },
{ "title": "Roboto", "value": "Roboto" },
{ "title": "Montserrat", "value": "Montserrat" },
{ "title": "Bitter", "value": "Bitter" }
]
imageList.json
[
{
"id": 1,
"publicId": ""
},
{
"id": 2,
"publicId": "<REPLACE THIS WITH YOUR OVERLAY IMAGE PUBLICID>"
},
{
"id": 3,
"publicId": "<REPLACE THIS WITH YOUR OVERLAY IMAGE PUBLICID>"
}
]
PS: The first item’s **publicId**
is left empty because it serves as the default state when no overlay is required.
Next, we need to create components that will serve as the building blocks of our application:
-
ColorPicker
a component for sets of predefined colors. -
ColorDropPicker
a component for picking a custom colour for our product. -
ImagePicker
a component for picking image. -
TextCustomPicker
a component for selecting fonts and adding custom text. -
ProductImage
a component for rendering customized product.
ColorPicker Component
To do this, we navigate to the components
folder, and in this folder, create a ColorPicker.vue
file containing the code snippet below:
<template>
<div class="w-full mb-6">
<h4 class="mb-4 font-semibold">Select a color:</h4>
<div class="flex">
<button
v-for="(color, i) in availableColors"
class="w-8 h-8 mr-2 block"
:class="[
i === 0 ? 'border' : '',
selectedColor === color ? 'border-4 border-red-400' : '',
]"
:key="i"
:style="{ background: `#${color}` }"
@click="setActiveColor(color)"
></button>
</div>
</div>
</template>
<script>
export default {
props: {
availableColors: { type: Array, required: true },
setActiveColor: { type: Function, required: true },
selectedColor: { type: String, required: true },
},
}
</script>
Code language: HTML, XML (xml)
The snippet above does the following:
- Configures the component to accept
availableColors
,setActiveColor
, andselectedColor
props. - Markup shows the list of the available colours, sets the first item as active by default, and a click event to change the active colour.
ColorDropPicker Component
In the same components
folder, create a ColorDropPicker.vue
file containing the code snippet below:
<template>
<div class="w-full mb-6">
<h4 class="mb-2 font-semibold">Add a custom color:</h4>
<input
type="color"
name=""
id=""
class="h-9 w-20 border-2 cursor-pointer"
@change="onChange"
/>
</div>
</template>
<script>
export default {
props: {
setActiveColor: { type: Function, required: true },
},
methods: {
onChange(event) {
this.setActiveColor(event.target.value.slice(1))
},
},
}
</script>
Code language: HTML, XML (xml)
The snippet above does the following:
- Configures the component to accept
setActiveColor
props. - Creates an
onChange
method that uses thesectActiveColor
props to change the colour. This method also removes the first letter from the generated colour. E.g., colour#ffffff
will return asffffff
. - Markup shows an input field of type
color
and achange
event to select a custom colour.
ImagePicker Component
In the same components
folder, create an ImagePicker.vue
file containing the code snippet below:
<template>
<div class="w-full mb-6">
<h4 class="mb-4 font-semibold">Add an image:</h4>
<div class="flex">
<div
v-for="image in imageList"
:key="image.id"
class="border-2 cursor-pointer mr-2"
:class="[
image.publicId === selectedImage ? 'border-red-400 border-4' : '',
]"
@click="setActiveImage(image.publicId)"
>
<div v-if="image.id === 1" class="w-11 h-11 bg-white"></div>
<cld-image v-else :public-id="image.publicId">
<cld-transformation width="45" height="45" crop="thumb" />
</cld-image>
</div>
</div>
</div>
</template>
<script>
export default {
props: {
imageList: { type: Array, required: true },
setActiveImage: { type: Function, required: true },
selectedImage: { type: String, required: true },
},
}
</script>
Code language: HTML, XML (xml)
The snippet above does the following:
- Configures the component to accept
imageList
,setActiveImage
, andselectedImage
props. - Markup shows the list of the available images using Cloudinary component, sets the first item as active by default, and a click event to change the active image.
TextCustomPicker Component
In the same components
folder, create an TextCustomPicker.vue
file containing the code snippet below:
<template>
<div class="w-full mb-6">
<h4 class="mb-4 font-semibold">Add a text:</h4>
<form @submit.prevent="onSubmit">
<select
v-model="activeFont"
@change="onChange($event)"
name="font-picker"
class="h-10 w-32 mb-3 border rounded"
>
<option v-for="(font, i) in fontList" :key="i" :value="font.value">
{{ font.title }}
</option>
</select>
<div class="flex">
<input
type="text"
placeholder="enter text"
required
class="h-10 w-60 mr-2 pl-2 border rounded"
v-model="text"
/>
<button class="px-4 bg-indigo-600 text-white rounded">Add Text</button>
</div>
</form>
</div>
</template>
<script>
export default {
props: {
fontList: { type: Array, required: true },
setActiveFont: { type: Function, required: true },
setText: { type: Function, required: true },
selectedFont: { type: String, required: true },
},
data() {
return {
activeFont: '',
text: '',
}
},
methods: {
onChange(event) {
this.setActiveFont(event.target.value)
},
onSubmit() {
this.setText(this.text)
},
},
mounted() {
this.activeFont = this.selectedFont
},
}
</script>
Code language: HTML, XML (xml)
The snippet above does the following:
- Configures the component to accept
fontList
,setActiveFont
,setText
, andselectedFont
props. - Create
data
properties to manage active font and custom text. - Create
onChange
andonSubmit
methods to change the active font and the custom text. - Use the
mounted
lifecycle to set the active font when the component is rendered. - Markup shows the list of the available fonts and a form to submit custom text changes.
ProductImage Component
In the same components
folder, create an ProductImage.vue
file containing the code snippet below:
<template>
<div class="lg:h-96">
<cld-image public-id="Hanging_T-Shirt_v83je9" width="500">
<cld-transformation :effect="`replace_color:${selectedColor}`" />
<cld-transformation :overlay="selectedImage" fetch-format="auto" width="110" />
<cld-transformation
:overlay="{
fontFamily: selectedFont,
fontSize: 33,
fontWeight: 'bold',
text: customText,
}"
gravity="center"
y="0.1"
/>
</cld-image>
</div>
</template>
<script>
export default {
props: {
selectedColor: { type: String, required: true },
selectedImage: { type: String, required: true },
selectedFont: { type: String, required: true },
customText: { type: String, required: true },
},
}
</script>
Code language: HTML, XML (xml)
The snippet above does the following:
- Configures the component to accept
selectedColor
,selectedImage
,selectedFont
andcustomText
props. - Configure
cld-image
andcld-transformation
s to render the product image, overlay, and text. Thepublic-id
specified(Hanging_T-Shirt_v83je9) is for the product image we uploaded earlier. We also leverage Cloudinary’s support for multiple transformations to transform the image. We added the following transformations, cropping, replacing colour, adding overlays for images, text, text position**,** text properties, and flags to alter the positioning of the text.
Putting it all together
With that done, we can now start using the components to create the product customization. To do this, we modify index.vue
file in the pages
folder to the following:
https://gist.github.com/Mr-Malomz/f4be24396e8192334ed66a005649bc9e
https://gist.github.com/Mr-Malomz/f4be24396e8192334ed66a005649bc9e
The snippet above does the following:
- Import the required JSON collections
- Create
data
properties to manage imported JSON collections, selected colour, selected image, selected font, custom text and key. The key property is required to trigger a re-render when any of the property changes. - Create
setActiveColor
,setActiveImage
,setActiveFont
, andsetText
methods to change colours, images, fonts and text respectively. We also changed thekey
property in all the functions to trigger re-render accordingly. - Markup to include the components and pass in the required props. In addition to the required props, we added the
key
props to theProductImage
component to track changes and re-render when needed.
With that done, we can start a development server using the command below:
npm run dev
This post discussed how to build a customizable product component using Cloudinary and Nuxt.js.
You may find these resources helpful: