Skip to content

E-commerce product customization page in NuxtJS

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

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.

Cloudinary console and upload button for other formats of upload

select web address and enter url

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.

Uploaded image with publicId highlighted

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, and selectedColor 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 the sectActiveColor props to change the colour. This method also removes the first letter from the generated colour. E.g., colour #ffffff will return as ffffff.
  • Markup shows an input field of type color and a change 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, and selectedImage 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, and selectedFont props.
  • Create data properties to manage active font and custom text.
  • Create onChange and onSubmit 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 and customText props.
  • Configure cld-image and cld-transformations to render the product image, overlay, and text. The public-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, and setText methods to change colours, images, fonts and text respectively. We also changed the key 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 the ProductImage 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

working demo

This post discussed how to build a customizable product component using Cloudinary and Nuxt.js.

You may find these resources helpful:

Back to top

Featured Post