{"id":28038,"date":"2022-03-24T20:17:10","date_gmt":"2022-03-24T20:17:10","guid":{"rendered":"http:\/\/creating-color-palettes-based-on-images"},"modified":"2022-03-24T20:17:10","modified_gmt":"2022-03-24T20:17:10","slug":"creating-color-palettes-based-on-images","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/guest_post\/creating-color-palettes-based-on-images\/","title":{"rendered":"Creating Color Palettes Based on Images"},"content":{"rendered":"<div class=\"wp-block-cloudinary-markdown \"><p>Sometimes you have a picture that seems to have the right vibe you want in your app or your environment. Maybe an image of an overwater bungalow has some colors you\u2019d like to use in an interior design project or it gives the right feel to a landing page you\u2019ve been working on.<\/p>\n<p>No matter what the end-use is, pictures tend to have great color palettes sitting there looking at us. That\u2019s why we\u2019re going to make an app that lets us upload these images and get a color palette directly from them. By the time you finish this, you\u2019ll be not only have practiced some JavaScript skills, but you\u2019ll be ready to take on some interior design projects as well with your great palettes.<\/p>\n<h2>Setting up the Next app<\/h2>\n<p>We\u2019ll be working with a local Postgres database so that we can store the color palettes generated from an image along with the image itself. So if you don\u2019t have this set up already, make sure to <a href=\"https:\/\/www.postgresql.org\/download\/\">download Postgres<\/a> and create a new table called <code>palettes<\/code>. I always use pgAdmin to perform actions in Postgres but feel free to use the command line if that\u2019s more comfortable.<\/p>\n<p>We\u2019ll also be using Cloudinary to host the images we upload, so if you don\u2019t have a free account, you can <a href=\"https:\/\/cloudinary.com\/users\/register\/free\">make one here<\/a>.<\/p>\n<p>Now, let\u2019s bootstrap a new NextJS app since that\u2019s the framework we\u2019ll play with this time. Open up a terminal and run the following command:<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">$ yarn create next-app --typescript\n<\/code><\/span><\/pre>\n<p>This will start the process of making the app and it\u2019ll prompt you for an app name pretty quickly. Since I\u2019m super creative, I\u2019ve called this project <code>image-color-palette<\/code>, but feel free to name this anything you like.<\/p>\n<p>Now open a terminal and go to the <code>image-color-palette<\/code> directory and install the following packages:<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">$ yarn add swr styled-components @prisma\/client prisma react-cloudinary-upload-widget node-vibrant axios\n$ yarn add -D ts-node typescript @types\/node\n<\/code><\/span><\/pre>\n<p>The packages and dev dependencies are everything we\u2019ll need to get this app working. We\u2019ll talk about what each of them does as we build out the functionality of the app. For now, let\u2019s set up the database so we can store the color palettes a user creates.<\/p>\n<h3>Saving the image and color info to the database<\/h3>\n<p>We\u2019ll be working with Prisma to handle all of the database operations and the data will be stored in our local Postgres instance. So at the root of your project, create a new directory called <code>prisma<\/code>. Then inside of that directory, create a new file called <code>schema.prisma<\/code>. This is where we\u2019ll define the schema we want the database to have.<\/p>\n<p>There are a few things we need to add to this file so that Prisma knows how to handle migrations to the database. So add the following code to the schema 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\">generator client {\n    provider = <span class=\"hljs-string\">\"prisma-client-js\"<\/span>\n}\n\ndatasource db {\n    provider = <span class=\"hljs-string\">\"postgresql\"<\/span>\n    url      = <span class=\"hljs-string\">\"postgres:\/\/username:password@localhost:5432\/palettes\"<\/span>\n}\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>This sets up the client type we\u2019ll use to connect to the database. Then the <code>datasource<\/code> defines the type of database we\u2019re working with, which in our case is Postgres. The <code>url<\/code> is where you\u2019ll need to put your connection string to the database. Make sure to update the <code>username<\/code> and <code>password<\/code> with your values.<\/p>\n<p>We created the <code>palettes<\/code> table back when we set up the local Postgres instance so you should be good to go here. Next, we need to define a model that tells Prisma what kind of database schema we\u2019re working with. Below the current code in the <code>schema.prisma<\/code> file, add the following model:<\/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\">model ColorPalette {\n    id        <span class=\"hljs-built_in\">String<\/span>   @id @<span class=\"hljs-keyword\">default<\/span>(uuid())\n    createdAt DateTime @<span class=\"hljs-keyword\">default<\/span>(now())\n    updatedAt DateTime @updatedAt\n    name      <span class=\"hljs-built_in\">String<\/span>\n    src       <span class=\"hljs-built_in\">String<\/span>\n    colorHigh <span class=\"hljs-built_in\">String<\/span>\n    colorMid  <span class=\"hljs-built_in\">String<\/span>\n    colorLow  <span class=\"hljs-built_in\">String<\/span>\n}\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>The top three fields are pretty standard things to include in a table, but the last five are our main concerns. We\u2019ll have a <code>name<\/code> for each palette generated, the <code>src<\/code> will be a URL to the image in Cloudinary, and <code>colorHigh<\/code>, <code>colorMid<\/code>, and <code>colorLow<\/code> are the hex values we\u2019ll save to make up the palette.<\/p>\n<p>This is all for our schema and Prisma setup. Now we need to create a migration to get all of this info to our database. To do that, run the following command in the root directory of your project:<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">$ npx prisma migrate dev\n<\/code><\/span><\/pre>\n<p>This will prompt you for a migration name and then it\u2019ll add a <code>ColorPalette<\/code> table to the <code>palettes<\/code> database with all of the columns we defined in that model.<\/p>\n<p>The last thing we need to get in place on the data side is a way to handle connections. So we\u2019ll need to set up a version of the Prisma client our back-end can connect to. In order to do that, we need to implement a small helper file.<\/p>\n<p>At the root of your project, create a new directory named <code>utils<\/code>. Inside this new folder, create a new file called <code>prisma.ts<\/code>. This is where we\u2019ll add the following code:<\/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\">import<\/span> { PrismaClient } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@prisma\/client\"<\/span>;\n\n<span class=\"hljs-keyword\">let<\/span> prisma: PrismaClient\n\n<span class=\"hljs-keyword\">if<\/span> (process.env.NODE_ENV === <span class=\"hljs-string\">'production'<\/span>) {\n  prisma = <span class=\"hljs-keyword\">new<\/span> PrismaClient()\n} <span class=\"hljs-keyword\">else<\/span> {\n  <span class=\"hljs-keyword\">if<\/span> (!global.prisma) {\n    global.prisma = <span class=\"hljs-keyword\">new<\/span> PrismaClient()\n  }\n  prisma = global.prisma\n}\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> prisma\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>We make the global Prisma client for development so we don\u2019t exhaust the database connection limit locally. This enables us to do development work without worrying about connection issues.<\/p>\n<p>That wraps up all of the data things! Now we can turn our attention to the API side of things.<\/p>\n<h2>Making the REST API to get colors<\/h2>\n<p>NextJS has an interesting way of including back-end functionality through <a href=\"https:\/\/nextjs.org\/docs\/api-routes\/introduction\">API routes<\/a>. This gives you the flexibility to create simple and complex back-ends without creating an entirely different project to maintain.<\/p>\n<p>Take a look in the <code>pages<\/code> directory and you\u2019ll see a sub-directory called <code>api<\/code>. There\u2019s an existing example file called <code>hello.ts<\/code> and you can delete that. Then add a new file named <code>images.ts<\/code>. This is where we\u2019ll handle the GET and POST requests from the front-end.<\/p>\n<p>We\u2019ll start by importing a few things and defining the type of data we\u2019re working with. In the <code>images.ts<\/code> file, add this code:<\/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-keyword\">import<\/span> type { NextApiRequest, NextApiResponse } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'next'<\/span>\n<span class=\"hljs-keyword\">import<\/span> prisma <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'..\/..\/utils\/prisma'<\/span>\n\ntype Palette = {\n  <span class=\"hljs-attr\">name<\/span>: string\n  <span class=\"hljs-attr\">src<\/span>: string\n  <span class=\"hljs-attr\">colorHigh<\/span>: string\n  <span class=\"hljs-attr\">colorMid<\/span>: string\n  <span class=\"hljs-attr\">colorLow<\/span>: string\n}\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>We\u2019re importing a few types from Next to define the response and request data we expect, the Prisma client we set up so we can connect to the database, and the type definition for the palette data we\u2019ll be working with on the front-end.<\/p>\n<p>To keep this post simple, we\u2019re going to create a handler function that determines what we need to do based on the type of request being sent. Below the type definition, add the following function:<\/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\">default<\/span> <span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">handle<\/span>(<span class=\"hljs-params\">req: NextApiRequest, res: NextApiResponse<\/span>) <\/span>{\n  <span class=\"hljs-keyword\">if<\/span> (req.method === <span class=\"hljs-string\">'GET'<\/span>) {\n    <span class=\"hljs-keyword\">await<\/span> handleGET(req, res)\n  } <span class=\"hljs-keyword\">else<\/span> <span class=\"hljs-keyword\">if<\/span> (req.method === <span class=\"hljs-string\">'POST'<\/span>) {\n    <span class=\"hljs-keyword\">await<\/span> handlePOST(req, res)\n  } <span class=\"hljs-keyword\">else<\/span> {\n    <span class=\"hljs-keyword\">throw<\/span> <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Error<\/span>(\n      <span class=\"hljs-string\">`The HTTP <span class=\"hljs-subst\">${req.method}<\/span> method is not supported at this route.`<\/span>\n    )\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>Our app will only support GET and POST requests, anything else will throw an error. Now we need the function to handle GET requests. Once users start making color palettes, they\u2019ll like want to be able to view them, that\u2019s why we\u2019re creating the <code>handleGET<\/code> function just below the <code>handle<\/code> function:<\/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\">const<\/span> handleGET = <span class=\"hljs-keyword\">async<\/span> (req: NextApiRequest, <span class=\"hljs-attr\">res<\/span>: NextApiResponse&lt;Palette&#91;]&gt;) =&gt; {\n  <span class=\"hljs-keyword\">const<\/span> colorPalettes = <span class=\"hljs-keyword\">await<\/span> prisma.colorPalette.findMany()\n  res.status(<span class=\"hljs-number\">200<\/span>).json(colorPalettes)\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>This one is relatively small because we want to get all of the color palettes a user has uploaded and return that as an array of palette objects. That\u2019s why we\u2019re using the <code>findMany<\/code> method on the <code>ColorPalette<\/code> table without any further filtering. Then we return a 200 status code along with the <code>colorPalettes<\/code> array in JSON format to the front-end.<\/p>\n<p>Next, we need to add the <code>handlePOST<\/code> function. Right below the <code>handleGET<\/code> function, add this code:<\/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\">const<\/span> handlePOST = <span class=\"hljs-keyword\">async<\/span> (req: NextApiRequest, <span class=\"hljs-attr\">res<\/span>: NextApiResponse&lt;Palette&gt;) =&gt; {\n  <span class=\"hljs-keyword\">const<\/span> newColorPalette = <span class=\"hljs-built_in\">JSON<\/span>.parse(req.body)\n\n  <span class=\"hljs-keyword\">const<\/span> result = <span class=\"hljs-keyword\">await<\/span> prisma.colorPalette.create({\n    <span class=\"hljs-attr\">data<\/span>: {\n      <span class=\"hljs-attr\">name<\/span>: newColorPalette.name,\n      <span class=\"hljs-attr\">src<\/span>: newColorPalette.src,\n      <span class=\"hljs-attr\">colorHigh<\/span>: newColorPalette.colorHigh,\n      <span class=\"hljs-attr\">colorMid<\/span>: newColorPalette.colorMid,\n      <span class=\"hljs-attr\">colorLow<\/span>: newColorPalette.colorLow,\n    },\n  })\n\n  res.status(<span class=\"hljs-number\">200<\/span>).json(result)\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>This function will create a new record in our table whenever a user successfully uploads an image to Cloudinary. We\u2019ll discuss how that works when we get to the front-end of the app. For now, we know what data we expect to receive and we\u2019ll parse that out of the request.<\/p>\n<p>Then we\u2019ll create that new record with the parsed data and return it to the front-end with a 200 success code. Now we have the back-end wrapped up! All that is left now is creating a front-end for users to interface with.<\/p>\n<h2>Setting up the front-end<\/h2>\n<p>The fun part is here! We\u2019ll finally be able to see how all of the back-end work we\u2019ve done so far connects to what users actually see. The first thing we need to do is add a file called <code>palette-generator.tsx<\/code> to the <code>pages<\/code> directory. NextJS knows what routes exist by the file names we add to this directory.<\/p>\n<p>Open that file and add the following imports and type definitions:<\/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\">import<\/span> useSwr <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'swr'<\/span>\n<span class=\"hljs-keyword\">import<\/span> Image <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'next\/image'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> styled <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'styled-components'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { WidgetLoader, Widget } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'react-cloudinary-upload-widget'<\/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> Vibrant <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"node-vibrant\"<\/span>;\n\ninterface ColorPalette {\n    <span class=\"hljs-attr\">colorHigh<\/span>: string\n    <span class=\"hljs-attr\">colorMid<\/span>: string\n    <span class=\"hljs-attr\">colorLow<\/span>: string\n}\ninterface Image extends ColorPalette {\n    <span class=\"hljs-attr\">name<\/span>: string\n    <span class=\"hljs-attr\">src<\/span>: string\n}\n\ninterface CloudinaryResult {\n    <span class=\"hljs-attr\">info<\/span>: {\n        <span class=\"hljs-attr\">url<\/span>: string\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>These give us the base for the component we\u2019re about to build so that we know what we have available and what to expect while we\u2019re working with this data. Now let\u2019s add a small styled component. When we get ready to display the color palettes users have generated, we want them to be the colors in our palette as well as have uniform sizes. Below the <code>CloudinaryResult<\/code> type, add this:<\/p>\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\">const<\/span> ColorBlot = styled.div<span class=\"hljs-string\">`\n    background-color: <span class=\"hljs-subst\">${(props: { hexCode: string }<\/span>) =&gt; props.hexCode};\n    height: 140px;\n    width: 140px;\n`<\/span>\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<p>This will be a regular <code>div<\/code> element that accepts a prop to set the color of the block and it has a set height and width. Feel free to play with this and change styles! Now let\u2019s add a little function to help us handle the different types of requests we need to make. Below the styled component we just made, add the following function:<\/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\">const<\/span> fetcher = <span class=\"hljs-function\">(<span class=\"hljs-params\">url: string, method: string = <span class=\"hljs-string\">'GET'<\/span>, data?: Image<\/span>) =&gt;<\/span> fetch(url, {\n    <span class=\"hljs-attr\">method<\/span>: method,\n    <span class=\"hljs-attr\">body<\/span>: <span class=\"hljs-built_in\">JSON<\/span>.stringify(data)\n}).then(<span class=\"hljs-function\">(<span class=\"hljs-params\">res<\/span>) =&gt;<\/span> res.json())\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>We\u2019re getting the URL, a method, and possibly some data so that we can manage GET and POST requests with this one function. The method defaults to <code>GET<\/code> unless it\u2019s specified. Then we make a regular fetch request and get the results.<\/p>\n<p>Now we can start working on the component that will be exported and seen by the user. Below the <code>fetcher<\/code> function, add this code:<\/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\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">PaletteGenerator<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n    <span class=\"hljs-keyword\">const<\/span> { data, error } = useSwr(<span class=\"hljs-string\">`\/api\/images`<\/span>, fetcher)\n    <span class=\"hljs-keyword\">const<\/span> &#91;name, setName] = useState&lt;string&gt;(<span class=\"hljs-string\">\"\"<\/span>)\n    <span class=\"hljs-keyword\">const<\/span> &#91;url, setUrl] = useState&lt;string&gt;(<span class=\"hljs-string\">\"\"<\/span>)\n    <span class=\"hljs-keyword\">const<\/span> &#91;palette, setPalette] = useState&lt;ColorPalette&gt;()\n\n    <span class=\"hljs-keyword\">if<\/span> (error) <span class=\"hljs-keyword\">return<\/span> <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>Failed to load images<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n    <span class=\"hljs-keyword\">if<\/span> (!data) <span class=\"hljs-keyword\">return<\/span> <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>Loading...<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n\n    <span class=\"hljs-keyword\">return<\/span> ()\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>Don\u2019t worry about the last return statement being empty right now because we have some things to build inside of the component before we start rendering things on the page. We\u2019re starting by defining a few different states and variables.<\/p>\n<p>The <code>useSwr<\/code> hook lets us load images in a performance-friendly way. We pass in the URL to the API we want to hit and the <code>fetcher<\/code> function so it knows where the data is coming from and any errors that come up. Then we define several states that store the info that we\u2019ll need to create a new palette record.<\/p>\n<p>After all of the variable definitions, there are a few different render states that possibly get returned to the browser. If there are any errors, then we show users the failed loading message. More importantly, while the data is being fetched from the back-end, we show users a loading state so the app doesn\u2019t crash and they have some feedback on what\u2019s happening.<\/p>\n<p>With all of this in place, let\u2019s add a few functions that will manage a lot for us.<\/p>\n<h3>Some helper functions<\/h3>\n<p>There are a couple of helper functions we need here to make the data easier to work with and to get the color palettes from images users upload. Below the loading state, add the following functions:<\/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\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">uploadImage<\/span>(<span class=\"hljs-params\">results: CloudinaryResult<\/span>) <\/span>{\n    <span class=\"hljs-keyword\">const<\/span> url = results.info.url\n\n    <span class=\"hljs-keyword\">const<\/span> img = <span class=\"hljs-built_in\">document<\/span>.createElement(<span class=\"hljs-string\">'img'<\/span>)\n    img.crossOrigin = <span class=\"hljs-string\">\"Anonymous\"<\/span>\n    img.src = url\n\n    <span class=\"hljs-keyword\">const<\/span> paletteData = <span class=\"hljs-keyword\">await<\/span> Vibrant.from(img).getPalette()\n\n    setUrl(url)\n    setPalette({\n        <span class=\"hljs-attr\">colorHigh<\/span>: paletteData.LightVibrant?.getHex() || <span class=\"hljs-string\">''<\/span>,\n        <span class=\"hljs-attr\">colorMid<\/span>: paletteData.Muted?.getHex() || <span class=\"hljs-string\">''<\/span>,\n        <span class=\"hljs-attr\">colorLow<\/span>: paletteData.DarkVibrant?.getHex() || <span class=\"hljs-string\">''<\/span>\n    })\n}\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">saveColorPalette<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n    <span class=\"hljs-keyword\">if<\/span> (palette != <span class=\"hljs-literal\">undefined<\/span>) {\n        <span class=\"hljs-keyword\">const<\/span> result = {\n            <span class=\"hljs-attr\">name<\/span>: name,\n            <span class=\"hljs-attr\">src<\/span>: url,\n            <span class=\"hljs-attr\">colorHigh<\/span>: palette.colorHigh,\n            <span class=\"hljs-attr\">colorMid<\/span>: palette.colorMid,\n            <span class=\"hljs-attr\">colorLow<\/span>: palette.colorLow\n        }\n        fetcher(<span class=\"hljs-string\">'\/api\/images'<\/span>, <span class=\"hljs-string\">'POST'<\/span>, result)\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>The <code>uploadImage<\/code> function will be used in the Cloudinary widget to get the URL of the image. Once we have that, then we have to create a new image element in order to get the color palette out of it with the <code>node-vibrant<\/code> package. The <code>crossOrigin<\/code> value will be <code>Anonymous<\/code> in this case to avoid weird CORS errors. The <code>src<\/code> will be the URL that\u2019s returned from the Cloudinary widget.<\/p>\n<p>Then this image element gets passed in <code>Vibrant<\/code> which gives us a number of different colors to choose from. I\u2019ve selected these options because they seem to give a wider range of colors, but feel free to play with these values based on the <a href=\"https:\/\/github.com\/Vibrant-Colors\/node-vibrant\"><code>node-vibrant<\/code> docs<\/a>.<\/p>\n<p>Once we have all of the colors and the image URL, we update those states we created earlier. The last helper function is <code>saveColorPalette<\/code>. This is how we make the POST request to save everything to the back-end.<\/p>\n<p>Alright! Now we can finally turn our attention to what is displayed to our users. All that\u2019s left is adding the elements in that empty return statement.<\/p>\n<h3>Adding the image uploader<\/h3>\n<p>We\u2019ll start with the image uploader and a few fields. Inside of that empty return statement, add the following elements:<\/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\">return<\/span> (\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"text\"<\/span> <span class=\"hljs-attr\">onChange<\/span>=<span class=\"hljs-string\">{e<\/span> =&gt;<\/span> setName(e.currentTarget.value)} \/&gt;\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">WidgetLoader<\/span> \/&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Widget<\/span>\n            <span class=\"hljs-attr\">sources<\/span>=<span class=\"hljs-string\">{&#91;<\/span>'<span class=\"hljs-attr\">local<\/span>', '<span class=\"hljs-attr\">camera<\/span>']}\n            <span class=\"hljs-attr\">cloudName<\/span>=<span class=\"hljs-string\">{<\/span>'<span class=\"hljs-attr\">your_cloud_name_here<\/span>'}\n            <span class=\"hljs-attr\">uploadPreset<\/span>=<span class=\"hljs-string\">{<\/span>'<span class=\"hljs-attr\">your_upload_preset_here<\/span>'}\n            <span class=\"hljs-attr\">buttonText<\/span>=<span class=\"hljs-string\">{<\/span>'<span class=\"hljs-attr\">Upload<\/span> <span class=\"hljs-attr\">Image<\/span>'}\n            <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">{{<\/span>\n                <span class=\"hljs-attr\">color:<\/span> '<span class=\"hljs-attr\">white<\/span>',\n                <span class=\"hljs-attr\">border:<\/span> '<span class=\"hljs-attr\">none<\/span>',\n                <span class=\"hljs-attr\">width:<\/span> '<span class=\"hljs-attr\">120px<\/span>',\n                <span class=\"hljs-attr\">backgroundColor:<\/span> '<span class=\"hljs-attr\">green<\/span>',\n                <span class=\"hljs-attr\">borderRadius:<\/span> '<span class=\"hljs-attr\">4px<\/span>',\n                <span class=\"hljs-attr\">height:<\/span> '<span class=\"hljs-attr\">25px<\/span>',\n            }}\n            <span class=\"hljs-attr\">folder<\/span>=<span class=\"hljs-string\">{<\/span>'<span class=\"hljs-attr\">color_palettes<\/span>'}\n            <span class=\"hljs-attr\">onSuccess<\/span>=<span class=\"hljs-string\">{uploadImage}<\/span>\n        \/&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">{{<\/span> <span class=\"hljs-attr\">display:<\/span> '<span class=\"hljs-attr\">block<\/span>' }} <span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{saveColorPalette}<\/span>&gt;<\/span>Generate color palette<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/&gt;<\/span><\/span>\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>This is how we\u2019ll get all of the info we need from a user to create and save their generated color palettes. We start by getting a name for the palette and updating the corresponding state. Then we insert the Cloudinary upload widget. (This is the reason we installed axios as the widget has that dependency.)<\/p>\n<p>In the <code>Widget<\/code> element, there are quite a few props you can work with, but the main two you need to update are the <code>cloudName<\/code> and <code>uploadPreset<\/code>. You can find those values in your Cloudinary dashboard. Now you can see where the <code>uploadImage<\/code> helper function comes in. Every time a user uploads an image, this function will be called. We\u2019ll set up a way to preview the palettes before saving them in the next section.<\/p>\n<p>For now, note that the last element is a button that calls the <code>saveColorPalette<\/code> function. When a user clicks this button, it will save the color palette, name, and image as a new record in the database. If you run the app now with <code>yarn dev<\/code> and navigate to the <code>palette-generator<\/code> route, you should see something similar to this.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/mediadevs\/image\/upload\/c_limit,w_2000\/f_auto\/q_auto\/v1646797661\/e-603fc55d218a650069f5228b\/pcj6zewmg79khdickkkz.png\" alt=\"upload widget, name input, and save button\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1688\" height=\"130\"\/><\/p>\n<p>Go ahead and upload a few images and save the palettes. We\u2019ll need that data for our final step!<\/p>\n<h3>Display the color palette<\/h3>\n<p>Users will probably want to see the color palettes before they save anything, so based on the states in the app, we\u2019ll display a preview of the data. Right below the button element, add this code:<\/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\">{\n    url != <span class=\"hljs-string\">\"\"<\/span> &amp;&amp; palette != <span class=\"hljs-literal\">undefined<\/span> &amp;&amp;\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h2<\/span>&gt;<\/span>{name}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h2<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Image<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">{url}<\/span> <span class=\"hljs-attr\">alt<\/span>=<span class=\"hljs-string\">{name}<\/span> <span class=\"hljs-attr\">height<\/span>=<span class=\"hljs-string\">{240}<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">{240}<\/span> \/&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ColorBlot<\/span> <span class=\"hljs-attr\">hexCode<\/span>=<span class=\"hljs-string\">{palette.colorHigh}<\/span>&gt;<\/span>{palette.colorHigh}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">ColorBlot<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ColorBlot<\/span> <span class=\"hljs-attr\">hexCode<\/span>=<span class=\"hljs-string\">{palette.colorMid}<\/span>&gt;<\/span>{palette.colorMid}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">ColorBlot<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ColorBlot<\/span> <span class=\"hljs-attr\">hexCode<\/span>=<span class=\"hljs-string\">{palette.colorLow}<\/span>&gt;<\/span>{palette.colorLow}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">ColorBlot<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\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<p>This checks to see if there\u2019s data defined for the current states and then displays the results. So upload an image and before you save it, take a look at the page. You should see something similar to this:<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/mediadevs\/image\/upload\/c_limit,w_2000\/f_auto\/q_auto\/v1646798016\/e-603fc55d218a650069f5228b\/ijdy01ymcfmh0zf38rhd.png\" alt=\"color palette preview\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1330\" height=\"1528\"\/><\/p>\n<p>Now we can see what all of our work has been leading up to! Go ahead and save this so you have more data to look at. The last step is displaying all of the palettes returned from our images API. We\u2019ll take that data and map it to the same elements you\u2019ve seen.<\/p>\n<p>The last piece of code we need to add is the display for all of the palettes. Just below the preview code we just added inside of the return statement, add this code:<\/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\">{\n    data.map(<span class=\"hljs-function\">(<span class=\"hljs-params\">image: Image<\/span>) =&gt;<\/span> (\n        <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">key<\/span>=<span class=\"hljs-string\">{image.name}<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h2<\/span>&gt;<\/span>{image.name}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h2<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Image<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">{image.src}<\/span> <span class=\"hljs-attr\">alt<\/span>=<span class=\"hljs-string\">{image.name}<\/span> <span class=\"hljs-attr\">height<\/span>=<span class=\"hljs-string\">{240}<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">{240}<\/span> \/&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ColorBlot<\/span> <span class=\"hljs-attr\">hexCode<\/span>=<span class=\"hljs-string\">{image.colorHigh}<\/span>&gt;<\/span>{image.colorHigh}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">ColorBlot<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ColorBlot<\/span> <span class=\"hljs-attr\">hexCode<\/span>=<span class=\"hljs-string\">{image.colorMid}<\/span>&gt;<\/span>{image.colorMid}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">ColorBlot<\/span>&gt;<\/span>\n            <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">ColorBlot<\/span> <span class=\"hljs-attr\">hexCode<\/span>=<span class=\"hljs-string\">{image.colorLow}<\/span>&gt;<\/span>{image.colorLow}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">ColorBlot<\/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-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>Now if you run the app and take a look at the <code>palette-generator<\/code> route, you should see all of the images and their associated color palettes on the page! That might look like this.<\/p>\n<p><img decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/mediadevs\/image\/upload\/c_limit,w_2000\/f_auto\/q_auto\/v1646798428\/e-603fc55d218a650069f5228b\/qaasblhcothnfx7oxzqx.png\" alt=\"all of the color palettes rendered\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"1450\" height=\"1504\"\/><\/p>\n<p>We\u2019re finished with this app now!<\/p>\n<h2>Finished code<\/h2>\n<p>If you want to check out the code for the whole project, take a look at <a href=\"https:\/\/github.com\/flippedcoder\/media-projects\/tree\/main\/image-color-palette\">this repo<\/a>.<\/p>\n<p>You can also look at the front-end functionality in <a href=\"https:\/\/codesandbox.io\/s\/friendly-resonance-jmnxbs\">this Code Sandbox<\/a>. Just make sure you update the <code>cloudName<\/code> and <code>uploadPreset<\/code> values with your own.<\/p>\n<p>&lt;CodeSandBox\ntitle=\u201cfriendly-resonance-jmnxbs\u201d\nid=\u201cfriendly-resonance-jmnxbs\u201d\n\/&gt;<\/p>\n<h2>Conclusion<\/h2>\n<p>Sometimes finding matching colors is hard, but finding an image that appeals to you is pretty easy. You could use these color palettes for everything from web design to interior design. Having an app like this can enable your users to do a lot of different tasks quickly and easily.<\/p>\n<\/div>","protected":false},"excerpt":{"rendered":"","protected":false},"author":41,"featured_media":28039,"comment_status":"","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[134,370,212,371],"class_list":["post-28038","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-guest-post","tag-image","tag-next-js","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>Creating Color Palettes Based on Images<\/title>\n<meta name=\"description\" content=\"There are times that finding colors that look good together can be really hard and you might look at pictures for inspiration. In this tutorial, we&#039;re going to create a tool that lets us get a color palette based on any image we upload.\" \/>\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\/creating-color-palettes-based-on-images\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Creating Color Palettes Based on Images\" \/>\n<meta property=\"og:description\" content=\"There are times that finding colors that look good together can be really hard and you might look at pictures for inspiration. In this tutorial, we&#039;re going to create a tool that lets us get a color palette based on any image we upload.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/guest_post\/creating-color-palettes-based-on-images\/\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2022-03-24T20:17:10+00:00\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925595\/Web_Assets\/blog\/f286cd636f49f0500c03ae691d86dba7919f636a-8192x6144-1_28039d7ff6\/f286cd636f49f0500c03ae691d86dba7919f636a-8192x6144-1_28039d7ff6.jpg?_i=AA\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"NewsArticle\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/creating-color-palettes-based-on-images\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/creating-color-palettes-based-on-images\/\"},\"author\":{\"name\":\"\",\"@id\":\"\"},\"headline\":\"Creating Color Palettes Based on Images\",\"datePublished\":\"2022-03-24T20:17:10+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/creating-color-palettes-based-on-images\/\"},\"wordCount\":6,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/creating-color-palettes-based-on-images\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925595\/Web_Assets\/blog\/f286cd636f49f0500c03ae691d86dba7919f636a-8192x6144-1_28039d7ff6\/f286cd636f49f0500c03ae691d86dba7919f636a-8192x6144-1_28039d7ff6.jpg?_i=AA\",\"keywords\":[\"Guest Post\",\"Image\",\"Next.js\",\"Under Review\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2022\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/creating-color-palettes-based-on-images\/\",\"url\":\"https:\/\/cloudinary.com\/blog\/guest_post\/creating-color-palettes-based-on-images\/\",\"name\":\"Creating Color Palettes Based on Images\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/creating-color-palettes-based-on-images\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/creating-color-palettes-based-on-images\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925595\/Web_Assets\/blog\/f286cd636f49f0500c03ae691d86dba7919f636a-8192x6144-1_28039d7ff6\/f286cd636f49f0500c03ae691d86dba7919f636a-8192x6144-1_28039d7ff6.jpg?_i=AA\",\"datePublished\":\"2022-03-24T20:17:10+00:00\",\"description\":\"There are times that finding colors that look good together can be really hard and you might look at pictures for inspiration. In this tutorial, we're going to create a tool that lets us get a color palette based on any image we upload.\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/creating-color-palettes-based-on-images\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/guest_post\/creating-color-palettes-based-on-images\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/creating-color-palettes-based-on-images\/#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925595\/Web_Assets\/blog\/f286cd636f49f0500c03ae691d86dba7919f636a-8192x6144-1_28039d7ff6\/f286cd636f49f0500c03ae691d86dba7919f636a-8192x6144-1_28039d7ff6.jpg?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925595\/Web_Assets\/blog\/f286cd636f49f0500c03ae691d86dba7919f636a-8192x6144-1_28039d7ff6\/f286cd636f49f0500c03ae691d86dba7919f636a-8192x6144-1_28039d7ff6.jpg?_i=AA\",\"width\":8192,\"height\":6144},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/creating-color-palettes-based-on-images\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Creating Color Palettes Based on Images\"}]},{\"@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":"Creating Color Palettes Based on Images","description":"There are times that finding colors that look good together can be really hard and you might look at pictures for inspiration. In this tutorial, we're going to create a tool that lets us get a color palette based on any image we upload.","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\/creating-color-palettes-based-on-images\/","og_locale":"en_US","og_type":"article","og_title":"Creating Color Palettes Based on Images","og_description":"There are times that finding colors that look good together can be really hard and you might look at pictures for inspiration. In this tutorial, we're going to create a tool that lets us get a color palette based on any image we upload.","og_url":"https:\/\/cloudinary.com\/blog\/guest_post\/creating-color-palettes-based-on-images\/","og_site_name":"Cloudinary Blog","article_published_time":"2022-03-24T20:17:10+00:00","twitter_card":"summary_large_image","twitter_image":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925595\/Web_Assets\/blog\/f286cd636f49f0500c03ae691d86dba7919f636a-8192x6144-1_28039d7ff6\/f286cd636f49f0500c03ae691d86dba7919f636a-8192x6144-1_28039d7ff6.jpg?_i=AA","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/creating-color-palettes-based-on-images\/#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/creating-color-palettes-based-on-images\/"},"author":{"name":"","@id":""},"headline":"Creating Color Palettes Based on Images","datePublished":"2022-03-24T20:17:10+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/creating-color-palettes-based-on-images\/"},"wordCount":6,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/creating-color-palettes-based-on-images\/#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925595\/Web_Assets\/blog\/f286cd636f49f0500c03ae691d86dba7919f636a-8192x6144-1_28039d7ff6\/f286cd636f49f0500c03ae691d86dba7919f636a-8192x6144-1_28039d7ff6.jpg?_i=AA","keywords":["Guest Post","Image","Next.js","Under Review"],"inLanguage":"en-US","copyrightYear":"2022","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/creating-color-palettes-based-on-images\/","url":"https:\/\/cloudinary.com\/blog\/guest_post\/creating-color-palettes-based-on-images\/","name":"Creating Color Palettes Based on Images","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/creating-color-palettes-based-on-images\/#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/creating-color-palettes-based-on-images\/#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925595\/Web_Assets\/blog\/f286cd636f49f0500c03ae691d86dba7919f636a-8192x6144-1_28039d7ff6\/f286cd636f49f0500c03ae691d86dba7919f636a-8192x6144-1_28039d7ff6.jpg?_i=AA","datePublished":"2022-03-24T20:17:10+00:00","description":"There are times that finding colors that look good together can be really hard and you might look at pictures for inspiration. In this tutorial, we're going to create a tool that lets us get a color palette based on any image we upload.","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/creating-color-palettes-based-on-images\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/guest_post\/creating-color-palettes-based-on-images\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/creating-color-palettes-based-on-images\/#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925595\/Web_Assets\/blog\/f286cd636f49f0500c03ae691d86dba7919f636a-8192x6144-1_28039d7ff6\/f286cd636f49f0500c03ae691d86dba7919f636a-8192x6144-1_28039d7ff6.jpg?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681925595\/Web_Assets\/blog\/f286cd636f49f0500c03ae691d86dba7919f636a-8192x6144-1_28039d7ff6\/f286cd636f49f0500c03ae691d86dba7919f636a-8192x6144-1_28039d7ff6.jpg?_i=AA","width":8192,"height":6144},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/creating-color-palettes-based-on-images\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Creating Color Palettes Based on Images"}]},{"@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\/v1681925595\/Web_Assets\/blog\/f286cd636f49f0500c03ae691d86dba7919f636a-8192x6144-1_28039d7ff6\/f286cd636f49f0500c03ae691d86dba7919f636a-8192x6144-1_28039d7ff6.jpg?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/28038","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=28038"}],"version-history":[{"count":0,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/28038\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/28039"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=28038"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=28038"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=28038"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}