{"id":28298,"date":"2022-05-18T14:01:37","date_gmt":"2022-05-18T14:01:37","guid":{"rendered":"http:\/\/generate-custom-art-with-react"},"modified":"2022-05-18T14:01:37","modified_gmt":"2022-05-18T14:01:37","slug":"generate-custom-art-with-react","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/guest_post\/generate-custom-art-with-react\/","title":{"rendered":"Generate Custom Art with React"},"content":{"rendered":"<div class=\"wp-block-cloudinary-markdown \"><p>Art is one of the ways you can express yourself freely. As someone working in tech, it might be hard to find the time to take up painting or drawing when you\u2019re trying to constantly stay on top of all the new tools coming out. Luckily for us, there are some creative ways to combine tech and art to make expressive visuals through data visualization.<\/p>\n<p>In this tutorial, we\u2019re going to make a React app to generate custom artwork with the <a href=\"https:\/\/d3js.org\/\">d3.js<\/a> and changing data. This will help us learn more about an advanced JavaScript topic, play with a new library, and make some art in the process.<\/p>\n<h2>Set up the React app<\/h2>\n<p>Let\u2019s start by creating a brand new React TypeScript app with the following command:<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">$ npx create-react-app art-generator --template typescript\n<\/code><\/span><\/pre>\n<p>Since we used <code>npx<\/code> to create the app, we\u2019ll be working with <code>npm<\/code> commands throughout the rest of the project instead of <code>yarn<\/code>. If you do prefer <code>yarn<\/code>, you can create the app with <code>yarn create-react-app art-generator --template typescript<\/code>.<\/p>\n<p>With all of the files and folders in place for the React app, we can install the packages we need.<\/p>\n<pre class=\"js-syntax-highlighted\"><span><code class=\"hljs shcb-wrap-lines\">$ npm i d3 @types\/d3 html-to-image\n<\/code><\/span><\/pre>\n<p>We\u2019re getting the <code>d3<\/code> library installed so we can make the art based on some data we get from the user and we\u2019re using <code>html-to-image<\/code> to save that art to Cloudinary. If you don\u2019t have a Cloudinary account, you can <a href=\"https:\/\/cloudinary.com\/users\/register\/free\">sign up for a free one here<\/a>. You\u2019ll need your cloud name and upload preset from your account settings in order to make the API call to upload the art images.<\/p>\n<p>Now that everything is set up and we have the credentials we need to upload images, we can start working on a new component to get some user input to create the images.<\/p>\n<h2>Add the generated art component<\/h2>\n<p>The first thing we need to do is create a new folder in the <code>src<\/code> folder called <code>components<\/code>. Inside the <code>components<\/code> folder, add a new file called <code>Art.tsx<\/code>. This is where we will write all of the code for this art functionality. To make sure that this component is rendered, we\u2019re going to update the <code>App.tsx<\/code> file to import this component we are going to make in a bit.<\/p>\n<p>So open your <code>App.tsx<\/code> file and edit it to look like this:<\/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\">\/\/ App.tsx<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> Art <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/components\/Art\"<\/span>;\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">App<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n  <span class=\"hljs-keyword\">return<\/span> <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">Art<\/span> \/&gt;<\/span><\/span>;\n}\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> App;\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>It trims down a lot of the boilerplate code and imports to the exact thing we need. If you try running the app now, you\u2019ll get an error because we haven\u2019t made the <code>Art<\/code> component yet. That\u2019s what we are about to make.<\/p>\n<h3>The art component<\/h3>\n<p>Open the <code>Art.tsx<\/code> file and add the following imports.<\/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-comment\">\/\/ Art.tsx<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> { useEffect, useRef, useState } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { toPng } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"html-to-image\"<\/span>;\n<span class=\"hljs-keyword\">import<\/span> * <span class=\"hljs-keyword\">as<\/span> d3 <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"d3\"<\/span>;\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>This is all we\u2019re going to need to create the artwork from user input. Next, we need to define the type for the user input data since we\u2019re building a TypeScript app. Beneath the import statements, add this type declaration.<\/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-comment\">\/\/ Art.tsx<\/span>\n...\n\ntype ConfigDataType = {\n  <span class=\"hljs-attr\">color<\/span>: string,\n  <span class=\"hljs-attr\">svgHeight<\/span>: number,\n  <span class=\"hljs-attr\">svgWidth<\/span>: number,\n  <span class=\"hljs-attr\">xMultiplier<\/span>: number,\n  <span class=\"hljs-attr\">yMultiplier<\/span>: number,\n  <span class=\"hljs-attr\">othWidth<\/span>: number,\n  <span class=\"hljs-attr\">data<\/span>: number&#91;],\n};\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>These are all of the values we\u2019ll get from the user to make their art and they\u2019ll be used in <code>d3<\/code> as values for attributes. In order to use <code>d3<\/code>, we need to write a function outside of the actual component to set up what and how <code>d3<\/code> will draw elements on the page.<\/p>\n<h3>Set up d3.js<\/h3>\n<p>Since we know the type of data we expect to be used in <code>d3<\/code>, let\u2019s go ahead and write a function to handle the <code>d3<\/code> drawings below the type definition.<\/p>\n<pre class=\"js-syntax-highlighted\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ Art.tsx<\/span>\n...\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">drawArt<\/span><span class=\"hljs-params\">(configData: ConfigDataType)<\/span> <\/span>{\n  <span class=\"hljs-keyword\">const<\/span> svg = d3\n    .select(<span class=\"hljs-string\">\"#art\"<\/span>)\n    .append(<span class=\"hljs-string\">\"svg\"<\/span>)\n    .attr(<span class=\"hljs-string\">\"width\"<\/span>, configData.svgWidth)\n    .attr(<span class=\"hljs-string\">\"height\"<\/span>, configData.svgHeight)\n    .style(<span class=\"hljs-string\">\"margin-left\"<\/span>, <span class=\"hljs-number\">100<\/span>);\n\n  svg\n    .selectAll(<span class=\"hljs-string\">\"rect\"<\/span>)\n    .data(configData.data)\n    .enter()\n    .append(<span class=\"hljs-string\">\"rect\"<\/span>)\n    .attr(<span class=\"hljs-string\">\"x\"<\/span>, (d, i) =&gt; i * configData.xMultiplier)\n    .attr(<span class=\"hljs-string\">\"y\"<\/span>, (d, i) =&gt; <span class=\"hljs-number\">300<\/span> - configData.yMultiplier * d)\n    .attr(<span class=\"hljs-string\">\"width\"<\/span>, configData.othWidth)\n    .attr(<span class=\"hljs-string\">\"height\"<\/span>, (d, i) =&gt; d * configData.yMultiplier)\n    .attr(<span class=\"hljs-string\">\"fill\"<\/span>, configData.color);\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\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n<p>This is a very simple <code>d3<\/code> drawing based on the user input. Honestly, <code>d3<\/code> has a pretty steep learning curve and I usually have the docs open the entire time I\u2019m working. So let\u2019s walk through this code in a little more detail.<\/p>\n<p>First, this function selects an HTML element with the <code>art<\/code> id and appends an <code>svg<\/code> HTML element inside it. (We\u2019ll make the <code>art<\/code> element in just a bit when we get to the rendered part of the component.) Then we set the <code>height<\/code> and <code>width<\/code> attributes for the <code>svg<\/code> element based on the user input. After that, we add a little <code>style<\/code> to the <code>svg<\/code> to give it a margin on the left.<\/p>\n<p>Now that the <code>svg<\/code> element is there, we can start adding the art inside of it. We start by preemptively selecting all of the <code>rect<\/code> elements that will be created in the <code>svg<\/code>. Then we get the data array the user entered and start appending <code>rect<\/code> elements with attributes for the <code>x<\/code>, <code>y<\/code>, <code>width<\/code>, <code>height<\/code>, and <code>fill<\/code> attributes set based on the user input.<\/p>\n<p>With this function, <code>d3<\/code> is ready to draw something for us.<\/p>\n<h3>Set up the component functions<\/h3>\n<p>We\u2019re <em>almost<\/em> ready to render something on the page when the app runs, but we have to set up a few things for the component to work correctly. Below the <code>drawArt<\/code> function, add the following code. It looks like a lot, but we\u2019ll explain what\u2019s going on.<\/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-comment\">\/\/ Art.tsx<\/span>\n...\n\nexport <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">Art<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n  <span class=\"hljs-keyword\">const<\/span> d3ContainerRef = useRef();\n  <span class=\"hljs-keyword\">const<\/span> &#91;configData, setConfigData] = useState&lt;ConfigDataType&gt;({\n    <span class=\"hljs-attr\">color<\/span>: <span class=\"hljs-string\">\"black\"<\/span>,\n    <span class=\"hljs-attr\">svgHeight<\/span>: <span class=\"hljs-number\">300<\/span>,\n    <span class=\"hljs-attr\">svgWidth<\/span>: <span class=\"hljs-number\">700<\/span>,\n    <span class=\"hljs-attr\">xMultiplier<\/span>: <span class=\"hljs-number\">90<\/span>,\n    <span class=\"hljs-attr\">yMultiplier<\/span>: <span class=\"hljs-number\">15<\/span>,\n    <span class=\"hljs-attr\">othWidth<\/span>: <span class=\"hljs-number\">65<\/span>,\n    <span class=\"hljs-attr\">data<\/span>: &#91;<span class=\"hljs-number\">12<\/span>, <span class=\"hljs-number\">5<\/span>, <span class=\"hljs-number\">6<\/span>, <span class=\"hljs-number\">6<\/span>, <span class=\"hljs-number\">9<\/span>, <span class=\"hljs-number\">10<\/span>],\n  });\n\n  useEffect(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n    drawArt(configData);\n  }, &#91;configData]);\n\n  <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">updateArt<\/span>(<span class=\"hljs-params\">e: any<\/span>) <\/span>{\n    e.preventDefault();\n\n    <span class=\"hljs-keyword\">const<\/span> newConfigs = {\n      <span class=\"hljs-attr\">color<\/span>: e.target.color?.value || configData.color,\n      <span class=\"hljs-attr\">svgHeight<\/span>: e.target.svgHeight?.value || configData.svgHeight,\n      <span class=\"hljs-attr\">svgWidth<\/span>: e.target.svgWidth?.value || configData.svgWidth,\n      <span class=\"hljs-attr\">xMultiplier<\/span>: e.target.xMultiplier?.value || configData.xMultiplier,\n      <span class=\"hljs-attr\">yMultiplier<\/span>: e.target.yMultiplier?.value || configData.yMultiplier,\n      <span class=\"hljs-attr\">othWidth<\/span>: e.target.othWidth?.value || configData.othWidth,\n      <span class=\"hljs-attr\">data<\/span>: e.target.data?.value || configData.data,\n    };\n\n    setConfigData(newConfigs);\n  }\n\n  <span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">submit<\/span>(<span class=\"hljs-params\">e: any<\/span>) <\/span>{\n    e.preventDefault();\n\n    <span class=\"hljs-keyword\">if<\/span> (d3ContainerRef.current === <span class=\"hljs-literal\">null<\/span>) {\n      <span class=\"hljs-keyword\">return<\/span>;\n    }\n\n    <span class=\"hljs-comment\">\/\/ @ts-ignore<\/span>\n    <span class=\"hljs-keyword\">const<\/span> dataUrl = <span class=\"hljs-keyword\">await<\/span> toPng(d3ContainerRef.current, { <span class=\"hljs-attr\">cacheBust<\/span>: <span class=\"hljs-literal\">true<\/span> });\n\n    <span class=\"hljs-keyword\">const<\/span> uploadApi = <span class=\"hljs-string\">`https:\/\/api.cloudinary.com\/v1_1\/your_cloud_name\/image\/upload`<\/span>;\n\n    <span class=\"hljs-keyword\">const<\/span> formData = <span class=\"hljs-keyword\">new<\/span> FormData();\n    formData.append(<span class=\"hljs-string\">\"file\"<\/span>, dataUrl);\n    formData.append(<span class=\"hljs-string\">\"upload_preset\"<\/span>, <span class=\"hljs-string\">\"your_upload_preset_value\"<\/span>);\n\n    <span class=\"hljs-keyword\">await<\/span> fetch(uploadApi, {\n      <span class=\"hljs-attr\">method<\/span>: <span class=\"hljs-string\">\"POST\"<\/span>,\n      <span class=\"hljs-attr\">body<\/span>: formData,\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>We start by creating a ref for the <code>art<\/code> element we targeted in the <code>drawArt<\/code> function. Then we set the initial state of the user input so that something shows on the screen when the app starts up. Next, you can see that we call the <code>drawArt<\/code> function each time the <code>configData<\/code> is changed. This is how we keep appending elements to the <code>svg<\/code>.<\/p>\n<p>Then we have a couple of helper functions. The <code>updateArt<\/code> function takes the values from the user input (we\u2019ll make the form for this shortly) and calls <code>setConfigData<\/code> to update the state, which triggers the <code>drawArt<\/code> function. This will only be called when a button is clicked to prevent unexpected re-renders.<\/p>\n<p>The <code>submit<\/code> function is what will get called when a user decides that they want to save the image to Cloudinary. We start by keeping the page from refreshing, then we make sure the ref element isn\u2019t empty. Next, we capture the image as a PNG and make a variable for the Cloudinary upload API.<\/p>\n<p>After that, we make a new <code>FormData<\/code> object to hold the values we need to upload an image to Cloudinary. Once the data is ready, we called the <code>fetch<\/code> method to submit a POST request to the API. That\u2019s all of the functions we need for the component so the only thing left is the return statement.<\/p>\n<h3>Add the rendered elements<\/h3>\n<p>We need a form to get the user input, a couple of buttons, and the ref element. Add this code below all of the component functions we just defined.<\/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-comment\">\/\/ Art.tsx<\/span>\n...\n\n<span class=\"hljs-comment\">\/\/ Art()<\/span>\n...\n\nreturn (\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">form<\/span> <span class=\"hljs-attr\">onSubmit<\/span>=<span class=\"hljs-string\">{updateArt}<\/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\">label<\/span> <span class=\"hljs-attr\">htmlFor<\/span>=<span class=\"hljs-string\">\"color\"<\/span>&gt;<\/span>Color<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&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\">name<\/span>=<span class=\"hljs-string\">\"color\"<\/span> <span class=\"hljs-attr\">onChange<\/span>=<span class=\"hljs-string\">{(e)<\/span> =&gt;<\/span> e.target.value} \/&gt;\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>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span> <span class=\"hljs-attr\">htmlFor<\/span>=<span class=\"hljs-string\">\"svgHeight\"<\/span>&gt;<\/span>SVG Height<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/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\">\"number\"<\/span>\n            <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"svgHeight\"<\/span>\n            <span class=\"hljs-attr\">onChange<\/span>=<span class=\"hljs-string\">{(e)<\/span> =&gt;<\/span> e.target.value}\n          \/&gt;\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>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span> <span class=\"hljs-attr\">htmlFor<\/span>=<span class=\"hljs-string\">\"svgWidth\"<\/span>&gt;<\/span>SVG Width<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/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\">\"number\"<\/span>\n            <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"svgWidth\"<\/span>\n            <span class=\"hljs-attr\">onChange<\/span>=<span class=\"hljs-string\">{(e)<\/span> =&gt;<\/span> e.target.value}\n          \/&gt;\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>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span> <span class=\"hljs-attr\">htmlFor<\/span>=<span class=\"hljs-string\">\"xMultiplier\"<\/span>&gt;<\/span>X Multiplier<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/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\">\"number\"<\/span>\n            <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"xMultiplier\"<\/span>\n            <span class=\"hljs-attr\">onChange<\/span>=<span class=\"hljs-string\">{(e)<\/span> =&gt;<\/span> e.target.value}\n          \/&gt;\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>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span> <span class=\"hljs-attr\">htmlFor<\/span>=<span class=\"hljs-string\">\"yMultiplier\"<\/span>&gt;<\/span>Y Multiplier<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/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\">\"number\"<\/span>\n            <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"yMultiplier\"<\/span>\n            <span class=\"hljs-attr\">onChange<\/span>=<span class=\"hljs-string\">{(e)<\/span> =&gt;<\/span> e.target.value}\n          \/&gt;\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>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span> <span class=\"hljs-attr\">htmlFor<\/span>=<span class=\"hljs-string\">\"othWidth\"<\/span>&gt;<\/span>Other Width<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/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\">\"number\"<\/span>\n            <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">\"othWidth\"<\/span>\n            <span class=\"hljs-attr\">onChange<\/span>=<span class=\"hljs-string\">{(e)<\/span> =&gt;<\/span> e.target.value}\n          \/&gt;\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>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span> <span class=\"hljs-attr\">htmlFor<\/span>=<span class=\"hljs-string\">\"data\"<\/span>&gt;<\/span>Some Numbers<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&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\">name<\/span>=<span class=\"hljs-string\">\"data\"<\/span> <span class=\"hljs-attr\">onChange<\/span>=<span class=\"hljs-string\">{(e)<\/span> =&gt;<\/span> e.target.value} \/&gt;\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"submit\"<\/span>&gt;<\/span>See Art<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">form<\/span>&gt;<\/span>\n      {\/* @ts-ignore *\/}\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"art\"<\/span> <span class=\"hljs-attr\">ref<\/span>=<span class=\"hljs-string\">{d3ContainerRef}<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"submit\"<\/span> <span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{submit}<\/span>&gt;<\/span>\n        Save picture\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;\/&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>We have a form here with several input fields and a button that calls the <code>updateArt<\/code> method. Then we have the <code>art<\/code> element we select in the <code>drawArt<\/code> function. Finally, there\u2019s the button to upload the image whenever the user is ready.<\/p>\n<p>Now you can run the app with <code>npm start<\/code> and 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\/v1652751456\/e-603fc55d218a650069f5228b\/h0952k2d74ozecd5vvpx.png\" alt=\"the artwork\" loading=\"lazy\" class=\"c-transformed-asset\"  width=\"2000\" height=\"1192\"\/><\/p>\n<p>That\u2019s it! Now you can play around with the values through the form or you can work on the code and make fancier data visualizations.<\/p>\n<h2>Finished code<\/h2>\n<p>You can check out the complete code in the <a href=\"https:\/\/github.com\/flippedcoder\/media-projects\/tree\/main\/art-generator\">art-generator folder of this repo<\/a> or this <a href=\"https:\/\/codesandbox.io\/s\/peaceful-murdock-nlbgpg\">CodeSandbox<\/a><\/p>\n<\/div>\n  \n  <div class=\"wp-block-cloudinary-code-sandbox \">\n    <iframe\n      src=\"https:\/\/codesandbox.io\/embed\/peaceful-murdock-nlbgpg?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=\"peaceful-murdock-nlbgpg\"\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  <div class=\"wp-block-cloudinary-markdown \"><h2>Conclusion<\/h2>\n<p>Yes, there is beauty in a simple bar chart. When is the last time you were actually able to make a simple bar chart for an app? No fancy transitions, no labels, no real scaling, or showing anything meaningful? That\u2019s a special kind of freedom and it does give you a chance to think about other things you can do with your data.<\/p>\n<\/div>","protected":false},"excerpt":{"rendered":"","protected":false},"author":41,"featured_media":28299,"comment_status":"","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[134,370,246,371],"class_list":["post-28298","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-guest-post","tag-image","tag-react","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>Generate Custom Art with React<\/title>\n<meta name=\"description\" content=\"There is beauty in data even though we don&#039;t get to see it often. If you&#039;re wanting to learn how to make data visualizations with d3.js, let&#039;s start by making simple art.\" \/>\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\/generate-custom-art-with-react\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Generate Custom Art with React\" \/>\n<meta property=\"og:description\" content=\"There is beauty in data even though we don&#039;t get to see it often. If you&#039;re wanting to learn how to make data visualizations with d3.js, let&#039;s start by making simple art.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/guest_post\/generate-custom-art-with-react\/\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2022-05-18T14:01:37+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\/v1681924886\/Web_Assets\/blog\/ec6325f1ba87dba61fdfd8bf3ddc3e16fe082eda-6000x4000-1_28299b91e9\/ec6325f1ba87dba61fdfd8bf3ddc3e16fe082eda-6000x4000-1_28299b91e9.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\/generate-custom-art-with-react\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/generate-custom-art-with-react\/\"},\"author\":{\"name\":\"\",\"@id\":\"\"},\"headline\":\"Generate Custom Art with React\",\"datePublished\":\"2022-05-18T14:01:37+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/generate-custom-art-with-react\/\"},\"wordCount\":5,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/generate-custom-art-with-react\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924886\/Web_Assets\/blog\/ec6325f1ba87dba61fdfd8bf3ddc3e16fe082eda-6000x4000-1_28299b91e9\/ec6325f1ba87dba61fdfd8bf3ddc3e16fe082eda-6000x4000-1_28299b91e9.jpg?_i=AA\",\"keywords\":[\"Guest Post\",\"Image\",\"React\",\"Under Review\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2022\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/generate-custom-art-with-react\/\",\"url\":\"https:\/\/cloudinary.com\/blog\/guest_post\/generate-custom-art-with-react\/\",\"name\":\"Generate Custom Art with React\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/generate-custom-art-with-react\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/generate-custom-art-with-react\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924886\/Web_Assets\/blog\/ec6325f1ba87dba61fdfd8bf3ddc3e16fe082eda-6000x4000-1_28299b91e9\/ec6325f1ba87dba61fdfd8bf3ddc3e16fe082eda-6000x4000-1_28299b91e9.jpg?_i=AA\",\"datePublished\":\"2022-05-18T14:01:37+00:00\",\"description\":\"There is beauty in data even though we don't get to see it often. If you're wanting to learn how to make data visualizations with d3.js, let's start by making simple art.\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/generate-custom-art-with-react\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/guest_post\/generate-custom-art-with-react\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/generate-custom-art-with-react\/#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924886\/Web_Assets\/blog\/ec6325f1ba87dba61fdfd8bf3ddc3e16fe082eda-6000x4000-1_28299b91e9\/ec6325f1ba87dba61fdfd8bf3ddc3e16fe082eda-6000x4000-1_28299b91e9.jpg?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924886\/Web_Assets\/blog\/ec6325f1ba87dba61fdfd8bf3ddc3e16fe082eda-6000x4000-1_28299b91e9\/ec6325f1ba87dba61fdfd8bf3ddc3e16fe082eda-6000x4000-1_28299b91e9.jpg?_i=AA\",\"width\":6000,\"height\":4000},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/guest_post\/generate-custom-art-with-react\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Generate Custom Art with React\"}]},{\"@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":"Generate Custom Art with React","description":"There is beauty in data even though we don't get to see it often. If you're wanting to learn how to make data visualizations with d3.js, let's start by making simple art.","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\/generate-custom-art-with-react\/","og_locale":"en_US","og_type":"article","og_title":"Generate Custom Art with React","og_description":"There is beauty in data even though we don't get to see it often. If you're wanting to learn how to make data visualizations with d3.js, let's start by making simple art.","og_url":"https:\/\/cloudinary.com\/blog\/guest_post\/generate-custom-art-with-react\/","og_site_name":"Cloudinary Blog","article_published_time":"2022-05-18T14:01:37+00:00","twitter_card":"summary_large_image","twitter_image":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924886\/Web_Assets\/blog\/ec6325f1ba87dba61fdfd8bf3ddc3e16fe082eda-6000x4000-1_28299b91e9\/ec6325f1ba87dba61fdfd8bf3ddc3e16fe082eda-6000x4000-1_28299b91e9.jpg?_i=AA","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/generate-custom-art-with-react\/#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/generate-custom-art-with-react\/"},"author":{"name":"","@id":""},"headline":"Generate Custom Art with React","datePublished":"2022-05-18T14:01:37+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/generate-custom-art-with-react\/"},"wordCount":5,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/generate-custom-art-with-react\/#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924886\/Web_Assets\/blog\/ec6325f1ba87dba61fdfd8bf3ddc3e16fe082eda-6000x4000-1_28299b91e9\/ec6325f1ba87dba61fdfd8bf3ddc3e16fe082eda-6000x4000-1_28299b91e9.jpg?_i=AA","keywords":["Guest Post","Image","React","Under Review"],"inLanguage":"en-US","copyrightYear":"2022","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/generate-custom-art-with-react\/","url":"https:\/\/cloudinary.com\/blog\/guest_post\/generate-custom-art-with-react\/","name":"Generate Custom Art with React","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/generate-custom-art-with-react\/#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/generate-custom-art-with-react\/#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924886\/Web_Assets\/blog\/ec6325f1ba87dba61fdfd8bf3ddc3e16fe082eda-6000x4000-1_28299b91e9\/ec6325f1ba87dba61fdfd8bf3ddc3e16fe082eda-6000x4000-1_28299b91e9.jpg?_i=AA","datePublished":"2022-05-18T14:01:37+00:00","description":"There is beauty in data even though we don't get to see it often. If you're wanting to learn how to make data visualizations with d3.js, let's start by making simple art.","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/guest_post\/generate-custom-art-with-react\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/guest_post\/generate-custom-art-with-react\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/generate-custom-art-with-react\/#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924886\/Web_Assets\/blog\/ec6325f1ba87dba61fdfd8bf3ddc3e16fe082eda-6000x4000-1_28299b91e9\/ec6325f1ba87dba61fdfd8bf3ddc3e16fe082eda-6000x4000-1_28299b91e9.jpg?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1681924886\/Web_Assets\/blog\/ec6325f1ba87dba61fdfd8bf3ddc3e16fe082eda-6000x4000-1_28299b91e9\/ec6325f1ba87dba61fdfd8bf3ddc3e16fe082eda-6000x4000-1_28299b91e9.jpg?_i=AA","width":6000,"height":4000},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/guest_post\/generate-custom-art-with-react\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Generate Custom Art with React"}]},{"@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\/v1681924886\/Web_Assets\/blog\/ec6325f1ba87dba61fdfd8bf3ddc3e16fe082eda-6000x4000-1_28299b91e9\/ec6325f1ba87dba61fdfd8bf3ddc3e16fe082eda-6000x4000-1_28299b91e9.jpg?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/28298","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=28298"}],"version-history":[{"count":0,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/28298\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/28299"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=28298"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=28298"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=28298"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}