{"id":28490,"date":"2022-03-23T22:23:02","date_gmt":"2022-03-23T22:23:02","guid":{"rendered":"http:\/\/E-commerce-Product-Variant-Customization-in-Next.js"},"modified":"2026-03-15T12:56:29","modified_gmt":"2026-03-15T19:56:29","slug":"e-commerce-product-variant-customization-in-next-js","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/guest_post\/e-commerce-product-variant-customization-in-next-js\/","title":{"rendered":"E-commerce Product Variant Customization"},"content":{"rendered":"<div class=\"wp-block-cloudinary-markdown \"><p>In modern e-commerce applications, customers demand tailor-made products and the ability to customize their shopping experience for products.<\/p>\n<p>With product variant customizations, customers can personalize the products themselves in online stores, exactly how they want them before purchase.<\/p>\n<p>This post outlines how to create a customizable product component on a Next.js page.<\/p>\n<h2>Sandbox<\/h2>\n<p>You can find a complete version of the application on <a href=\"https:\/\/codesandbox.io\/s\/awesome-franklin-3e7r9?file=\/pages\/index.js\">Codesandbox<\/a> and <a href=\"https:\/\/github.com\/iamfortune\/cloudinary-product-customization-component\/tree\/main\/\">GitHub<\/a>.<\/p>\n<\/div>\n\n\n  <div class=\"wp-block-cloudinary-code-sandbox \">\n    <iframe\n      src=\"https:\/\/codesandbox.io\/embed\/awesome-franklin-3e7r9?theme=dark&amp;codemirror=1&amp;highlights=&amp;editorsize=50&amp;fontsize=14&amp;expanddevtools=0&amp;hidedevtools=0&amp;eslint=0&amp;forcerefresh=0&amp;hidenavigation=0&amp;initialpath=%2F&amp;module=&amp;moduleview=0&amp;previewwindow=&amp;view=&amp;runonclick=1\"\n      height=\"500\"\n      style=\"width: 100%;\"\n      title=\"Ecommerce product customization in Next.js\"\n      loading=\"lazy\"\n      allow=\"accelerometer; ambient-light-sensor; camera; encrypted-media; geolocation; gyroscope; hid; microphone; midi; payment; usb; vr; xr-spatial-tracking\"\n      sandbox=\"allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts\"\n    ><\/iframe>\n  <\/div>\n\n\n<div class=\"wp-block-cloudinary-markdown \"><p>GitHub URL: <a href=\"https:\/\/github.com\/iamfortune\/cloudinary-product-customization-component\/tree\/main\/\">https:\/\/github.com\/iamfortune\/cloudinary-product-customization-component\/tree\/main\/<\/a><\/p>\n<h2>Prerequisites and Installation<\/h2>\n<p>Following through with this post requires basic knowledge of JavaScript and React.js.<\/p>\n<p>To get started, we install the Next.js CLI and create a Next application using NPX &#8211; a node package runner. We can also use yarn to install packages and create a new project. Next.js ships with starters, and in our application, we\u2019ll use the Next.js default starter.<\/p>\n<p>To get a Next.js project started, we navigate to our desired directory and run the terminal command below:<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">    npx create-next-app \n<\/code><\/span><\/pre>\n<p>Follow the prompt to create a Next.js project using the default starter.<\/p>\n<p>This project uses <a href=\"https:\/\/cloudinary.com\/\">Cloudinary<\/a> to store and transform media assets.<\/p>\n<p>Cloudinary provides a robust media experience for any media type, covering image and video storage, transformation, delivery, and optimization.\nWe\u2019ll use the <a href=\"https:\/\/www.npmjs.com\/package\/cloudinary-react\">cloudinary-react<\/a> package, which allows us to utilize Cloudinary assets in our Next.js application.<\/p>\n<p>We proceed to install the following dependencies using the command below:<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">    npm install cloudinary-react react-icons\n<\/code><\/span><\/pre>\n<p><a href=\"https:\/\/react-icons.github.io\/react-icons\/\">React-icons<\/a> is an icon library for React applications.<\/p>\n<p>TailwindCSS classes are used in styling the React components in this project. You can find <a href=\"https:\/\/tailwindcss.com\/docs\/guides\/nextjs\">how to install Tailwind in a Next.js project here<\/a>.<\/p>\n<h2>Product Customization Components<\/h2>\n<p>The product customizer has the following parts:<\/p>\n<ol>\n<li>A color selector with a custom color picker<\/li>\n<li>A texture selector from two provided textures<\/li>\n<li>A logo selector<\/li>\n<li>A text input to add free text.<\/li>\n<li>A rendered image of the product that updates with each modification.<\/li>\n<\/ol>\n<p>We\u2019ll proceed to implement each component.<\/p>\n<h2>Creating the Color Selector<\/h2>\n<p>We create a new directory called <code>components<\/code>, which will contain our color schemes, elements and layout for our application.\nIn the <code>components<\/code> folder,  we create new folder called <code>Colors<\/code> and in it, we create a new file named <code>ColorDropPicker.jsx<\/code> that will house the custom <a href=\"https:\/\/cloudinary.com\/tools\/color-picker\">color picker<\/a> of our product customizer. Next, we import all required dependencies in the file.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">    <span class=\"hljs-comment\">\/\/ components\/Colors\/ColorDropPicker.jsx<\/span>\n    <span class=\"hljs-keyword\">import<\/span> { FaCaretDown } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'react-icons\/fa'<\/span>;\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p><code>FaCaretDown<\/code> is a caret icon component from <a href=\"https:\/\/react-icons.github.io\/react-icons\/\">react-icons<\/a>. With it, users can toggle to display a list of colors to choose from.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">    <span class=\"hljs-keyword\">const<\/span> ColorDropPicker = <span class=\"hljs-function\">(<span class=\"hljs-params\">{ onChange }<\/span>) =&gt;<\/span> {\n        <span class=\"hljs-keyword\">return<\/span> (\n            <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{<\/span>`<span class=\"hljs-attr\">flex<\/span> <span class=\"hljs-attr\">flex-row<\/span> <span class=\"hljs-attr\">justify-between<\/span> <span class=\"hljs-attr\">items-center<\/span> <span class=\"hljs-attr\">border<\/span> <span class=\"hljs-attr\">border-gray-500<\/span> <span class=\"hljs-attr\">bg-gray-200<\/span>`} <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">{{<\/span> <span class=\"hljs-attr\">width:<\/span> <span class=\"hljs-attr\">52<\/span>, <span class=\"hljs-attr\">padding:<\/span> `<span class=\"hljs-attr\">10px<\/span> <span class=\"hljs-attr\">auto<\/span>` }}&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span>\n                    <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">{<\/span>`<span class=\"hljs-attr\">color<\/span>`}\n                    <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">{{<\/span> <span class=\"hljs-attr\">width:<\/span> <span class=\"hljs-attr\">32<\/span>, <span class=\"hljs-attr\">height:<\/span> <span class=\"hljs-attr\">32<\/span> }}\n                    <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{<\/span>`<span class=\"hljs-attr\">outline-none<\/span> <span class=\"hljs-attr\">cursor-pointer<\/span>`}\n                    <span class=\"hljs-attr\">onChange<\/span>=<span class=\"hljs-string\">{<\/span> <span class=\"hljs-attr\">e<\/span> =&gt;<\/span> onChange(e.currentTarget.value.slice(1)) }\n                    \/\/ slice to remove the '#' at the beginning of the hex color code\n                \/&gt;\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">FaCaretDown<\/span> <span class=\"hljs-attr\">size<\/span>=<span class=\"hljs-string\">{12}<\/span> \/&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span><\/span>\n        );\n    };\n    <span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> ColorDropPicker;\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>We created a component <code>ColorDropPicker<\/code> which takes in an <code>onChange<\/code> prop. The component returned an input for a user to input colors that will apply to the product. We defined some default color schemes, to update a product\u2019s color scheme with one chosen by a user. We used the native JavaScript <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Array\/slice\">Slice<\/a> to remove the <code>#<\/code> in the array containing our default color code.<\/p>\n<p>Finally, we set a size of 12 to <code>faCaretDown<\/code>.<\/p>\n<p>In the Colors directory, we proceed to create a new file called <code>ColorPickerSquare.jsx<\/code>. This component will display a square containing the color selected using the color picker.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">    <span class=\"hljs-keyword\">const<\/span> ColorPickerSquare = <span class=\"hljs-function\">(<span class=\"hljs-params\">{ color, onSelect, className, isFluid, isFocused, onClick }<\/span>) =&gt;<\/span> {\n        <span class=\"hljs-keyword\">const<\/span> squareImage = <span class=\"hljs-string\">`https:\/\/res.cloudinary.com\/demo-robert\/image\/upload\/w_30,h_30\/e_replace_color:<span class=\"hljs-subst\">${color}<\/span>:60:white\/l_heather_texture,o_0,w_30,h_30,c_crop\/white-bar.jpg`<\/span>;\n        <span class=\"hljs-keyword\">return<\/span> (\n            <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>\n                <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{<\/span>`${<span class=\"hljs-attr\">isFocused<\/span> &amp;&amp; '<span class=\"hljs-attr\">border-2<\/span> <span class=\"hljs-attr\">border-gray-500<\/span>'} <span class=\"hljs-attr\">transition<\/span> <span class=\"hljs-attr\">duration-300<\/span> <span class=\"hljs-attr\">cursor-pointer<\/span> ${<span class=\"hljs-attr\">className<\/span>} ${<span class=\"hljs-attr\">color<\/span> == <span class=\"hljs-string\">'white'<\/span> || <span class=\"hljs-attr\">color<\/span> == <span class=\"hljs-string\">'ffffff'<\/span> ? '<span class=\"hljs-attr\">border<\/span> <span class=\"hljs-attr\">border-gray-500<\/span>' <span class=\"hljs-attr\">:<\/span> <span class=\"hljs-attr\">null<\/span>}`} \n                <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">{{<\/span> \n                    <span class=\"hljs-attr\">backgroundColor:<\/span> <span class=\"hljs-attr\">color<\/span> ?? '<span class=\"hljs-attr\">transparent<\/span>', <span class=\"hljs-attr\">width:<\/span> !<span class=\"hljs-attr\">isFluid<\/span> ? <span class=\"hljs-attr\">32<\/span> <span class=\"hljs-attr\">:<\/span> `<span class=\"hljs-attr\">auto<\/span>`, <span class=\"hljs-attr\">height:<\/span> !<span class=\"hljs-attr\">isFluid<\/span> ? <span class=\"hljs-attr\">32<\/span> <span class=\"hljs-attr\">:<\/span> `<span class=\"hljs-attr\">auto<\/span>`,\n                    <span class=\"hljs-attr\">backgroundImage:<\/span> `<span class=\"hljs-attr\">url<\/span>('${<span class=\"hljs-attr\">squareImage<\/span>}')`, <span class=\"hljs-attr\">backgroundRepeat:<\/span> `<span class=\"hljs-attr\">no-repeat<\/span>`, <span class=\"hljs-attr\">backgroundPosition:<\/span> `<span class=\"hljs-attr\">center<\/span>`,\n                    <span class=\"hljs-attr\">minWidth:<\/span> !<span class=\"hljs-attr\">isFluid<\/span> ? <span class=\"hljs-attr\">32<\/span> <span class=\"hljs-attr\">:<\/span> <span class=\"hljs-attr\">isFluid<\/span>&#91;<span class=\"hljs-attr\">0<\/span>], <span class=\"hljs-attr\">minHeight:<\/span> !<span class=\"hljs-attr\">isFluid<\/span> ? <span class=\"hljs-attr\">32<\/span> <span class=\"hljs-attr\">:<\/span> <span class=\"hljs-attr\">isFluid<\/span>&#91;<span class=\"hljs-attr\">1<\/span>]\n                }}\n                <span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{<\/span> <span class=\"hljs-attr\">e<\/span> =&gt;<\/span> {\n                    onSelect ? onSelect(e) : null;\n                    onClick ? onClick(e): null;\n                }}\n            &gt;\n                <span class=\"hljs-symbol\">&amp;nbsp;<\/span>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n        );\n    };\n    \n    <span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> ColorPickerSquare;\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>In the code above, we added a <a href=\"https:\/\/cloudinary.com\/tools\/square-crop-image\">square image<\/a> from Cloudinary which accepts a variable color transformation. This color will be updated based on the user selected product color. We added variable tailwind classes to the component to handle transitions and border styling.<\/p>\n<p>To the square component, we added styles for background color, image, minimum height and width. The functions passed to the component via the <code>onClick<\/code> and <code>onSelect<\/code> props are also propagated to the component\u2019s <code>onClick<\/code> handler.<\/p>\n<p>Next, in the Colors directory, we create a new file called <code>ColorPickerArray.jsx<\/code>. This component will contain an array of data for specific product colors, CSS classes for each color, and an array of width and height for each color.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">    <span class=\"hljs-comment\">\/\/ components\/Colors\/ColorPickerArray.jsx<\/span>\n    <span class=\"hljs-keyword\">import<\/span> { useState } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>;\n    <span class=\"hljs-keyword\">import<\/span> { ColorPickerSquare } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\"<\/span>;\n    \n    <span class=\"hljs-keyword\">const<\/span> ColorPickerArray = <span class=\"hljs-function\">(<span class=\"hljs-params\">{ colorsArray, elementClassName, isFluid, onSelect }<\/span>) =&gt;<\/span> {\n        <span class=\"hljs-comment\">\/\/ SET INDEX OBJECT TO AUTO-FOCUSED<\/span>\n        <span class=\"hljs-keyword\">const<\/span> &#91;focusedSquare, setFocusedSquare] = useState(<span class=\"hljs-number\">0<\/span>);\n        <span class=\"hljs-keyword\">return<\/span> (\n            <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{<\/span>`<span class=\"hljs-attr\">flex<\/span> <span class=\"hljs-attr\">flex-row<\/span>`}&gt;<\/span>\n                { \n                    colorsArray.map((color, index) =&gt; (\n                        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ColorPickerSquare<\/span>\n                            <span class=\"hljs-attr\">isFocused<\/span>=<span class=\"hljs-string\">{<\/span> <span class=\"hljs-attr\">index<\/span> === <span class=\"hljs-string\">focusedSquare<\/span> }\n                            <span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{<\/span> <span class=\"hljs-attr\">e<\/span> =&gt;<\/span> setFocusedSquare(index) }\n                            color={color}\n                            onSelect={e =&gt; onSelect ? onSelect(e, index) : null}\n                            className={elementClassName}\n                            isFluid={isFluid}\n                        \/&gt;\n                    )) \n                }\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n        );\n    };\n    \n    <span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> ColorPickerArray;\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>In the file above, we initialized a function component called <code>ColorPickerArray<\/code> which takes in <code>colorsArray<\/code>, <code>elementClassName<\/code>, <code>isFluid<\/code> and <code>onSelect<\/code> as props.<\/p>\n<p>We created a state variable using the <code>useState<\/code> hook to store the index of any selected color. Using the native JavaScript <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Map\">map<\/a> method, we loop through the provided colors and render a <code>ColorPickerSquare<\/code> component for each.<\/p>\n<h2>Creating a Font Picker Component<\/h2>\n<p>We require custom text in a select number of fonts which we will specify. We create a new component in <code>components\/FontPicker\/index.js<\/code> with:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">    <span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> FontPicker = <span class=\"hljs-function\">(<span class=\"hljs-params\">{ fonts, className, onChange, value }<\/span>) =&gt;<\/span> {\n      <span class=\"hljs-keyword\">const<\/span> renderOptionElements = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span>\n        fonts.map(<span class=\"hljs-function\">(<span class=\"hljs-params\">font<\/span>) =&gt;<\/span> (\n          <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">option<\/span>\n            <span class=\"hljs-attr\">key<\/span>=<span class=\"hljs-string\">{font.id}<\/span>\n            <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">{font.value}<\/span>\n            <span class=\"hljs-attr\">selected<\/span>=<span class=\"hljs-string\">{font.selected}<\/span>\n            <span class=\"hljs-attr\">hidden<\/span>=<span class=\"hljs-string\">{font.hidden}<\/span>\n          &gt;<\/span>\n            {font.title}\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">option<\/span>&gt;<\/span><\/span>\n        ));\n      <span class=\"hljs-keyword\">return<\/span> (\n        <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">select<\/span>\n          <span class=\"hljs-attr\">key<\/span>=<span class=\"hljs-string\">{className.id}<\/span>\n          <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{className}<\/span>\n          <span class=\"hljs-attr\">onChange<\/span>=<span class=\"hljs-string\">{(e)<\/span> =&gt;<\/span> (onChange ? onChange(e) : null)}\n          value={value}\n        &gt;\n          {renderOptionElements()}\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">select<\/span>&gt;<\/span><\/span>\n      );\n    };\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>In the created <code>FontPicker<\/code> component, we defined props of <code>fonts<\/code>, <code>className<\/code>, <code>onChange<\/code>, and <code>value<\/code>. Using the native Javascript <a href=\"https:\/\/developer.mozilla.org\/en-US\/docs\/Web\/JavaScript\/Reference\/Global_Objects\/Map\">map<\/a> object, we iterate over the list of fonts and display the font title. The list of font options are rendered in a <code>select<\/code> element and selecting a font option triggers the function passed in the <code>onChange<\/code> props.<\/p>\n<h2>Building an Overlay Picker Component<\/h2>\n<p>We require logo overlays on the product. These logos can be selected from a list of rendered square logos. We will create an overlay selector which renders a provided logo enclosed in a square. In a new component <code>components\/Overlays\/OverlayPicker.jsx<\/code>, we create a component which renders the overlay selector.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">    <span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> OverlayPickerSquare = ({\n      onSelect,\n      className,\n      isFluid,\n      isFocused,\n      onClick,\n      image\n    }) =&gt; {\n      <span class=\"hljs-keyword\">return<\/span> (\n        <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>\n          <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{<\/span>`${\n            <span class=\"hljs-attr\">isFocused<\/span> &amp;&amp; \"<span class=\"hljs-attr\">border-2<\/span> <span class=\"hljs-attr\">border-gray-500<\/span>\"\n          } <span class=\"hljs-attr\">transition<\/span> <span class=\"hljs-attr\">duration-300<\/span> <span class=\"hljs-attr\">cursor-pointer<\/span> ${<span class=\"hljs-attr\">className<\/span>}`}\n          <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">{{<\/span>\n            <span class=\"hljs-attr\">width:<\/span> !<span class=\"hljs-attr\">isFluid<\/span> ? <span class=\"hljs-attr\">32<\/span> <span class=\"hljs-attr\">:<\/span> `<span class=\"hljs-attr\">auto<\/span>`,\n            <span class=\"hljs-attr\">height:<\/span> !<span class=\"hljs-attr\">isFluid<\/span> ? <span class=\"hljs-attr\">32<\/span> <span class=\"hljs-attr\">:<\/span> `<span class=\"hljs-attr\">auto<\/span>`,\n            <span class=\"hljs-attr\">backgroundImage:<\/span> `<span class=\"hljs-attr\">url<\/span>('${<span class=\"hljs-attr\">image<\/span>}')`,\n            <span class=\"hljs-attr\">backgroundRepeat:<\/span> `<span class=\"hljs-attr\">no-repeat<\/span>`,\n            <span class=\"hljs-attr\">backgroundPosition:<\/span> `<span class=\"hljs-attr\">center<\/span>`,\n            <span class=\"hljs-attr\">minWidth:<\/span> !<span class=\"hljs-attr\">isFluid<\/span> ? <span class=\"hljs-attr\">32<\/span> <span class=\"hljs-attr\">:<\/span> <span class=\"hljs-attr\">isFluid<\/span>&#91;<span class=\"hljs-attr\">0<\/span>],\n            <span class=\"hljs-attr\">minHeight:<\/span> !<span class=\"hljs-attr\">isFluid<\/span> ? <span class=\"hljs-attr\">32<\/span> <span class=\"hljs-attr\">:<\/span> <span class=\"hljs-attr\">isFluid<\/span>&#91;<span class=\"hljs-attr\">1<\/span>]\n          }}\n          <span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{(e)<\/span> =&gt;<\/span> {\n            onSelect ? onSelect(e) : null;\n            onClick ? onClick(e) : null;\n          }}\n        &gt;\n          <span class=\"hljs-symbol\">&amp;nbsp;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n      );\n    };\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>In the code snippet above, we created and exported an <code>Over``lay``PickerSquare<\/code> component having props of <code>onSelect<\/code>, <code>className<\/code>, <code>isFluid<\/code>, <code>isFocused<\/code>, <code>onClick<\/code>, and <code>image<\/code>. We added custom style properties to the component and triggered the <code>onSelect<\/code> and <code>onClick<\/code> functions if passed, to the component when it\u2019s clicked.<\/p>\n<p>To complete the overlay picker, we\u2019ll create a parent component that renders multiple overlay squares once we pass an array of image logos to it. We do this in a new component <code>components\/Overlays\/OverlayPickerArray.jsx<\/code> with:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">    <span class=\"hljs-keyword\">import<\/span> { useState } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>;\n    <span class=\"hljs-keyword\">import<\/span> { OverlayPickerSquare } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/OverlayPicker\"<\/span>;\n    <span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> OverlayPickerArray = ({\n      overlaysArray,\n      elementClassName,\n      isFluid,\n      onSelect\n    }) =&gt; {\n      <span class=\"hljs-comment\">\/\/ SET INDEX OBJECT TO AUTO-FOCUSED<\/span>\n      <span class=\"hljs-keyword\">const<\/span> &#91;focusedSquare, setFocusedSquare] = useState(<span class=\"hljs-number\">0<\/span>);\n      <span class=\"hljs-keyword\">return<\/span> (\n        <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{<\/span>`<span class=\"hljs-attr\">flex<\/span> <span class=\"hljs-attr\">flex-row<\/span>`}&gt;<\/span>\n          {overlaysArray.map((overlay, index) =&gt; (\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">OverlayPickerSquare<\/span>\n              <span class=\"hljs-attr\">isFocused<\/span>=<span class=\"hljs-string\">{index<\/span> === <span class=\"hljs-string\">focusedSquare}<\/span>\n              <span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{(e)<\/span> =&gt;<\/span> setFocusedSquare(index)}\n              onSelect={(e) =&gt; (onSelect ? onSelect(e, overlay.overlay) : null)}\n              className={elementClassName}\n              isFluid={isFluid}\n              image={overlay.image}\n            \/&gt;\n          ))}\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n      );\n    };\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>In the code snippet above, we created the <code>OverlayPickerArray<\/code> component which takes props of provided logos with <code>overlaysArray<\/code>, loops through each overlay and renders the imported <code>OverlayPickerSquare<\/code> component for each logo.<\/p>\n<p>The index of the currently selected square is stored in a <code>focusedSquare<\/code> state variable for use in the <code>isFocused<\/code> prop value of <code>OverlayPickerSquare<\/code>.<\/p>\n<h2>Creating a Texture Picker Component<\/h2>\n<p>Furthermore, we require a texture picker. This is similar to the overlay picker. We create similar components of <code>TexturePickerSquare<\/code> and <code>TexturePickerArray<\/code> in <code>components\/Textures<\/code> to create squares of product texture selectors and render an array of provided textures.<\/p>\n<p>In <code>components\/Textures\/TexturePickerSquare.jsx<\/code> we have:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">    <span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> TexturePickerSquare = ({\n      onSelect,\n      className,\n      isFluid,\n      isFocused,\n      onClick,\n      image\n    }) =&gt; {\n      <span class=\"hljs-keyword\">return<\/span> (\n        <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>\n          <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{<\/span>`${\n            <span class=\"hljs-attr\">isFocused<\/span> &amp;&amp; \"<span class=\"hljs-attr\">border-2<\/span> <span class=\"hljs-attr\">border-gray-500<\/span>\"\n          } <span class=\"hljs-attr\">transition<\/span> <span class=\"hljs-attr\">duration-300<\/span> <span class=\"hljs-attr\">cursor-pointer<\/span> ${<span class=\"hljs-attr\">className<\/span>}`}\n          <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">{{<\/span>\n            <span class=\"hljs-attr\">width:<\/span> !<span class=\"hljs-attr\">isFluid<\/span> ? <span class=\"hljs-attr\">32<\/span> <span class=\"hljs-attr\">:<\/span> `<span class=\"hljs-attr\">auto<\/span>`,\n            <span class=\"hljs-attr\">height:<\/span> !<span class=\"hljs-attr\">isFluid<\/span> ? <span class=\"hljs-attr\">32<\/span> <span class=\"hljs-attr\">:<\/span> `<span class=\"hljs-attr\">auto<\/span>`,\n            <span class=\"hljs-attr\">backgroundImage:<\/span> `<span class=\"hljs-attr\">url<\/span>('${<span class=\"hljs-attr\">image<\/span>}')`,\n            <span class=\"hljs-attr\">backgroundRepeat:<\/span> `<span class=\"hljs-attr\">no-repeat<\/span>`,\n            <span class=\"hljs-attr\">backgroundPosition:<\/span> `<span class=\"hljs-attr\">center<\/span>`,\n            <span class=\"hljs-attr\">minWidth:<\/span> !<span class=\"hljs-attr\">isFluid<\/span> ? <span class=\"hljs-attr\">32<\/span> <span class=\"hljs-attr\">:<\/span> <span class=\"hljs-attr\">isFluid<\/span>&#91;<span class=\"hljs-attr\">0<\/span>],\n            <span class=\"hljs-attr\">minHeight:<\/span> !<span class=\"hljs-attr\">isFluid<\/span> ? <span class=\"hljs-attr\">32<\/span> <span class=\"hljs-attr\">:<\/span> <span class=\"hljs-attr\">isFluid<\/span>&#91;<span class=\"hljs-attr\">1<\/span>]\n          }}\n          <span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{(e)<\/span> =&gt;<\/span> {\n            onSelect ? onSelect(e) : null;\n            onClick ? onClick(e) : null;\n          }}\n        &gt;\n          <span class=\"hljs-symbol\">&amp;nbsp;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n      );\n    };\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>And in <code>component\/Textures\/TexturePickerArray.jsx<\/code> we have:<\/p>\n<!-- Is .\/TexturePicker suppose to be .\/TexturePickerSquare -->\n<blockquote>\n<p>Comment: [William] No, the current naming is correct<\/p>\n<\/blockquote>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">    <span class=\"hljs-keyword\">import<\/span> { useState } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>;\n    <span class=\"hljs-keyword\">import<\/span> { TexturePickerSquare } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/TexturePicker\"<\/span>;\n    <span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> TexturePickerArray = ({\n      texturesArray,\n      elementClassName,\n      isFluid,\n      onSelect\n    }) =&gt; {\n      <span class=\"hljs-comment\">\/\/ SET INDEX OBJECT TO AUTO-FOCUSED<\/span>\n      <span class=\"hljs-keyword\">const<\/span> &#91;focusedSquare, setFocusedSquare] = useState(<span class=\"hljs-number\">0<\/span>);\n      <span class=\"hljs-keyword\">return<\/span> (\n        <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{<\/span>`<span class=\"hljs-attr\">flex<\/span> <span class=\"hljs-attr\">flex-row<\/span>`}&gt;<\/span>\n          {texturesArray.map((texture, index) =&gt; (\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">TexturePickerSquare<\/span>\n              <span class=\"hljs-attr\">key<\/span>=<span class=\"hljs-string\">{texture.id}<\/span>\n              <span class=\"hljs-attr\">isFocused<\/span>=<span class=\"hljs-string\">{index<\/span> === <span class=\"hljs-string\">focusedSquare}<\/span>\n              <span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{(e)<\/span> =&gt;<\/span> setFocusedSquare(index)}\n              onSelect={(e) =&gt; (onSelect ? onSelect(e, texture.texture) : null)}\n              className={elementClassName}\n              isFluid={isFluid}\n              image={texture.image}\n            \/&gt;\n          ))}\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n      );\n    };\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<h2>Building the Product Image component<\/h2>\n<p>In this component, we\u2019ll create an image viewer, which will act as a wrapper for our image. To do this, inside the <code>components<\/code> directory we create a new folder called <code>ImageViewer<\/code> and in it, create an <code>index.j``sx<\/code> file with the following content:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">    <span class=\"hljs-keyword\">import<\/span> {CloudinaryContext, Transformation, Image} <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'cloudinary-react'<\/span>;\n    \n    <span class=\"hljs-keyword\">const<\/span> ProductImage = <span class=\"hljs-function\">(<span class=\"hljs-params\">{ alt, displacement, productColor, overlay, texture, text, font}<\/span>) =&gt;<\/span> {\n        \n        <span class=\"hljs-keyword\">return<\/span> (\n            <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">CloudinaryContext<\/span> <span class=\"hljs-attr\">cloudName<\/span>=<span class=\"hljs-string\">\"demo-robert\"<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Image<\/span> <span class=\"hljs-attr\">publicId<\/span>=<span class=\"hljs-string\">\"Hanging_T-Shirt_v83je9\"<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">\"650\"<\/span> <span class=\"hljs-attr\">secure<\/span>=<span class=\"hljs-string\">\"true\"<\/span>&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Transformation<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">\"300\"<\/span> <span class=\"hljs-attr\">fetchFormat<\/span>=<span class=\"hljs-string\">\"auto\"<\/span> \/&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Transformation<\/span> <span class=\"hljs-attr\">overlay<\/span>=<span class=\"hljs-string\">{texture}<\/span> <span class=\"hljs-attr\">quality<\/span>=<span class=\"hljs-string\">\"auto\"<\/span> <span class=\"hljs-attr\">fetchFormat<\/span>=<span class=\"hljs-string\">\"auto\"<\/span> \/&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Transformation<\/span> <span class=\"hljs-attr\">overlay<\/span>=<span class=\"hljs-string\">{overlay}<\/span> <span class=\"hljs-attr\">quality<\/span>=<span class=\"hljs-string\">\"auto\"<\/span> <span class=\"hljs-attr\">fetchFormat<\/span>=<span class=\"hljs-string\">\"auto\"<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">\"110\"<\/span> \/&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Transformation<\/span> <span class=\"hljs-attr\">overlay<\/span>=<span class=\"hljs-string\">{{fontFamily:<\/span> <span class=\"hljs-attr\">font<\/span>, <span class=\"hljs-attr\">fontSize:<\/span> <span class=\"hljs-attr\">33<\/span>, <span class=\"hljs-attr\">fontWeight:<\/span> \"<span class=\"hljs-attr\">bold<\/span>\", <span class=\"hljs-attr\">text:<\/span> <span class=\"hljs-attr\">text<\/span>}} <span class=\"hljs-attr\">gravity<\/span>=<span class=\"hljs-string\">\"center\"<\/span> <span class=\"hljs-attr\">y<\/span>=<span class=\"hljs-string\">{<\/span> <span class=\"hljs-attr\">overlay<\/span> === <span class=\"hljs-string\">''<\/span> ? <span class=\"hljs-attr\">40<\/span> <span class=\"hljs-attr\">:<\/span> <span class=\"hljs-attr\">130<\/span> }  <span class=\"hljs-attr\">color<\/span>=<span class=\"hljs-string\">{<\/span>`#<span class=\"hljs-attr\">666<\/span>`} \/&gt;<\/span>\n                    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Transformation<\/span> <span class=\"hljs-attr\">rawTransformation<\/span>=<span class=\"hljs-string\">{<\/span>`<span class=\"hljs-attr\">e_replace_color:<\/span>${<span class=\"hljs-attr\">productColor<\/span>}`} \/&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Image<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">CloudinaryContext<\/span>&gt;<\/span><\/span>\n        );\n    };\n    \n    <span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> ProductImage;\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>In the code above, we wrapped our <code>ProductImage<\/code> component in <code>CloudinaryContext<\/code>. Cloudinary context specifies the <code>cloud_name<\/code>, with which we fetch media assets from Cloudinary. All data passed to <code>CloudinaryContext<\/code> as props are provided to its children Cloudinary components.<\/p>\n<blockquote>\n<p>To obtain our <code>cloud_name<\/code>, we need to <a href=\"https:\/\/cloudinary.com\/signup\">create an account on Cloudinary<\/a> and get the cloud name value from our Cloudinary dashboard.<\/p>\n<\/blockquote>\n<p>At the crust of this application is Cloudinary\u2019s robust transformation capabilities.\nWe rendered an image from Cloudinary using the <code>Image<\/code> component, and applied Cloudinary transformations to it using the <code>Transformation<\/code> component. We specified transformations for texture, an overlain text, general overlays and color.<\/p>\n<h2>Building app Navigation Bar<\/h2>\n<p>To create our project\u2019s navbar, we create a new folder called <code>base<\/code> inside our component directory, and inside it we create a new file <code>Navbar.jsx<\/code>. We create a <code>Navbar<\/code> component with the following:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">    <span class=\"hljs-keyword\">import<\/span> Link <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"next\/link\"<\/span>;\n    <span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> Navbar = <span class=\"hljs-function\">(<span class=\"hljs-params\">{ className }<\/span>) =&gt;<\/span> {\n      <span class=\"hljs-keyword\">return<\/span> (\n        <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">nav<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>\n            <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{<\/span>`<span class=\"hljs-attr\">flex<\/span> <span class=\"hljs-attr\">flex-row<\/span> <span class=\"hljs-attr\">justify-start<\/span> <span class=\"hljs-attr\">items-end<\/span> <span class=\"hljs-attr\">font-lighter<\/span> <span class=\"hljs-attr\">pb-3<\/span> <span class=\"hljs-attr\">pt-2<\/span> ${<span class=\"hljs-attr\">className<\/span>}`}\n          &gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n              <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Link<\/span> <span class=\"hljs-attr\">href<\/span>=<span class=\"hljs-string\">\"\/\"<\/span>&gt;<\/span>\n                <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img<\/span>\n                  <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">\"https:\/\/res-1.cloudinary.com\/cloudinary\/image\/asset\/dpr_2.0\/logo-e0df892053afd966cc0bfe047ba93ca4.png\"<\/span>\n                  <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">{172}<\/span>\n                  <span class=\"hljs-attr\">height<\/span>=<span class=\"hljs-string\">{38}<\/span>\n                  <span class=\"hljs-attr\">alt<\/span>=<span class=\"hljs-string\">\"logo\"<\/span>\n                \/&gt;<\/span>\n              <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Link<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span>\n              <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"capitalize text-blue-deep relative top-2 ml-3\"<\/span>\n              <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">{{<\/span> <span class=\"hljs-attr\">fontSize:<\/span> <span class=\"hljs-attr\">26<\/span> }}\n            &gt;<\/span>\n              Product personalization demo\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">nav<\/span>&gt;<\/span><\/span>\n      );\n    };\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>In the code above, we added a logo with a <code>home<\/code> route path. Next, we added a title to the navbar using a <code>h1<\/code> tag.<\/p>\n<h2>Creating a Page Component<\/h2>\n<p>We created a wrapper component for the page which contains the navigation bar and houses all other child elements. In the <code>components<\/code> directory, we created a file with the path <code>layout\/page.jsx<\/code> and the following content:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">    <span class=\"hljs-keyword\">import<\/span> Head <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"next\/head\"<\/span>;\n    <span class=\"hljs-keyword\">import<\/span> { Navbar } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/Base\/Navbar\"<\/span>;\n    \n    <span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> Page = <span class=\"hljs-function\">(<span class=\"hljs-params\">{ children }<\/span>) =&gt;<\/span> {\n      <span class=\"hljs-keyword\">return<\/span> (\n        <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"min-h-screen relative h-screen max-w-full\"<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Head<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">meta<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"author\"<\/span> <span class=\"hljs-attr\">content<\/span>=<span class=\"hljs-string\">\"Fortune Ikechi\"<\/span> \/&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">meta<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"description\"<\/span> <span class=\"hljs-attr\">content<\/span>=<span class=\"hljs-string\">\"Cloudinary Product Personalization\"<\/span> \/&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">meta<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"viewport\"<\/span> <span class=\"hljs-attr\">content<\/span>=<span class=\"hljs-string\">\"initial-scale=1.0, width=device-width\"<\/span> \/&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">title<\/span>&gt;<\/span>Product Personalization Demo<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">title<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">Head<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{<\/span>`<span class=\"hljs-attr\">flex<\/span> <span class=\"hljs-attr\">flex-col<\/span> <span class=\"hljs-attr\">items-center<\/span> <span class=\"hljs-attr\">min-h-screen<\/span> <span class=\"hljs-attr\">w-full<\/span>`}&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{<\/span>`<span class=\"hljs-attr\">w-full<\/span> <span class=\"hljs-attr\">pl-44<\/span>`}&gt;<\/span>\n              <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Navbar<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{<\/span>`<span class=\"hljs-attr\">w-8<\/span>\/<span class=\"hljs-attr\">12<\/span> <span class=\"hljs-attr\">ml-20<\/span>`} \/&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n            {children}\n          <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n      );\n    };\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>In the code above, we imported our <code>Navbar<\/code> component from the <code>B``ase<\/code> directory. Next, we created a <code>Page<\/code> component that will take in a <code>children<\/code> prop. In the component\u2019s <code>head<\/code> element, we added the author and page information in <code>meta<\/code> tags before rendering a child element or component.<\/p>\n<h2>Creating a Text Input<\/h2>\n<p>Part of the product customization requires we add text content on the product. To do this, in the <code>Base<\/code> directory, we create a new file <code>TextInput.jsx<\/code> with the following content:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">    <span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> DefaultTextInput = <span class=\"hljs-function\">(<span class=\"hljs-params\">{ className, onChange, placeholder }<\/span>) =&gt;<\/span> {\n      <span class=\"hljs-keyword\">return<\/span> (\n        <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span>\n          <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"text\"<\/span>\n          <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{<\/span>`${<span class=\"hljs-attr\">className<\/span>} <span class=\"hljs-attr\">default-styled-input<\/span>`}\n          <span class=\"hljs-attr\">onChange<\/span>=<span class=\"hljs-string\">{(e)<\/span> =&gt;<\/span> (onChange ? onChange(e) : null)}\n          placeholder={placeholder || \"\"}\n        \/&gt;<\/span>\n      );\n    };\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Here, we created a component <code>DefaultTextInput<\/code> with an input element that takes a <code>text<\/code> type and an <code>onChange<\/code> function for updating the input text, we also added a placeholder text.<\/p>\n<h2>Creating a Default Button<\/h2>\n<p>Similar to a default text input, we will create a default button component with an <code>onClick<\/code> function and a title tag for the button. In the <code>Base<\/code> folder, create a new file <code>DefaultButton.jsx<\/code> with the content:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">    <span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> DefaultButton = <span class=\"hljs-function\">(<span class=\"hljs-params\">{ title, onClick }<\/span>) =&gt;<\/span> {\n        <span class=\"hljs-keyword\">return<\/span> (\n            <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span>\n                <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{<\/span>`<span class=\"hljs-attr\">bg-orange<\/span> <span class=\"hljs-attr\">rounded-sm<\/span> <span class=\"hljs-attr\">px-5<\/span> <span class=\"hljs-attr\">py-1<\/span> <span class=\"hljs-attr\">text-white<\/span> <span class=\"hljs-attr\">flex<\/span> <span class=\"hljs-attr\">flex-row<\/span> <span class=\"hljs-attr\">items-center<\/span> <span class=\"hljs-attr\">justify-center<\/span> <span class=\"hljs-attr\">outline-none<\/span> <span class=\"hljs-attr\">focus:ring-2<\/span> <span class=\"hljs-attr\">focus:ring-yellow-300<\/span>`}\n              <span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{(e)<\/span> =&gt;<\/span> (onClick ? onClick(e) : null)}\n        &gt;\n          {title}\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span><\/span>\n      );\n    };\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<h2>Creating a Product Customization Component<\/h2>\n<p>In this section, we\u2019ll put together all the different parts of our application. To do this, we navigate to the <code>pages\/i``ndex.js<\/code> file and add all the features needed by the user to customize a product.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">    <span class=\"hljs-keyword\">import<\/span> { useState, useEffect } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'react'<\/span>;\n    <span class=\"hljs-keyword\">import<\/span> { ProductImage } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/components\/ImageViewer\"<\/span>;\n    <span class=\"hljs-keyword\">import<\/span> { Page } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/components\/layout\/Page\"<\/span>;\n    <span class=\"hljs-keyword\">import<\/span> { ColorDropPicker } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/components\/Colors\/ColorDropPicker\"<\/span>;\n    <span class=\"hljs-keyword\">import<\/span> { ColorPickerArray } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/components\/Colors\/ColorPickerArray\"<\/span>;\n    <span class=\"hljs-keyword\">import<\/span> { FontPicker } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/components\/FontPicker\"<\/span>;\n    <span class=\"hljs-keyword\">import<\/span> { DefaultTextInput } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/components\/Base\/TextInput\"<\/span>;\n    <span class=\"hljs-keyword\">import<\/span> { DefaultButton } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/components\/Base\/DefaultButton\"<\/span>;\n    <span class=\"hljs-keyword\">import<\/span> { OverlayPickerArray } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/components\/Overlays\/OverlayPickerArray\"<\/span>;\n    <span class=\"hljs-keyword\">import<\/span> { TexturePickerArray } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"..\/components\/Textures\/TexturePickerArray\"<\/span>;\n    \n    <span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">Home<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n        <span class=\"hljs-comment\">\/\/ COLORS<\/span>\n        <span class=\"hljs-keyword\">const<\/span> availableColors = &#91;<span class=\"hljs-string\">`ffffff`<\/span>, <span class=\"hljs-string\">`47E8D2`<\/span>, <span class=\"hljs-string\">`DCA381`<\/span>, <span class=\"hljs-string\">`702C3C`<\/span>, <span class=\"hljs-string\">`E9C660`<\/span>, <span class=\"hljs-string\">`A11D1F`<\/span>, <span class=\"hljs-string\">`897115`<\/span>, <span class=\"hljs-string\">`598DE6`<\/span>];\n        \n        <span class=\"hljs-comment\">\/\/OVERLAYS<\/span>\n        <span class=\"hljs-keyword\">const<\/span> overlayOptionsArray = &#91;\n            { <span class=\"hljs-attr\">image<\/span>: <span class=\"hljs-string\">`https:\/\/res.cloudinary.com\/demo-robert\/image\/upload\/w_30,h_30,e_red:0\/e_green:0\/e_blue:0\/l_heather_texture,o_0,w_30,h_30,c_crop\/white-bar.jpg`<\/span>, <span class=\"hljs-attr\">overlay<\/span>: <span class=\"hljs-string\">``<\/span> },\n            { <span class=\"hljs-attr\">image<\/span>: <span class=\"hljs-string\">`https:\/\/res.cloudinary.com\/demo-robert\/image\/upload\/q_auto,f_auto,h_30\/cloudinary-logo.jpg`<\/span>, <span class=\"hljs-attr\">overlay<\/span>: <span class=\"hljs-string\">`cloudinary-logo`<\/span> },\n            { <span class=\"hljs-attr\">image<\/span>: <span class=\"hljs-string\">`https:\/\/res.cloudinary.com\/demo-robert\/image\/upload\/q_auto,f_auto,h_30\/fire.png`<\/span>, <span class=\"hljs-attr\">overlay<\/span>: <span class=\"hljs-string\">`fire`<\/span> }\n        ];\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Here, we created data for customizable colors and the overlain logo.<\/p>\n<p>Next, we add default data for textures, and fonts.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-16\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">    <span class=\"hljs-comment\">\/\/ TEXTURES<\/span>\n        <span class=\"hljs-keyword\">const<\/span> textureOptionsArray = &#91;\n            { <span class=\"hljs-attr\">image<\/span>: <span class=\"hljs-string\">`https:\/\/res.cloudinary.com\/demo-robert\/image\/upload\/w_30,h_30,e_red:0\/e_green:0\/e_blue:0\/l_heather_texture,o_0,w_30,h_30,c_crop\/white-bar.jpg`<\/span>, <span class=\"hljs-attr\">texture<\/span>: <span class=\"hljs-string\">``<\/span> },\n            { <span class=\"hljs-attr\">image<\/span>: <span class=\"hljs-string\">`https:\/\/res.cloudinary.com\/demo-robert\/image\/upload\/w_30,h_30,e_red:0\/e_green:0\/e_blue:0\/l_heather_texture,o_30,w_30,h_30,c_crop\/white-bar.jpg`<\/span>, <span class=\"hljs-attr\">texture<\/span>: <span class=\"hljs-string\">`hanging-shirt-texture`<\/span> },\n        ];\n    \n        <span class=\"hljs-comment\">\/\/ PERSONALIZATION TEXT FONTS<\/span>\n        <span class=\"hljs-keyword\">const<\/span> availableFonts = &#91;\n            { <span class=\"hljs-attr\">title<\/span>: <span class=\"hljs-string\">`Arial`<\/span>, <span class=\"hljs-attr\">value<\/span>: <span class=\"hljs-string\">`Arial`<\/span>, <span class=\"hljs-attr\">selected<\/span>: <span class=\"hljs-literal\">true<\/span> },\n            { <span class=\"hljs-attr\">title<\/span>: <span class=\"hljs-string\">`Georgia`<\/span>, <span class=\"hljs-attr\">value<\/span>: <span class=\"hljs-string\">`Georgia`<\/span> },\n            { <span class=\"hljs-attr\">title<\/span>: <span class=\"hljs-string\">`Sacremento`<\/span>, <span class=\"hljs-attr\">value<\/span>: <span class=\"hljs-string\">`Sacramento`<\/span> },\n            { <span class=\"hljs-attr\">title<\/span>: <span class=\"hljs-string\">`Roboto`<\/span>, <span class=\"hljs-attr\">value<\/span>: <span class=\"hljs-string\">`Roboto`<\/span> },\n            { <span class=\"hljs-attr\">title<\/span>: <span class=\"hljs-string\">`Montserrat`<\/span>, <span class=\"hljs-attr\">value<\/span>: <span class=\"hljs-string\">`Montserrat`<\/span> },\n            { <span class=\"hljs-attr\">title<\/span>: <span class=\"hljs-string\">`Bitter`<\/span>, <span class=\"hljs-attr\">value<\/span>: <span class=\"hljs-string\">`Bitter`<\/span> }\n        ];\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-16\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>Next, we add data on perspectives and displacements for a user\u2019s product:<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-17\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">    <span class=\"hljs-comment\">\/\/ PERSPECTIVES<\/span>\n        <span class=\"hljs-keyword\">const<\/span> imagePerspectives = &#91;\n            { <span class=\"hljs-attr\">image<\/span>: <span class=\"hljs-string\">`https:\/\/res.cloudinary.com\/demo-robert\/q_auto,f_auto\/$text_!%20!\/o_0\/l_sample,o_0,w_220,ar_30:25,c_fit,y_-40,x_-5,e_overlay\/l_text:arial_100_bold:$(text),y_90,co_rgb:333,o_70,w_250\/l_hanging_displace,e_displace,x_10,y_10\/u_Hanging_T-Shirt_v83je9,e_replace_color:white:60:white\/l_hanging-shirt-texture,o_0\/l_Hanger_qa2diz,fl_relative,w_1.0\/w_75,ar_1:1,c_pad\/shirt_only.jpg`<\/span> },\n            { <span class=\"hljs-attr\">image<\/span>: <span class=\"hljs-string\">`https:\/\/res.cloudinary.com\/demo-robert\/q_auto,f_auto\/$text_!%20!\/o_0\/l_sample,o_0,w_330,ar_30:25,c_fit,y_-30,x_-5,e_overlay\/l_text:arial_100_bold:$(text),y_150,co_rgb:333,o_70,w_350\/l_laying_displace,e_displace,x_10,y_10\/u_laying-shirt_xqstgr,e_replace_color:white:60:white\/l_laying-shirt-texture,o_0\/w_75,ar_1:1,c_pad\/shirt_only.jpg`<\/span> },\n            { <span class=\"hljs-attr\">image<\/span>: <span class=\"hljs-string\">`https:\/\/res.cloudinary.com\/demo-robert\/q_auto,f_auto\/$text_!%20!\/o_0\/l_sample,o_0,w_300,ar_30:25,c_fit,y_-200,x_-5,e_overlay\/l_text:arial_100_bold:$(text),y_-40,co_rgb:333,o_70,w_300\/l_shirt_displace,e_displace,x_10,y_10\/u_shirt_only,e_replace_color:white:60:white\/l_heather_texture,o_0\/u_model2\/w_75,ar_1:1,c_pad\/shirt_only.jpg`<\/span> },\n        ];\n        <span class=\"hljs-comment\">\/\/ DISPLACEMENTS<\/span>\n        <span class=\"hljs-keyword\">const<\/span> availableImageDisplacements = &#91;\n           <span class=\"hljs-string\">`hanging_displace`<\/span>, <span class=\"hljs-string\">`laying_displace`<\/span>, <span class=\"hljs-string\">`shirt_displace`<\/span>\n        ];\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-17\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>To complete our customization component, we will add functions enabling users to add or update the color, texture, text, images, and logos on products.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-18\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">    <span class=\"hljs-comment\">\/\/ PRODUCT IMAGE<\/span>\n        <span class=\"hljs-keyword\">const<\/span> &#91;productColor, setProductColor] = useState(availableColors&#91;<span class=\"hljs-number\">0<\/span>]);\n        <span class=\"hljs-keyword\">const<\/span> &#91;productOverlay, setProductOverlay] = useState(<span class=\"hljs-string\">''<\/span>);\n        <span class=\"hljs-keyword\">const<\/span> &#91;productTexture, setProductTexture] = useState(<span class=\"hljs-string\">''<\/span>);\n        <span class=\"hljs-keyword\">const<\/span> &#91;productText, setProductText] = useState(<span class=\"hljs-string\">' '<\/span>); <span class=\"hljs-comment\">\/\/ space important here, or image will not be displayed<\/span>\n        <span class=\"hljs-keyword\">const<\/span> &#91;tempText, setTempText] = useState(<span class=\"hljs-string\">''<\/span>);\n        <span class=\"hljs-keyword\">const<\/span> &#91;productFont, setProductFont] = useState(availableFonts&#91;<span class=\"hljs-number\">0<\/span>].value);\n        <span class=\"hljs-keyword\">const<\/span> &#91;productDisplacement, setProductDisplacement] = useState(availableImageDisplacements&#91;<span class=\"hljs-number\">1<\/span>]);\n    \n        <span class=\"hljs-comment\">\/\/ CHANGE TEXT ON TEXT OVERLAY<\/span>\n        <span class=\"hljs-keyword\">const<\/span> changeProductText = <span class=\"hljs-function\"><span class=\"hljs-params\">e<\/span> =&gt;<\/span> {\n            tempText == <span class=\"hljs-string\">''<\/span> ? setProductText(<span class=\"hljs-string\">' '<\/span>) : setProductText(tempText);\n        };\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-18\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>We created state variables with their update methods for each transformation type. We also created a function to update or add a text on a product.\nNext, we will update the rendered JSX with the components, state variables and update methods as in this final GitHub Gist.<\/p>\n<p><a href=\"https:\/\/gist.github.com\/Chuloo\/cc50dcb4db88e695f9d7025dfd42b818\">https:\/\/gist.github.com\/Chuloo\/cc50dcb4db88e695f9d7025dfd42b818<\/a><\/p>\n<p><a href=\"https:\/\/gist.github.com\/Chuloo\/cc50dcb4db88e695f9d7025dfd42b818\">https:\/\/gist.github.com\/Chuloo\/cc50dcb4db88e695f9d7025dfd42b818<\/a><\/p>\n<p>Our final application should look like this:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/c_limit,w_2000\/f_auto\/q_auto\/media_jams\/s_870728521BED156AAD0D7ECFBDFF5B7A9A29A8CFF3E9A8A50B6201A99241D5E5_1623372039995_ezgif.com-gif-maker+2.gif\" alt=\"Product variant customization with Cloudinary\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"600\" height=\"272\"\/><\/p>\n<h2>Summary<\/h2>\n<p>In this post, we created a product variant customization component using Next.js and Cloudinary. Customization features include color, text, texture, and logo. Furthermore, we can improve the application by utilizing other image transformation properties.<\/p>\n<h2>Resources<\/h2>\n<p>You can check out the following valuable resources.<\/p>\n<ul>\n<li>\n<a href=\"https:\/\/nextjs.org\/learn\/basics\/create-nextjs-app\">Nextjs: Getting started<\/a>\n<\/li>\n<li>\n<a href=\"https:\/\/cloudinary.com\/documentation\/upload_images\">Cloudinary Assets upload<\/a>\n<\/li>\n<\/ul>\n<\/div>","protected":false},"excerpt":{"rendered":"","protected":false},"author":41,"featured_media":28491,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[134,370,246,386,371],"class_list":["post-28490","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-guest-post","tag-image","tag-react","tag-transform","tag-under-review"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.6 (Yoast SEO v26.9) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>E-commerce Product Variant Customization<\/title>\n<meta name=\"description\" content=\"Implement product customizations as seen on e-commerce stores using Next.js\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/cloudinary.com\/blog\/guest_post\/e-commerce-product-variant-customization-in-next-js\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"E-commerce Product Variant Customization\" \/>\n<meta property=\"og:description\" content=\"Implement product customizations as seen on e-commerce stores using Next.js\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/guest_post\/e-commerce-product-variant-customization-in-next-js\/\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2022-03-23T22:23:02+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-03-15T19:56:29+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924396\/Web_Assets\/blog\/c342fa48f7cfee79074fc642c22e7c7ca5ad731a-5477x3651-1_2849143d59\/c342fa48f7cfee79074fc642c22e7c7ca5ad731a-5477x3651-1_2849143d59.jpg?_i=AA\" \/>\n\t<meta property=\"og:image:width\" content=\"5477\" \/>\n\t<meta property=\"og:image:height\" content=\"3651\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"NewsArticle\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/e-commerce-product-variant-customization-in-next-js\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/e-commerce-product-variant-customization-in-next-js\/\"},\"author\":{\"name\":\"\",\"@id\":\"\"},\"headline\":\"E-commerce Product Variant Customization\",\"datePublished\":\"2022-03-23T22:23:02+00:00\",\"dateModified\":\"2026-03-15T19:56:29+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/e-commerce-product-variant-customization-in-next-js\/\"},\"wordCount\":4,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/e-commerce-product-variant-customization-in-next-js\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924396\/Web_Assets\/blog\/c342fa48f7cfee79074fc642c22e7c7ca5ad731a-5477x3651-1_2849143d59\/c342fa48f7cfee79074fc642c22e7c7ca5ad731a-5477x3651-1_2849143d59.jpg?_i=AA\",\"keywords\":[\"Guest Post\",\"Image\",\"React\",\"Transform\",\"Under Review\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2022\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/e-commerce-product-variant-customization-in-next-js\/\",\"url\":\"https:\/\/cloudinary.com\/blog\/guest_post\/e-commerce-product-variant-customization-in-next-js\/\",\"name\":\"E-commerce Product Variant Customization\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/e-commerce-product-variant-customization-in-next-js\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/e-commerce-product-variant-customization-in-next-js\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924396\/Web_Assets\/blog\/c342fa48f7cfee79074fc642c22e7c7ca5ad731a-5477x3651-1_2849143d59\/c342fa48f7cfee79074fc642c22e7c7ca5ad731a-5477x3651-1_2849143d59.jpg?_i=AA\",\"datePublished\":\"2022-03-23T22:23:02+00:00\",\"dateModified\":\"2026-03-15T19:56:29+00:00\",\"description\":\"Implement product customizations as seen on e-commerce stores using Next.js\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/e-commerce-product-variant-customization-in-next-js\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/guest_post\/e-commerce-product-variant-customization-in-next-js\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/e-commerce-product-variant-customization-in-next-js\/#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924396\/Web_Assets\/blog\/c342fa48f7cfee79074fc642c22e7c7ca5ad731a-5477x3651-1_2849143d59\/c342fa48f7cfee79074fc642c22e7c7ca5ad731a-5477x3651-1_2849143d59.jpg?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924396\/Web_Assets\/blog\/c342fa48f7cfee79074fc642c22e7c7ca5ad731a-5477x3651-1_2849143d59\/c342fa48f7cfee79074fc642c22e7c7ca5ad731a-5477x3651-1_2849143d59.jpg?_i=AA\",\"width\":5477,\"height\":3651},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/e-commerce-product-variant-customization-in-next-js\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"E-commerce Product Variant Customization\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"name\":\"Cloudinary Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/cloudinary.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\",\"name\":\"Cloudinary Blog\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"width\":312,\"height\":60,\"caption\":\"Cloudinary Blog\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"\"}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"E-commerce Product Variant Customization","description":"Implement product customizations as seen on e-commerce stores using Next.js","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/cloudinary.com\/blog\/guest_post\/e-commerce-product-variant-customization-in-next-js\/","og_locale":"en_US","og_type":"article","og_title":"E-commerce Product Variant Customization","og_description":"Implement product customizations as seen on e-commerce stores using Next.js","og_url":"https:\/\/cloudinary.com\/blog\/guest_post\/e-commerce-product-variant-customization-in-next-js\/","og_site_name":"Cloudinary Blog","article_published_time":"2022-03-23T22:23:02+00:00","article_modified_time":"2026-03-15T19:56:29+00:00","og_image":[{"width":5477,"height":3651,"url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924396\/Web_Assets\/blog\/c342fa48f7cfee79074fc642c22e7c7ca5ad731a-5477x3651-1_2849143d59\/c342fa48f7cfee79074fc642c22e7c7ca5ad731a-5477x3651-1_2849143d59.jpg?_i=AA","type":"image\/jpeg"}],"twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/e-commerce-product-variant-customization-in-next-js\/#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/e-commerce-product-variant-customization-in-next-js\/"},"author":{"name":"","@id":""},"headline":"E-commerce Product Variant Customization","datePublished":"2022-03-23T22:23:02+00:00","dateModified":"2026-03-15T19:56:29+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/e-commerce-product-variant-customization-in-next-js\/"},"wordCount":4,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/e-commerce-product-variant-customization-in-next-js\/#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924396\/Web_Assets\/blog\/c342fa48f7cfee79074fc642c22e7c7ca5ad731a-5477x3651-1_2849143d59\/c342fa48f7cfee79074fc642c22e7c7ca5ad731a-5477x3651-1_2849143d59.jpg?_i=AA","keywords":["Guest Post","Image","React","Transform","Under Review"],"inLanguage":"en-US","copyrightYear":"2022","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/e-commerce-product-variant-customization-in-next-js\/","url":"https:\/\/cloudinary.com\/blog\/guest_post\/e-commerce-product-variant-customization-in-next-js\/","name":"E-commerce Product Variant Customization","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/e-commerce-product-variant-customization-in-next-js\/#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/e-commerce-product-variant-customization-in-next-js\/#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924396\/Web_Assets\/blog\/c342fa48f7cfee79074fc642c22e7c7ca5ad731a-5477x3651-1_2849143d59\/c342fa48f7cfee79074fc642c22e7c7ca5ad731a-5477x3651-1_2849143d59.jpg?_i=AA","datePublished":"2022-03-23T22:23:02+00:00","dateModified":"2026-03-15T19:56:29+00:00","description":"Implement product customizations as seen on e-commerce stores using Next.js","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/e-commerce-product-variant-customization-in-next-js\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/guest_post\/e-commerce-product-variant-customization-in-next-js\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/e-commerce-product-variant-customization-in-next-js\/#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924396\/Web_Assets\/blog\/c342fa48f7cfee79074fc642c22e7c7ca5ad731a-5477x3651-1_2849143d59\/c342fa48f7cfee79074fc642c22e7c7ca5ad731a-5477x3651-1_2849143d59.jpg?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924396\/Web_Assets\/blog\/c342fa48f7cfee79074fc642c22e7c7ca5ad731a-5477x3651-1_2849143d59\/c342fa48f7cfee79074fc642c22e7c7ca5ad731a-5477x3651-1_2849143d59.jpg?_i=AA","width":5477,"height":3651},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/e-commerce-product-variant-customization-in-next-js\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"E-commerce Product Variant Customization"}]},{"@type":"WebSite","@id":"https:\/\/cloudinary.com\/blog\/#website","url":"https:\/\/cloudinary.com\/blog\/","name":"Cloudinary Blog","description":"","publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/cloudinary.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/cloudinary.com\/blog\/#organization","name":"Cloudinary Blog","url":"https:\/\/cloudinary.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","width":312,"height":60,"caption":"Cloudinary Blog"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":""}]}},"jetpack_featured_media_url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924396\/Web_Assets\/blog\/c342fa48f7cfee79074fc642c22e7c7ca5ad731a-5477x3651-1_2849143d59\/c342fa48f7cfee79074fc642c22e7c7ca5ad731a-5477x3651-1_2849143d59.jpg?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/28490","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/users\/41"}],"replies":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/comments?post=28490"}],"version-history":[{"count":2,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/28490\/revisions"}],"predecessor-version":[{"id":39881,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/28490\/revisions\/39881"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/28491"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=28490"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=28490"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=28490"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}