{"id":37344,"date":"2025-04-02T07:00:00","date_gmt":"2025-04-02T14:00:00","guid":{"rendered":"https:\/\/cloudinary.com\/blog\/?p=37344"},"modified":"2026-03-08T09:11:51","modified_gmt":"2026-03-08T16:11:51","slug":"smart-invites-made-easy-automate-react","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/smart-invites-made-easy-automate-react","title":{"rendered":"Smart Invites Made Easy: Automate With Cloudinary and React"},"content":{"rendered":"\n<p>Adding names to invitations can be tedious, especially when dealing with large guest lists. But what if you could automate the process with just a simple file upload? In this blog post, we\u2019ll build an invite maker using React and Cloudinary, allowing users to upload a CSV file and automatically populate invitations with invitee names. No manual entry required.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Key Features We\u2019ll Cover<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>CSV upload and name auto-population.<\/strong> Upload a CSV file with names and let the app generate personalized invites automatically.<\/li>\n\n\n\n<li><strong>Text overlay with Cloudinary.<\/strong> Dynamically place guest names on beautifully designed invitation templates.<\/li>\n\n\n\n<li><strong>Cloud storage and optimization.<\/strong> Store your files securely on Cloudinary<strong> <\/strong>using the <a href=\"https:\/\/cloudinary.com\/documentation\/upload_widget\">Cloudinary<strong> <\/strong>Upload widget<\/a> with <a href=\"https:\/\/cloudinary.com\/documentation\/upload_presets\">upload presets<\/a>, ensuring fast, optimized delivery.<\/li>\n\n\n\n<li><strong>Downloadable invitations.<\/strong> Download all the invitations you created with just one click.<\/li>\n<\/ul>\n\n\n\n<p>By leveraging Cloudinary, we ensure high-quality image optimization, fast content delivery, and seamless management. Whether you&#8217;re building an event management tool or just looking for ways to streamline your workflow, this solution will help you automate and enhance the invite creation process.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Part 1: Set Up Cloudinary<\/h2>\n\n\n\n<p>To begin, log in to your <a href=\"https:\/\/cloudinary.com\/\">Cloudinary account or create a free account<\/a>. If prompted with the question, \u201cWhat\u2019s your main interest?\u201d, select <strong>Coding with APIs and SDKs<\/strong> or <strong>Skip<\/strong>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1743533936\/blog-Smart_Invites_Made_Easy-1.png\" alt=\"\"\/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Cloudinary Cloud<\/h3>\n\n\n\n<p>In your account, select <strong>Settings <\/strong>&gt;<strong> Product Environments<\/strong>. Here, you\u2019ll see the cloud that you\u2019ve created. Click the three-dot menu to edit your Cloudinary cloud name.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1743533935\/blog-Smart_Invites_Made_Easy-2.png\" alt=\"\"\/><\/figure>\n\n\n\n<p>Edit the product environment and name your cloud, then click <strong>Save Changes<\/strong>.<\/p>\n\n\n\n<p>It\u2019s important to keep the same cloud name across different tools to be consistent.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1743533935\/blog-Smart_Invites_Made_Easy-3.png\" alt=\"\"\/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Get Cloudinary API Keys<\/h3>\n\n\n\n<p>In your account, select <strong>Settings <\/strong>&gt;<strong> Product Environments Setting<\/strong> &gt;<strong> API Keys<\/strong>. Click <strong>Generate New API Key <\/strong>and save these credentials in a safe place. We\u2019ll use them later.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Cloudinary Upload Presets<\/h4>\n\n\n\n<p>In your account, ensure you\u2019re inside the product environment you just created, then select <strong>Settings <\/strong>&gt;<strong> Upload<\/strong>. Click <strong>+ Add Upload Preset<\/strong>.&nbsp;<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img decoding=\"async\" src=\"https:\/\/lh7-rt.googleusercontent.com\/docsz\/AD_4nXfuIDqhluAY5Fipaqv5CnKnh27iRIeDXt8jAv6Q4guEEKM-xtKaiCASopCGkalB7xlqJKMP39ueuhZwZDH_4iKR_COHPoiQKfvJXU5CHzdMgXiiuaEfDnyqqOKEPgPebwevAw_o4Q?key=VAlbS7N7Ii5tnHQ64enE5tj6\" alt=\"\"\/><\/figure>\n\n\n\n<p>Enter the name of your preset. I named my preset \u201cupload-images\u201d. Now enter the folder name where they will live; mine is \u201cflyers\u201d. Click the <strong>Signing Mode <\/strong>dropdown and select <strong>Unsigned<\/strong><strong><em>. <\/em><\/strong>For asset naming, make sure the two switches are turned on. Your upload preset should look like this:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img decoding=\"async\" src=\"https:\/\/cloudinary-marketing-res.cloudinary.com\/image\/upload\/v1743533935\/blog-Smart_Invites_Made_Easy-5.png\" alt=\"\"\/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Part 2:<strong> <\/strong>Build the React app<\/h2>\n\n\n\n<p>In this tutorial, I\u2019m using <a href=\"https:\/\/vitejs.dev\/guide\/\">Vite<\/a> to build my React application. I recommend that you do the same. Follow the instructions on Vite\u2019s official website to create a React application.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Build the Invite Maker App<\/h3>\n\n\n\n<p>In your <a href=\"https:\/\/github.com\/cloudinary-devs\/Cloudinary-ImageMaker-React\/blob\/main\/src\/App.tsx\"><strong>App.tsx<\/strong><\/a> file, replace the existing code with the following:<\/p>\n\n\n<pre class=\"wp-block-code\" 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-keyword\">import<\/span> LeftSideBar <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/LeftSideBar\"<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> PlayGround <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/PlayGround\"<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> RightSideBar <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/RightSideBar\"<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> { TemplateProvider } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/TemplateContext\"<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> { TextOverlayProvider } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/TextOverlayContext\"<\/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\n\u00a0\u00a0<span class=\"hljs-keyword\">return<\/span> (\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"flex\"<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">TemplateProvider<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">TextOverlayProvider<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">LeftSideBar<\/span> \/&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">PlayGround<\/span> \/&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">RightSideBar<\/span> \/&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">TextOverlayProvider<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">TemplateProvider<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n\n\u00a0\u00a0);\n\n}\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> App;<\/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\n\n<h3 class=\"wp-block-heading\">TemplateContext<\/h3>\n\n\n\n<p>Time to work on the TemplateContext. This provider will help you share across components the template the user has selected to start adding the overlay text to it.<\/p>\n\n\n\n<p>Create a <a href=\"https:\/\/github.com\/cloudinary-devs\/Cloudinary-ImageMaker-React\/blob\/main\/src\/TemplateContext.tsx\"><strong>TemplateContext.tsx<\/strong><\/a> file and copy\/paste the following code:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">import<\/span> { createContext, useContext, useState, ReactNode } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>;\n\ninterface TemplateContextType {\n\n\u00a0\u00a0<span class=\"hljs-attr\">selectedTemplate<\/span>: string;\n\n\u00a0\u00a0setSelectedTemplate: <span class=\"hljs-function\">(<span class=\"hljs-params\">template: string<\/span>) =&gt;<\/span> <span class=\"hljs-keyword\">void<\/span>;\n\n}\n\n<span class=\"hljs-keyword\">const<\/span> TemplateContext = createContext&lt;TemplateContextType | <span class=\"hljs-literal\">undefined<\/span>&gt;(<span class=\"hljs-literal\">undefined<\/span>);\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> TemplateProvider = <span class=\"hljs-function\">(<span class=\"hljs-params\">{ children }: { children: ReactNode }<\/span>) =&gt;<\/span> {\n\n\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> &#91;selectedTemplate, setSelectedTemplate] = useState&lt;string&gt;(<span class=\"hljs-string\">\"\"<\/span>);\n\n\u00a0\u00a0<span class=\"hljs-keyword\">return<\/span> (\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">TemplateContext.Provider<\/span> <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">{{<\/span> <span class=\"hljs-attr\">selectedTemplate<\/span>, <span class=\"hljs-attr\">setSelectedTemplate<\/span> }}&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0{children}\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">TemplateContext.Provider<\/span>&gt;<\/span><\/span>\n\n\u00a0\u00a0);\n\n};\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> useTemplate = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n\n\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> context = useContext(TemplateContext);\n\n\u00a0\u00a0<span class=\"hljs-keyword\">if<\/span> (!context) {\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">throw<\/span> <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Error<\/span>(<span class=\"hljs-string\">\"useTemplate must be used within a TemplateProvider\"<\/span>);\n\n\u00a0\u00a0}\n\n\u00a0\u00a0<span class=\"hljs-keyword\">return<\/span> context;\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\n\n<h3 class=\"wp-block-heading\">TextOverlayContext<\/h3>\n\n\n\n<p>Now we\u2019ll need to create another context. This context will be in charge of sharing the state of everything related to the text overlay we\u2019re adding to the image.<br><br>Create a <a href=\"https:\/\/github.com\/cloudinary-devs\/Cloudinary-ImageMaker-React\/blob\/main\/src\/TextOverlayContext.tsx\">TextOverlayContext.tsx<\/a> file and copy\/paste the following code:<\/p>\n\n\n<pre class=\"wp-block-code\" 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> { createContext, useContext, useState } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>;\n\ninterface TextOverlayContextProps {\n\n\u00a0\u00a0<span class=\"hljs-attr\">text<\/span>: string;\n\n\u00a0\u00a0setText: <span class=\"hljs-function\">(<span class=\"hljs-params\">text: string<\/span>) =&gt;<\/span> <span class=\"hljs-keyword\">void<\/span>;\n\n\u00a0\u00a0color: string;\n\n\u00a0\u00a0setColor: <span class=\"hljs-function\">(<span class=\"hljs-params\">color: string<\/span>) =&gt;<\/span> <span class=\"hljs-keyword\">void<\/span>;\n\n\u00a0\u00a0font: string;\n\n\u00a0\u00a0setFont: <span class=\"hljs-function\">(<span class=\"hljs-params\">font: string<\/span>) =&gt;<\/span> <span class=\"hljs-keyword\">void<\/span>;\n\n\u00a0\u00a0fontSize: number;\n\n\u00a0\u00a0setFontSize: <span class=\"hljs-function\">(<span class=\"hljs-params\">size: number<\/span>) =&gt;<\/span> <span class=\"hljs-keyword\">void<\/span>;\n\n\u00a0\u00a0position: { <span class=\"hljs-attr\">x<\/span>: number; y: number };\n\n\u00a0\u00a0setPosition: <span class=\"hljs-function\">(<span class=\"hljs-params\">pos: { x: number; y: number }<\/span>) =&gt;<\/span> <span class=\"hljs-keyword\">void<\/span>;\n\n\u00a0\u00a0imgSize: { <span class=\"hljs-attr\">width<\/span>: number; height: number };\n\n\u00a0\u00a0setImgSize: <span class=\"hljs-function\">(<span class=\"hljs-params\">size: { width: number; height: number }<\/span>) =&gt;<\/span> <span class=\"hljs-keyword\">void<\/span>;\n\n\u00a0\u00a0imgOriginalSize: { <span class=\"hljs-attr\">width<\/span>: number; height: number };\n\n\u00a0\u00a0setImgOriginalSize: <span class=\"hljs-function\">(<span class=\"hljs-params\">size: { width: number; height: number }<\/span>) =&gt;<\/span> <span class=\"hljs-keyword\">void<\/span>;\n\n}\n\n<span class=\"hljs-keyword\">const<\/span> TextOverlayContext = createContext&lt;TextOverlayContextProps | <span class=\"hljs-literal\">undefined<\/span>&gt;(<span class=\"hljs-literal\">undefined<\/span>);\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> TextOverlayProvider = <span class=\"hljs-function\">(<span class=\"hljs-params\">{ children }: { children: React.ReactNode }<\/span>) =&gt;<\/span> {\n\n\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> &#91;text, setText] = useState(<span class=\"hljs-string\">\"\"<\/span>);\n\n\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> &#91;color, setColor] = useState(<span class=\"hljs-string\">\"#000000\"<\/span>);\n\n\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> &#91;font, setFont] = useState(<span class=\"hljs-string\">\"Arial\"<\/span>);\n\n\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> &#91;fontSize, setFontSize] = useState(<span class=\"hljs-number\">24<\/span>);\n\n\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> &#91;position, setPosition] = useState({ <span class=\"hljs-attr\">x<\/span>: <span class=\"hljs-number\">50<\/span>, <span class=\"hljs-attr\">y<\/span>: <span class=\"hljs-number\">50<\/span> });\n\n\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> &#91;imgSize, setImgSize] = useState({ <span class=\"hljs-attr\">width<\/span>: <span class=\"hljs-number\">500<\/span>, <span class=\"hljs-attr\">height<\/span>: <span class=\"hljs-number\">500<\/span> });\n\n\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> &#91;imgOriginalSize, setImgOriginalSize] = useState({ <span class=\"hljs-attr\">width<\/span>: <span class=\"hljs-number\">500<\/span>, <span class=\"hljs-attr\">height<\/span>: <span class=\"hljs-number\">500<\/span> });\n\n\u00a0\u00a0<span class=\"hljs-keyword\">return<\/span> (\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">TextOverlayContext.Provider<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">{{<\/span> <span class=\"hljs-attr\">text<\/span>, <span class=\"hljs-attr\">setText<\/span>, <span class=\"hljs-attr\">color<\/span>, <span class=\"hljs-attr\">setColor<\/span>, <span class=\"hljs-attr\">font<\/span>, <span class=\"hljs-attr\">setFont<\/span>, <span class=\"hljs-attr\">fontSize<\/span>, <span class=\"hljs-attr\">setFontSize<\/span>, <span class=\"hljs-attr\">position<\/span>, <span class=\"hljs-attr\">setPosition<\/span>, <span class=\"hljs-attr\">imgSize<\/span>, <span class=\"hljs-attr\">setImgSize<\/span>, <span class=\"hljs-attr\">imgOriginalSize<\/span>, <span class=\"hljs-attr\">setImgOriginalSize<\/span> }}\n\n\u00a0\u00a0\u00a0\u00a0&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0{children}\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">TextOverlayContext.Provider<\/span>&gt;<\/span><\/span>\n\n\u00a0\u00a0);\n\n};\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">const<\/span> useTextOverlay = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n\n\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> context = useContext(TextOverlayContext);\n\n\u00a0\u00a0<span class=\"hljs-keyword\">if<\/span> (!context) {\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">throw<\/span> <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Error<\/span>(<span class=\"hljs-string\">\"useTextOverlay must be used within a TextOverlayProvider\"<\/span>);\n\n\u00a0\u00a0}\n\n\u00a0\u00a0<span class=\"hljs-keyword\">return<\/span> context;\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\n\n<h3 class=\"wp-block-heading\">LeftSideBar<\/h3>\n\n\n\n<p>This file represents the left sidebar, where we display the settings for adding text to the flyer. In this sidebar, we also have the button to upload the list of invites, and a table will render with all the names, allowing us to generate the flyers with the names of the invitees.<\/p>\n\n\n\n<p>Create a <a href=\"https:\/\/github.com\/cloudinary-devs\/Cloudinary-ImageMaker-React\/blob\/main\/src\/LeftSideBar.tsx\">LeftSideBar.tsx<\/a> file and copy\/paste the following code:<\/p>\n\n\n<pre class=\"wp-block-code\" 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> { Cloudinary } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@cloudinary\/url-gen\/index\"<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> { useTemplate } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/TemplateContext\"<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> { fill } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@cloudinary\/url-gen\/actions\/resize\"<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> CloudinaryUploadWidget <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/CloudinaryUploadWidget\"<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> { useEffect, useState } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> templates = &#91;<span class=\"hljs-string\">\"1.png\"<\/span>, <span class=\"hljs-string\">\"2.png\"<\/span>, <span class=\"hljs-string\">\"3.png\"<\/span>, <span class=\"hljs-string\">\"4.png\"<\/span>, <span class=\"hljs-string\">\"5.png\"<\/span>, <span class=\"hljs-string\">\"6.png\"<\/span>];\n\n<span class=\"hljs-keyword\">const<\/span> myFlyers = &#91;<span class=\"hljs-string\">\"f7.png\"<\/span>, <span class=\"hljs-string\">\"f8.png\"<\/span>, <span class=\"hljs-string\">\"f9.png\"<\/span>, <span class=\"hljs-string\">\"f10.png\"<\/span>, <span class=\"hljs-string\">\"f11.png\"<\/span>, <span class=\"hljs-string\">\"f12.png\"<\/span>];\n\n<span class=\"hljs-keyword\">const<\/span> LeftSideBar = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n\n\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> { selectedTemplate, setSelectedTemplate } = useTemplate();\n\n\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> &#91;flyers, setFlyers] = useState(myFlyers);\n\n\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> cld = <span class=\"hljs-keyword\">new<\/span> Cloudinary({\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">cloud<\/span>: {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">cloudName<\/span>: <span class=\"hljs-string\">\"invite-maker\"<\/span>,\n\n\u00a0\u00a0\u00a0\u00a0},\n\n\u00a0\u00a0});\n\n\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> getFlyersFromCloudinary = <span class=\"hljs-function\">(<span class=\"hljs-params\">images: string&#91;], folderName: string<\/span>) =&gt;<\/span> {\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">return<\/span> images.map(<span class=\"hljs-function\">(<span class=\"hljs-params\">image, index: number<\/span>) =&gt;<\/span> {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> tempImg = cld.image(<span class=\"hljs-string\">`<span class=\"hljs-subst\">${folderName}<\/span>\/<span class=\"hljs-subst\">${image}<\/span>`<\/span>);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">return<\/span> (\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">key<\/span>=<span class=\"hljs-string\">{index}<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">{<\/span>`<span class=\"hljs-attr\">flex<\/span> <span class=\"hljs-attr\">justify-center<\/span> <span class=\"hljs-attr\">items-center<\/span> <span class=\"hljs-attr\">relative<\/span> <span class=\"hljs-attr\">cursor-pointer<\/span> <span class=\"hljs-attr\">rounded-md<\/span> <span class=\"hljs-attr\">overflow-hidden<\/span> <span class=\"hljs-attr\">border-2<\/span> <span class=\"hljs-attr\">transition-all<\/span> <span class=\"hljs-attr\">duration-300<\/span> <span class=\"hljs-attr\">hover:shadow-lg<\/span> ${\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">selectedTemplate<\/span> === `\/` + <span class=\"hljs-attr\">image<\/span> ? \"<span class=\"hljs-attr\">border-blue-500<\/span>\" <span class=\"hljs-attr\">:<\/span> \"<span class=\"hljs-attr\">border-transparent<\/span>\"\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}`}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{()<\/span> =&gt;<\/span> setSelectedTemplate(tempImg?.publicID)}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&gt;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">{tempImg.resize(fill().height(320)).toURL()}<\/span> <span class=\"hljs-attr\">alt<\/span>=<span class=\"hljs-string\">{<\/span>`<span class=\"hljs-attr\">Template<\/span> ${<span class=\"hljs-attr\">index<\/span> + <span class=\"hljs-attr\">1<\/span>}`} <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"object-cover hover:opacity-80\"<\/span> \/&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0)})\n\n\u00a0\u00a0}\n\n\u00a0\u00a0<span class=\"hljs-keyword\">return<\/span> (\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"w-200 bg-gray-100 p-4 shadow-lg rounded-lg h-screen flex flex-col\"<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"text-lg font-semibold mb-4 sticky top-0 bg-gray-100 p-2 z-10\"<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0InviteMaker\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"flex-1 overflow-y-auto\"<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h2<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"text-lg font-semibold mb-4 sticky top-0 bg-gray-100 p-2 z-10\"<\/span>&gt;<\/span>Templates<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h2<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"grid grid-cols-2 gap-3\"<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0{getFlyersFromCloudinary(templates, \"templates\")}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"border-t border-gray-300 my-2\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"flex-1 overflow-y-auto\"<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h2<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"text-lg font-semibold mb-4 sticky top-0 bg-gray-100 p-2 z-10\"<\/span>&gt;<\/span>My Flyers<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h2<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"grid grid-cols-2 gap-3\"<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0{getFlyersFromCloudinary(flyers, \"flyers\")}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">CloudinaryUploadWidget<\/span> <span class=\"hljs-attr\">getFlyers<\/span>=<span class=\"hljs-string\">{getFlyersFromCloudinary}<\/span> <span class=\"hljs-attr\">folderName<\/span>=<span class=\"hljs-string\">\"flyers\"<\/span> <span class=\"hljs-attr\">files<\/span>=<span class=\"hljs-string\">{flyers}<\/span> <span class=\"hljs-attr\">setFlyers<\/span>=<span class=\"hljs-string\">{setFlyers}\/<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n\n\u00a0\u00a0);\n\n};\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> LeftSideBar;<\/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\n\n<h3 class=\"wp-block-heading\">PlayGround<\/h3>\n\n\n\n<p>This file plays a very important role because we\u2019re using the state from different components and rendering the flyer selected with the text overlay we want to add. Additionally, this file renders the flyer scaled to the size of the screen.<\/p>\n\n\n\n<p>Create a <a href=\"https:\/\/github.com\/cloudinary-devs\/Cloudinary-ImageMaker-React\/blob\/main\/src\/PlayGround.tsx\">PlayGround.tsx<\/a> file and copy\/paste the following code:<\/p>\n\n\n<pre class=\"wp-block-code\" 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\">import<\/span> { useTemplate } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/TemplateContext\"<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> { useTextOverlay } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/TextOverlayContext\"<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> { useRef, useState, useEffect } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> PlayGround = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n\n\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> { selectedTemplate } = useTemplate();\n\n\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> {\n\n\u00a0\u00a0\u00a0\u00a0text,\n\n\u00a0\u00a0\u00a0\u00a0color,\n\n\u00a0\u00a0\u00a0\u00a0font,\n\n\u00a0\u00a0\u00a0\u00a0fontSize,\n\n\u00a0\u00a0\u00a0\u00a0position,\n\n\u00a0\u00a0\u00a0\u00a0imgSize,\n\n\u00a0\u00a0\u00a0\u00a0setImgSize,\n\n\u00a0\u00a0\u00a0\u00a0setImgOriginalSize,\n\n\u00a0\u00a0} = useTextOverlay();\n\n\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> imgRef = useRef&lt;HTMLImageElement&gt;(<span class=\"hljs-literal\">null<\/span>);\n\n\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> textRef = useRef&lt;HTMLDivElement&gt;(<span class=\"hljs-literal\">null<\/span>);\n\n\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> &#91;textSize, setTextSize] = useState({ <span class=\"hljs-attr\">width<\/span>: <span class=\"hljs-number\">0<\/span>, <span class=\"hljs-attr\">height<\/span>: <span class=\"hljs-number\">0<\/span> });\n\n\u00a0\u00a0useEffect(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> updateSize = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">if<\/span> (imgRef.current &amp;&amp; selectedTemplate &amp;&amp; imgRef.current.complete) {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> containerWidth = <span class=\"hljs-built_in\">window<\/span>.innerWidth * <span class=\"hljs-number\">0.8<\/span>;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> containerHeight = <span class=\"hljs-built_in\">window<\/span>.innerHeight * <span class=\"hljs-number\">0.8<\/span>;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> aspectRatio =\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0imgRef.current.naturalWidth \/ imgRef.current.naturalHeight || <span class=\"hljs-number\">1<\/span>;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">let<\/span> newWidth = <span class=\"hljs-built_in\">Math<\/span>.min(\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0containerWidth,\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0imgRef.current.naturalWidth || containerWidth\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">let<\/span> newHeight = newWidth \/ aspectRatio;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">if<\/span> (newHeight &gt; containerHeight) {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0newHeight = containerHeight;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0newWidth = newHeight * aspectRatio;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0setImgSize({\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">width<\/span>: newWidth,\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">height<\/span>: newHeight,\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0});\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0setImgOriginalSize({\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">width<\/span>: imgRef.current.naturalWidth || containerWidth,\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">height<\/span>: imgRef.current.naturalHeight || containerHeight,\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0});\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0};\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">if<\/span> (selectedTemplate) {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> checkImageLoad = setInterval(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">if<\/span> (imgRef.current &amp;&amp; imgRef.current.complete) {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0updateSize();\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0clearInterval(checkImageLoad);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}, <span class=\"hljs-number\">100<\/span>);\n\n\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-built_in\">window<\/span>.addEventListener(<span class=\"hljs-string\">\"resize\"<\/span>, updateSize);\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-built_in\">window<\/span>.removeEventListener(<span class=\"hljs-string\">\"resize\"<\/span>, updateSize);\n\n\u00a0\u00a0\u00a0\u00a0};\n\n\u00a0\u00a0}, &#91;selectedTemplate]);\n\n\u00a0\u00a0useEffect(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">if<\/span> (textRef.current) {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0setTextSize({\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">width<\/span>: textRef.current.clientWidth,\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">height<\/span>: textRef.current.clientHeight,\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0});\n\n\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0}, &#91;text, font, fontSize, position, imgSize]);\n\n\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> safeX = <span class=\"hljs-built_in\">Math<\/span>.max(\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-number\">0<\/span>,\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-built_in\">Math<\/span>.min(position.x, imgSize.width - textSize.width)\n\n\u00a0\u00a0);\n\n\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> safeY = <span class=\"hljs-built_in\">Math<\/span>.max(\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-number\">0<\/span>,\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-built_in\">Math<\/span>.min(position.y, imgSize.height - textSize.height)\n\n\u00a0\u00a0);\n\n\u00a0\u00a0<span class=\"hljs-keyword\">return<\/span> (\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"flex flex-col items-center w-full h-screen bg-gray-200\"<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0{selectedTemplate ? (\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"relative\"<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">{{<\/span> <span class=\"hljs-attr\">width:<\/span> <span class=\"hljs-attr\">imgSize.width<\/span>, <span class=\"hljs-attr\">height:<\/span> <span class=\"hljs-attr\">imgSize.height<\/span> }}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">img<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">ref<\/span>=<span class=\"hljs-string\">{imgRef}<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">{<\/span>`<span class=\"hljs-attr\">https:<\/span>\/\/<span class=\"hljs-attr\">res.cloudinary.com<\/span>\/<span class=\"hljs-attr\">invite-maker<\/span>\/<span class=\"hljs-attr\">image<\/span>\/<span class=\"hljs-attr\">upload<\/span>\/<span class=\"hljs-attr\">v1<\/span>\/${<span class=\"hljs-attr\">selectedTemplate<\/span>}`}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">alt<\/span>=<span class=\"hljs-string\">\"Selected Template\"<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"w-full h-auto\"<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0{text &amp;&amp; (\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">ref<\/span>=<span class=\"hljs-string\">{textRef}<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"absolute\"<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">{{<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">top:<\/span> `${<span class=\"hljs-attr\">safeY<\/span>}<span class=\"hljs-attr\">px<\/span>`,\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">left:<\/span> `${<span class=\"hljs-attr\">safeX<\/span>}<span class=\"hljs-attr\">px<\/span>`,\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">color:<\/span> <span class=\"hljs-attr\">color<\/span>,\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">fontFamily:<\/span> <span class=\"hljs-attr\">font<\/span>,\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">fontSize:<\/span> `${<span class=\"hljs-attr\">fontSize<\/span>}<span class=\"hljs-attr\">px<\/span>`,\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">fontWeight:<\/span> \"<span class=\"hljs-attr\">bold<\/span>\",\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">whiteSpace:<\/span> \"<span class=\"hljs-attr\">nowrap<\/span>\",\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">overflow:<\/span> \"<span class=\"hljs-attr\">hidden<\/span>\",\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">maxWidth:<\/span> `${<span class=\"hljs-attr\">imgSize.width<\/span>}<span class=\"hljs-attr\">px<\/span>`,\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0{text}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0)}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0) : (\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">p<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"text-gray-600\"<\/span>&gt;<\/span>Select a template to preview it here.<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">p<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0)}\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n\n\u00a0\u00a0);\n\n};\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> PlayGround;<\/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\n\n<p>Let\u2019s explain our frontend code:<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">updateSize<\/h4>\n\n\n\n<p>The component ensures that the selected image doesn\u2019t take up the entire screen but instead scales down to fit within 80% of the viewport width and height. The <code>updateSize<\/code> function calculates the new dimensions of the image while preserving its original aspect ratio.<\/p>\n\n\n<pre class=\"wp-block-code\" 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> updateSize = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">if<\/span> (imgRef.current &amp;&amp; selectedTemplate &amp;&amp; imgRef.current.complete) {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> containerWidth = <span class=\"hljs-built_in\">window<\/span>.innerWidth * <span class=\"hljs-number\">0.8<\/span>;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> containerHeight = <span class=\"hljs-built_in\">window<\/span>.innerHeight * <span class=\"hljs-number\">0.8<\/span>;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> aspectRatio =\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0imgRef.current.naturalWidth \/ imgRef.current.naturalHeight || <span class=\"hljs-number\">1<\/span>;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">let<\/span> newWidth = <span class=\"hljs-built_in\">Math<\/span>.min(\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0containerWidth,\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0imgRef.current.naturalWidth || containerWidth\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">let<\/span> newHeight = newWidth \/ aspectRatio;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">if<\/span> (newHeight &gt; containerHeight) {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0newHeight = containerHeight;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0newWidth = newHeight * aspectRatio;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0setImgSize({\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">width<\/span>: newWidth,\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">height<\/span>: newHeight,\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0});\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0setImgOriginalSize({\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">width<\/span>: imgRef.current.naturalWidth || containerWidth,\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">height<\/span>: imgRef.current.naturalHeight || containerHeight,\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0});\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0};<\/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\n\n<h4 class=\"wp-block-heading\">safeZone<\/h4>\n\n\n\n<p>The text is positioned over the image dynamically based on position.x and position.y. Since text can vary in size, it\u2019s necessary to ensure it doesn\u2019t overflow beyond the image boundaries. This is done using the safeX and safeY calculations.<\/p>\n\n\n<pre class=\"wp-block-code\" 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> safeX = <span class=\"hljs-built_in\">Math<\/span>.max(<span class=\"hljs-number\">0<\/span>, <span class=\"hljs-built_in\">Math<\/span>.min(position.x, imgSize.width - textSize.width));\n\n<span class=\"hljs-keyword\">const<\/span> safeY = <span class=\"hljs-built_in\">Math<\/span>.max(<span class=\"hljs-number\">0<\/span>, <span class=\"hljs-built_in\">Math<\/span>.min(position.y, imgSize.height - textSize.height));<\/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\n\n<p>These calculations guarantee that safeX and safeY values keep the text within the image area, preventing it from being placed outside the visible image.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">setSize<\/h4>\n\n\n\n<p>Additionally, the <code>useEffect()<\/code> hook listens for changes to text, font, <code>fontSize<\/code>, position, and <code>imgSize<\/code>, updating <code>textSize<\/code> accordingly.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">useEffect(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">if<\/span> (textRef.current) {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0setTextSize({\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">width<\/span>: textRef.current.clientWidth,\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">height<\/span>: textRef.current.clientHeight,\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0});\n\n\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0}, &#91;text, font, fontSize, position, imgSize]);<\/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\n\n<p>This ensures that text dimensions are recalculated whenever the user makes changes, keeping the layout responsive and accurate.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">RightSideBar<\/h3>\n\n\n\n<p>This component will handle the majority of the functionality of the application and the text overlay (size, color, positioning, font family, etc), allowing the user to upload a list of names to automatically generate the invites.<\/p>\n\n\n\n<p>Create a <a href=\"https:\/\/github.com\/cloudinary-devs\/Cloudinary-ImageMaker-React\/blob\/main\/src\/RightSideBar.tsx\">RightSideBar.tsx<\/a> file and copy\/paste the following code:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">import<\/span> { Cloudinary } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@cloudinary\/url-gen\/index\"<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> { useTextOverlay } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/TextOverlayContext\"<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> Papa <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"papaparse\"<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> { useState, useRef, useEffect } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"react\"<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> { source } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@cloudinary\/url-gen\/actions\/overlay\"<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> { text <span class=\"hljs-keyword\">as<\/span> cloudinaryText } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@cloudinary\/url-gen\/qualifiers\/source\"<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> { TextStyle } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@cloudinary\/url-gen\/qualifiers\/textStyle\"<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> { Position } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@cloudinary\/url-gen\/qualifiers\"<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> { compass } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@cloudinary\/url-gen\/qualifiers\/gravity\"<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> { fill } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"@cloudinary\/url-gen\/actions\/resize\"<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> JSZip <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"jszip\"<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> { saveAs } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\"file-saver\"<\/span>;\n\n<span class=\"hljs-keyword\">import<\/span> { useTemplate } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/TemplateContext\"<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> RightSideBar = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n\n\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> {\n\n\u00a0\u00a0\u00a0\u00a0text,\n\n\u00a0\u00a0\u00a0\u00a0setText,\n\n\u00a0\u00a0\u00a0\u00a0color,\n\n\u00a0\u00a0\u00a0\u00a0setColor,\n\n\u00a0\u00a0\u00a0\u00a0font,\n\n\u00a0\u00a0\u00a0\u00a0setFont,\n\n\u00a0\u00a0\u00a0\u00a0fontSize,\n\n\u00a0\u00a0\u00a0\u00a0setFontSize,\n\n\u00a0\u00a0\u00a0\u00a0position,\n\n\u00a0\u00a0\u00a0\u00a0setPosition,\n\n\u00a0\u00a0\u00a0\u00a0imgSize,\n\n\u00a0\u00a0\u00a0\u00a0imgOriginalSize,\n\n\u00a0\u00a0} = useTextOverlay();\n\n\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> &#91;csvData, setCsvData] = useState&lt;string&#91;]&gt;(&#91;]);\n\n\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> &#91;, setGeneratedLinks] = useState&lt;string&#91;]&gt;(&#91;]);\n\n\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> fileInputRef = useRef&lt;HTMLInputElement&gt;(<span class=\"hljs-literal\">null<\/span>);\n\n\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> { selectedTemplate } = useTemplate();\n\n\u00a0\u00a0useEffect(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">if<\/span> (csvData.length &gt; <span class=\"hljs-number\">0<\/span>) {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0setText(<span class=\"hljs-string\">`<span class=\"hljs-subst\">${csvData&#91;<span class=\"hljs-number\">0<\/span>]}<\/span> <span class=\"hljs-subst\">${text}<\/span>`<\/span>);\n\n\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0}, &#91;csvData]);\n\n\u00a0\u00a0<span class=\"hljs-comment\">\/\/ Create and configure your Cloudinary instance.<\/span>\n\n\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> cld = <span class=\"hljs-keyword\">new<\/span> Cloudinary({\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">cloud<\/span>: {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">cloudName<\/span>: <span class=\"hljs-string\">\"invite-maker\"<\/span>,\n\n\u00a0\u00a0\u00a0\u00a0},\n\n\u00a0\u00a0});\n\n\u00a0\u00a0useEffect(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">if<\/span> (selectedTemplate &amp;&amp; selectedTemplate.length &gt; <span class=\"hljs-number\">0<\/span>) {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> myImage = cld.image(<span class=\"hljs-string\">`<span class=\"hljs-subst\">${selectedTemplate}<\/span>`<\/span>);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-comment\">\/\/ Compute precise scaling ratios<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> scaleX = imgOriginalSize.width \/ imgSize.width;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> scaleY = imgOriginalSize.height \/ imgSize.height;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-comment\">\/\/ Scale font size proportionally<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> adjustedFontSize = <span class=\"hljs-built_in\">Math<\/span>.round(fontSize * scaleX);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-comment\">\/\/ Estimate text width and height using an approximate character size multiplier<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> estimatedTextWidth = adjustedFontSize * text.length * <span class=\"hljs-number\">0.6<\/span>; <span class=\"hljs-comment\">\/\/ Approximate width<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> estimatedTextHeight = adjustedFontSize; <span class=\"hljs-comment\">\/\/ Text height is usually 1 line height<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-comment\">\/\/ Compute safe positions for text placement<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> mappedX = <span class=\"hljs-built_in\">Math<\/span>.max(\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-number\">0<\/span>,\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-built_in\">Math<\/span>.min(position.x * scaleX, imgOriginalSize.width - estimatedTextWidth)\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> mappedY = <span class=\"hljs-built_in\">Math<\/span>.max(\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-number\">0<\/span>,\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-built_in\">Math<\/span>.min(\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0position.y * scaleY,\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0imgOriginalSize.height - estimatedTextHeight\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0) <span class=\"hljs-comment\">\/\/ Prevent Y overflow<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-comment\">\/\/ Adjust offsets only if there's enough space<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> adjustedX =\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0mappedX +\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0(mappedX + estimatedTextWidth &lt; imgOriginalSize.width\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0? estimatedTextWidth \/ <span class=\"hljs-number\">8<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0: <span class=\"hljs-number\">0<\/span>);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> adjustedY =\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0mappedY +\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0(mappedY + estimatedTextHeight &lt;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0imgOriginalSize.height - estimatedTextHeight\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0? adjustedFontSize \/ <span class=\"hljs-number\">2<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0: <span class=\"hljs-number\">0<\/span>); <span class=\"hljs-comment\">\/\/ Prevents text from going below the image<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-comment\">\/\/ Apply position mapping using absolute values<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> safeFont = font &amp;&amp; font.length &gt; <span class=\"hljs-number\">0<\/span> ? font : <span class=\"hljs-string\">\"Arial\"<\/span>; <span class=\"hljs-comment\">\/\/ Default to Arial if font is missing<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> safeFontSize = fontSize &gt; <span class=\"hljs-number\">0<\/span> ? fontSize : <span class=\"hljs-number\">20<\/span>; <span class=\"hljs-comment\">\/\/ Default to 20px if fontSize is invalid<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> adjustedFontSizes = <span class=\"hljs-built_in\">Math<\/span>.round(safeFontSize * scaleX);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-comment\">\/\/ Check if adjustedFontSizes is valid before using it<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">if<\/span> (adjustedFontSizes &gt; <span class=\"hljs-number\">0<\/span> &amp;&amp; safeFont) {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0myImage.overlay(\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0source(\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0cloudinaryText(text, <span class=\"hljs-keyword\">new<\/span> TextStyle(safeFont, adjustedFontSizes)) <span class=\"hljs-comment\">\/\/ Scale font size correctly<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.textColor(color)\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0).position(\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">new<\/span> Position()\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.gravity(compass(<span class=\"hljs-string\">\"north_west\"<\/span>))\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.offsetX(<span class=\"hljs-built_in\">Math<\/span>.round(adjustedX))\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.offsetY(<span class=\"hljs-built_in\">Math<\/span>.round(adjustedY)) <span class=\"hljs-comment\">\/\/ Use absolute values<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0)\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-comment\">\/\/ Generate the Cloudinary URL<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> myUrl = myImage.toURL();\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-built_in\">console<\/span>.log(myUrl);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0}, &#91;text, color, font, fontSize, position, imgSize, imgOriginalSize, selectedTemplate]);\n\n\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> handleCSVUpload = <span class=\"hljs-function\">(<span class=\"hljs-params\">event: React.ChangeEvent&lt;HTMLInputElement&gt;<\/span>) =&gt;<\/span> {\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> file = event.target.files?.&#91;<span class=\"hljs-number\">0<\/span>];\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">if<\/span> (!file) <span class=\"hljs-keyword\">return<\/span>;\n\n\u00a0\u00a0\u00a0\u00a0Papa.parse&lt;string&#91;]&gt;(file, {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">complete<\/span>: <span class=\"hljs-function\">(<span class=\"hljs-params\">result<\/span>) =&gt;<\/span> {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> filteredData = result.data\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.filter(\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-function\">(<span class=\"hljs-params\">row, index<\/span>) =&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0index !== <span class=\"hljs-number\">0<\/span> || (row&#91;<span class=\"hljs-number\">0<\/span>] &amp;&amp; row&#91;<span class=\"hljs-number\">0<\/span>].toLowerCase() !== <span class=\"hljs-string\">\"name\"<\/span>)\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0)\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.map(<span class=\"hljs-function\">(<span class=\"hljs-params\">row<\/span>) =&gt;<\/span> row&#91;<span class=\"hljs-number\">0<\/span>]);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0setCsvData(filteredData);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0},\n\n\u00a0\u00a0\u00a0\u00a0});\n\n\u00a0\u00a0};\n\n\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> generateFlyers = <span class=\"hljs-keyword\">async<\/span> () =&gt; {\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">if<\/span> (!selectedTemplate || selectedTemplate.length === <span class=\"hljs-number\">0<\/span>) <span class=\"hljs-keyword\">return<\/span>;\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> previewName = csvData&#91;<span class=\"hljs-number\">0<\/span>] || <span class=\"hljs-string\">\"\"<\/span>;\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> links: string&#91;] = csvData.map(<span class=\"hljs-function\">(<span class=\"hljs-params\">name<\/span>) =&gt;<\/span> {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> myImage = cld.image(<span class=\"hljs-string\">`<span class=\"hljs-subst\">${selectedTemplate}<\/span>`<\/span>);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0myImage.resize(\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0fill().width(imgOriginalSize.width).height(imgOriginalSize.height)\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> scaleX = imgOriginalSize.width \/ imgSize.width;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> scaleY = imgOriginalSize.height \/ imgSize.height;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> adjustedFontSize = <span class=\"hljs-built_in\">Math<\/span>.round(fontSize * scaleX);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> estimatedTextWidth =\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0adjustedFontSize * (name.length + text.length) * <span class=\"hljs-number\">0.65<\/span>;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> estimatedTextHeight = adjustedFontSize;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">let<\/span> mappedX = <span class=\"hljs-built_in\">Math<\/span>.max(\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-number\">0<\/span>,\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-built_in\">Math<\/span>.min(\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0position.x * scaleX,\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0imgOriginalSize.width - estimatedTextWidth * <span class=\"hljs-number\">0.9<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0)\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> mappedY = <span class=\"hljs-built_in\">Math<\/span>.max(\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-number\">0<\/span>,\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-built_in\">Math<\/span>.min(\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0position.y * scaleY,\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0imgOriginalSize.height - estimatedTextHeight\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0)\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-comment\">\/\/ Prevent overflow on X-axis with finer adjustment<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">if<\/span> (mappedX + estimatedTextWidth &gt; imgOriginalSize.width) {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0mappedX = imgOriginalSize.width - estimatedTextWidth * <span class=\"hljs-number\">0.6<\/span>;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">if<\/span> (mappedX &lt; <span class=\"hljs-number\">0<\/span>) {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0mappedX = <span class=\"hljs-number\">0<\/span>;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> safeFont = font || <span class=\"hljs-string\">\"Arial\"<\/span>; <span class=\"hljs-comment\">\/\/ Default to Arial if font is missing<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> safeFontSize = fontSize &gt; <span class=\"hljs-number\">0<\/span> ? fontSize : <span class=\"hljs-number\">20<\/span>; <span class=\"hljs-comment\">\/\/ Default to 20px if fontSize is invalid<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> adjustedFontSizes = <span class=\"hljs-built_in\">Math<\/span>.round(safeFontSize * scaleX);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">if<\/span> (adjustedFontSizes &gt; <span class=\"hljs-number\">0<\/span> &amp;&amp; safeFont) {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0myImage.overlay(\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0source(\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0cloudinaryText(\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-string\">`<span class=\"hljs-subst\">${name}<\/span> <span class=\"hljs-subst\">${text.replace(previewName, <span class=\"hljs-string\">\"\"<\/span>)}<\/span>`<\/span>,\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">new<\/span> TextStyle(safeFont, adjustedFontSizes)\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0).textColor(color)\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0).position(\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">new<\/span> Position()\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.gravity(compass(<span class=\"hljs-string\">\"north_west\"<\/span>))\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.offsetX(<span class=\"hljs-built_in\">Math<\/span>.round(mappedX))\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.offsetY(<span class=\"hljs-built_in\">Math<\/span>.round(mappedY))\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0)\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">return<\/span> myImage.toURL();\n\n\u00a0\u00a0\u00a0\u00a0});\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-built_in\">console<\/span>.log(links&#91;<span class=\"hljs-number\">1<\/span>]);\n\n\u00a0\u00a0\u00a0\u00a0setGeneratedLinks(links);\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">if<\/span> (links.length === <span class=\"hljs-number\">1<\/span>) {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> link = <span class=\"hljs-built_in\">document<\/span>.createElement(<span class=\"hljs-string\">\"a\"<\/span>);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0link.href = links&#91;<span class=\"hljs-number\">0<\/span>];\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0link.download = <span class=\"hljs-string\">\"flyer.png\"<\/span>;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-built_in\">document<\/span>.body.appendChild(link);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0link.click();\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-built_in\">document<\/span>.body.removeChild(link);\n\n\u00a0\u00a0\u00a0\u00a0} <span class=\"hljs-keyword\">else<\/span> {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> zip = <span class=\"hljs-keyword\">new<\/span> JSZip();\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> folder = zip.folder(<span class=\"hljs-string\">\"Flyers\"<\/span>);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">if<\/span> (folder) {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">await<\/span> <span class=\"hljs-built_in\">Promise<\/span>.all(\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0links.map(<span class=\"hljs-keyword\">async<\/span> (url, index) =&gt; {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> response = <span class=\"hljs-keyword\">await<\/span> fetch(url);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> blob = <span class=\"hljs-keyword\">await<\/span> response.blob();\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0folder.file(<span class=\"hljs-string\">`flyer_<span class=\"hljs-subst\">${index + <span class=\"hljs-number\">1<\/span>}<\/span>.png`<\/span>, blob);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0})\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> zipBlob = <span class=\"hljs-keyword\">await<\/span> zip.generateAsync({ <span class=\"hljs-attr\">type<\/span>: <span class=\"hljs-string\">\"blob\"<\/span> });\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0saveAs(zipBlob, <span class=\"hljs-string\">\"flyers.zip\"<\/span>);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0};\n\n\u00a0\u00a0<span class=\"hljs-keyword\">return<\/span> (\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"w-64 bg-gray-100 p-4 shadow-lg rounded-lg h-screen flex flex-col\"<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"flex-1\"<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h2<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"text-lg font-semibold mb-4\"<\/span>&gt;<\/span>Customize Text<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h2<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"mb-2\"<\/span>&gt;<\/span>Text<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"text\"<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">{text}<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">onChange<\/span>=<span class=\"hljs-string\">{(e)<\/span> =&gt;<\/span> setText(e.target.value)}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0className=\"border p-2 rounded mb-2\"\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/&gt;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"mb-2\"<\/span>&gt;<\/span>Color<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"color\"<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">{color}<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">onChange<\/span>=<span class=\"hljs-string\">{(e)<\/span> =&gt;<\/span> setColor(e.target.value)}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0className=\"border p-2 rounded mb-2 w-full\"\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/&gt;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"mb-2\"<\/span>&gt;<\/span>Font<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0{\/* As for font families, we offer several built-in options, including Arial, Verdana, Helvetica, Trebuchet MS, Times New Roman, Georgia, Courier New, Open Sans, Roboto, and Montserrat. *\/}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">select<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">{font}<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">onChange<\/span>=<span class=\"hljs-string\">{(e)<\/span> =&gt;<\/span> setFont(e.target.value)}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0className=\"border p-2 rounded mb-2 w-full\"\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&gt;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">option<\/span> <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">\"Arial\"<\/span>&gt;<\/span>Arial<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">option<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">option<\/span> <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">\"Times New Roman\"<\/span>&gt;<\/span>Times New Roman<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">option<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">option<\/span> <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">\"Courier New\"<\/span>&gt;<\/span>Courier New<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">option<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">option<\/span> <span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">\"Verdana\"<\/span>&gt;<\/span>Verdana<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">option<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">select<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"mb-2 w-full\"<\/span>&gt;<\/span>Font Size {fontSize}px<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"range\"<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">min<\/span>=<span class=\"hljs-string\">\"10\"<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">max<\/span>=<span class=\"hljs-string\">\"100\"<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">{fontSize}<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">onChange<\/span>=<span class=\"hljs-string\">{(e)<\/span> =&gt;<\/span> setFontSize(Number(e.target.value))}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0className=\"mb-2 w-full\"\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/&gt;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"mb-2\"<\/span>&gt;<\/span>X Position<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"range\"<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">min<\/span>=<span class=\"hljs-string\">\"0\"<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">max<\/span>=<span class=\"hljs-string\">{imgSize.width<\/span> <span class=\"hljs-attr\">-<\/span> <span class=\"hljs-attr\">fontSize<\/span>}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">{position.x}<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">onChange<\/span>=<span class=\"hljs-string\">{(e)<\/span> =&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0setPosition({ ...position, x: Number(e.target.value) })\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0className=\"mb-2 w-full\"\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/&gt;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">label<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"mb-2\"<\/span>&gt;<\/span>Y Position<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">label<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"range\"<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">min<\/span>=<span class=\"hljs-string\">\"0\"<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">max<\/span>=<span class=\"hljs-string\">{imgSize.height<\/span> <span class=\"hljs-attr\">-<\/span> <span class=\"hljs-attr\">fontSize<\/span>}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">value<\/span>=<span class=\"hljs-string\">{position.y}<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">onChange<\/span>=<span class=\"hljs-string\">{(e)<\/span> =&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0setPosition({ ...position, y: Number(e.target.value) })\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0className=\"mb-2 w-full\"\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/&gt;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"flex-1 flex flex-col overflow-hidden\"<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"file\"<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">accept<\/span>=<span class=\"hljs-string\">\".csv\"<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">onChange<\/span>=<span class=\"hljs-string\">{handleCSVUpload}<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">ref<\/span>=<span class=\"hljs-string\">{fileInputRef}<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"hidden\"<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\/&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{()<\/span> =&gt;<\/span> fileInputRef.current?.click()}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0className=\"mt-4 px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-500\"\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&gt;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Upload Name List\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0{csvData.length &gt; 0 &amp;&amp; (\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">onClick<\/span>=<span class=\"hljs-string\">{generateFlyers}<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"mt-4 px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-500\"<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0Generate Flyers\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0)}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0{csvData.length &gt; 0 &amp;&amp; (\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"w-full max-w-xs mt-4 p-2 bg-white shadow-lg rounded-lg flex-1 overflow-y-auto\"<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h3<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"text-lg font-semibold mb-2\"<\/span>&gt;<\/span>CSV Names<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h3<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">table<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"w-full border-collapse border border-gray-300\"<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">tbody<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0{csvData.map((name, index) =&gt; (\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">tr<\/span> <span class=\"hljs-attr\">key<\/span>=<span class=\"hljs-string\">{index}<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"border border-gray-300\"<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">td<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"p-2\"<\/span>&gt;<\/span>{name}<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">td<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">tr<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0))}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">tbody<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">table<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0)}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/span>\n\n\u00a0\u00a0);\n\n};\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> RightSideBar;<\/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\n\n<p>Let\u2019s explain our frontend code:<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">handleCSVUpload<\/h4>\n\n\n<pre class=\"wp-block-code\" 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> handleCSVUpload = <span class=\"hljs-function\">(<span class=\"hljs-params\">event: React.ChangeEvent&lt;HTMLInputElement&gt;<\/span>) =&gt;<\/span> {\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> file = event.target.files?.&#91;<span class=\"hljs-number\">0<\/span>];\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">if<\/span> (!file) <span class=\"hljs-keyword\">return<\/span>;\n\n\u00a0\u00a0\u00a0\u00a0Papa.parse&lt;string&#91;]&gt;(file, {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-attr\">complete<\/span>: <span class=\"hljs-function\">(<span class=\"hljs-params\">result<\/span>) =&gt;<\/span> {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> filteredData = result.data\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.filter(\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-function\">(<span class=\"hljs-params\">row, index<\/span>) =&gt;<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0index !== <span class=\"hljs-number\">0<\/span> || (row&#91;<span class=\"hljs-number\">0<\/span>] &amp;&amp; row&#91;<span class=\"hljs-number\">0<\/span>].toLowerCase() !== <span class=\"hljs-string\">\"name\"<\/span>)\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0)\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.map(<span class=\"hljs-function\">(<span class=\"hljs-params\">row<\/span>) =&gt;<\/span> row&#91;<span class=\"hljs-number\">0<\/span>]);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0setCsvData(filteredData);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0},\n\n\u00a0\u00a0\u00a0\u00a0});\n\n\u00a0\u00a0};<\/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\n\n<p>The <code>handleCSVUpload<\/code> function reads the uploaded CSV file, extracts names from the first column, and updates <code>csvData<\/code>. It ignores the first row if the first column contains &#8220;name&#8221; to filter out headers.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">generateFlyers<\/h4>\n\n\n\n<p>The <code>generateFlyers<\/code> function dynamically creates flyers using Cloudinary by overlaying text on a template image. It scales font size, calculates text position, and prevents text from overflowing.<\/p>\n\n\n\n<p>First, the function retrieves the first name in the list to use as a reference for centering text. Then, it iterates through each name in csvData, applies the overlay, and generates a URL for each flyer.<\/p>\n\n\n<pre class=\"wp-block-code\" 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\">const<\/span> generateFlyers = <span class=\"hljs-keyword\">async<\/span> () =&gt; {\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">if<\/span> (!selectedTemplate || selectedTemplate.length === <span class=\"hljs-number\">0<\/span>) <span class=\"hljs-keyword\">return<\/span>;\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> previewName = csvData&#91;<span class=\"hljs-number\">0<\/span>] || <span class=\"hljs-string\">\"\"<\/span>;\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> links: string&#91;] = csvData.map(<span class=\"hljs-function\">(<span class=\"hljs-params\">name<\/span>) =&gt;<\/span> {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> myImage = cld.image(<span class=\"hljs-string\">`<span class=\"hljs-subst\">${selectedTemplate}<\/span>`<\/span>);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0myImage.resize(\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0fill().width(imgOriginalSize.width).height(imgOriginalSize.height)\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> scaleX = imgOriginalSize.width \/ imgSize.width;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> scaleY = imgOriginalSize.height \/ imgSize.height;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> adjustedFontSize = <span class=\"hljs-built_in\">Math<\/span>.round(fontSize * scaleX);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> estimatedTextWidth =\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0adjustedFontSize * (name.length + text.length) * <span class=\"hljs-number\">0.65<\/span>;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> estimatedTextHeight = adjustedFontSize;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">let<\/span> mappedX = <span class=\"hljs-built_in\">Math<\/span>.max(\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-number\">0<\/span>,\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-built_in\">Math<\/span>.min(\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0position.x * scaleX,\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0imgOriginalSize.width - estimatedTextWidth * <span class=\"hljs-number\">0.9<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0)\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> mappedY = <span class=\"hljs-built_in\">Math<\/span>.max(\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-number\">0<\/span>,\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-built_in\">Math<\/span>.min(\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0position.y * scaleY,\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0imgOriginalSize.height - estimatedTextHeight\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0)\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-comment\">\/\/ Prevent overflow on X-axis with finer adjustment<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">if<\/span> (mappedX + estimatedTextWidth &gt; imgOriginalSize.width) {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0mappedX = imgOriginalSize.width - estimatedTextWidth * <span class=\"hljs-number\">0.6<\/span>;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">if<\/span> (mappedX &lt; <span class=\"hljs-number\">0<\/span>) {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0mappedX = <span class=\"hljs-number\">0<\/span>;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> safeFont = font || <span class=\"hljs-string\">\"Arial\"<\/span>; <span class=\"hljs-comment\">\/\/ Default to Arial if font is missing<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> safeFontSize = fontSize &gt; <span class=\"hljs-number\">0<\/span> ? fontSize : <span class=\"hljs-number\">20<\/span>; <span class=\"hljs-comment\">\/\/ Default to 20px if fontSize is invalid<\/span>\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> adjustedFontSizes = <span class=\"hljs-built_in\">Math<\/span>.round(safeFontSize * scaleX);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">if<\/span> (adjustedFontSizes &gt; <span class=\"hljs-number\">0<\/span> &amp;&amp; safeFont) {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0myImage.overlay(\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0source(\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0cloudinaryText(\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-string\">`<span class=\"hljs-subst\">${name}<\/span> <span class=\"hljs-subst\">${text.replace(previewName, <span class=\"hljs-string\">\"\"<\/span>)}<\/span>`<\/span>,\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">new<\/span> TextStyle(safeFont, adjustedFontSizes)\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0).textColor(color)\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0).position(\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">new<\/span> Position()\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.gravity(compass(<span class=\"hljs-string\">\"north_west\"<\/span>))\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.offsetX(<span class=\"hljs-built_in\">Math<\/span>.round(mappedX))\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0.offsetY(<span class=\"hljs-built_in\">Math<\/span>.round(mappedY))\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0)\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">return<\/span> myImage.toURL();\n\n\u00a0\u00a0\u00a0\u00a0});\n\n\u00a0\u00a0\u00a0\u00a0setGeneratedLinks(links);\n\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">if<\/span> (links.length === <span class=\"hljs-number\">1<\/span>) {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> link = <span class=\"hljs-built_in\">document<\/span>.createElement(<span class=\"hljs-string\">\"a\"<\/span>);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0link.href = links&#91;<span class=\"hljs-number\">0<\/span>];\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0link.download = <span class=\"hljs-string\">\"flyer.png\"<\/span>;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-built_in\">document<\/span>.body.appendChild(link);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0link.click();\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-built_in\">document<\/span>.body.removeChild(link);\n\n\u00a0\u00a0\u00a0\u00a0} <span class=\"hljs-keyword\">else<\/span> {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> zip = <span class=\"hljs-keyword\">new<\/span> JSZip();\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> folder = zip.folder(<span class=\"hljs-string\">\"Flyers\"<\/span>);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">if<\/span> (folder) {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">await<\/span> <span class=\"hljs-built_in\">Promise<\/span>.all(\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0links.map(<span class=\"hljs-keyword\">async<\/span> (url, index) =&gt; {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> response = <span class=\"hljs-keyword\">await<\/span> fetch(url);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> blob = <span class=\"hljs-keyword\">await<\/span> response.blob();\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0folder.file(<span class=\"hljs-string\">`flyer_<span class=\"hljs-subst\">${index + <span class=\"hljs-number\">1<\/span>}<\/span>.png`<\/span>, blob);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0})\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> zipBlob = <span class=\"hljs-keyword\">await<\/span> zip.generateAsync({ <span class=\"hljs-attr\">type<\/span>: <span class=\"hljs-string\">\"blob\"<\/span> });\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0saveAs(zipBlob, <span class=\"hljs-string\">\"flyers.zip\"<\/span>);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0};<\/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\n\n<p>This function ensures:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Proper scaling of text size and positioning<\/strong> based on the image\u2019s original size.<\/li>\n\n\n\n<li><strong>Prevention of text overflow<\/strong> on the X-axis by adjusting <code>mappedX<\/code> when needed.<\/li>\n\n\n\n<li><strong>Maintaining correct text centering<\/strong> relative to the preview name.<\/li>\n<\/ul>\n\n\n\n<h4 class=\"wp-block-heading\">downloadingFlyers<\/h4>\n\n\n\n<p>Once the flyers are generated, they\u2019re either downloaded individually (if there\u2019s only one) or bundled into a ZIP file (if there are multiple).<\/p>\n\n\n\n<p>If only one flyer is generated, it\u2019s downloaded directly as a PNG:<\/p>\n\n\n<pre class=\"wp-block-code\" 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\">if<\/span> (links.length === <span class=\"hljs-number\">1<\/span>) {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> link = <span class=\"hljs-built_in\">document<\/span>.createElement(<span class=\"hljs-string\">\"a\"<\/span>);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0link.href = links&#91;<span class=\"hljs-number\">0<\/span>];\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0link.download = <span class=\"hljs-string\">\"flyer.png\"<\/span>;\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-built_in\">document<\/span>.body.appendChild(link);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0link.click();\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-built_in\">document<\/span>.body.removeChild(link);\n\n\u00a0}<\/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\n\n<p>If multiple flyers are generated, they\u2019re fetched as blobs, added to a ZIP file using JSZip, and then saved using file-saver:<\/p>\n\n\n<pre class=\"wp-block-code\" 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\">else<\/span> {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> zip = <span class=\"hljs-keyword\">new<\/span> JSZip();\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> folder = zip.folder(<span class=\"hljs-string\">\"Flyers\"<\/span>);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">if<\/span> (folder) {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">await<\/span> <span class=\"hljs-built_in\">Promise<\/span>.all(\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0links.map(<span class=\"hljs-keyword\">async<\/span> (url, index) =&gt; {\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> response = <span class=\"hljs-keyword\">await<\/span> fetch(url);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> blob = <span class=\"hljs-keyword\">await<\/span> response.blob();\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0folder.file(<span class=\"hljs-string\">`flyer_<span class=\"hljs-subst\">${index + <span class=\"hljs-number\">1<\/span>}<\/span>.png`<\/span>, blob);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0})\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-keyword\">const<\/span> zipBlob = <span class=\"hljs-keyword\">await<\/span> zip.generateAsync({ <span class=\"hljs-attr\">type<\/span>: <span class=\"hljs-string\">\"blob\"<\/span> });\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0saveAs(zipBlob, <span class=\"hljs-string\">\"flyers.zip\"<\/span>);\n\n\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0\u00a0\u00a0\u00a0}\n\n\u00a0};<\/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\n\n<h2 class=\"wp-block-heading\">Part 3: Install Dependencies<\/h2>\n\n\n\n<p>In the root of the project, you have the package.json. Replace everything that you currently have in the package.json, then copy and paste the content of <a href=\"https:\/\/github.com\/cloudinary-devs\/Cloudinary-ImageMaker-React\/blob\/main\/package.json\">this file<\/a> into your package.json.<\/p>\n\n\n\n<p>Then run:<\/p>\n\n\n<pre class=\"wp-block-code\"><span><code class=\"hljs shcb-wrap-lines\">npm install<\/code><\/span><\/pre>\n\n\n<p>This command will install the following dependencies:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong><code>@cloudinary\/react<\/code>.<\/strong> React SDK for Cloudinary, enabling dynamic image rendering and transformations in React components.<\/li>\n\n\n\n<li><strong><code>@cloudinary\/url-gen<\/code><\/strong>. Generates Cloudinary URLs for image transformations like resizing, overlays, and text placement.<\/li>\n\n\n\n<li><strong><code>@tailwindcss\/vite<\/code><\/strong>. Optimizes Tailwind CSS integration with Vite for faster builds and better performance.<\/li>\n\n\n\n<li><strong><code>file-saver<\/code><\/strong>. Allows users to download files (images, ZIPs) directly from the browser.<\/li>\n\n\n\n<li><strong><code>jszip<\/code>.<\/strong> Creates ZIP files on the client side for bulk downloads of generated flyers.<\/li>\n\n\n\n<li><strong><code>papaparse<\/code>.<\/strong> Parses CSV files efficiently, extracting name data for flyer customization.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Configure TailwindCSS<\/h3>\n\n\n\n<p>Run the following commands:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-wrap-lines\"><span class=\"hljs-selector-tag\">npm<\/span> <span class=\"hljs-selector-tag\">install<\/span> <span class=\"hljs-selector-tag\">tailwindcss<\/span> <span class=\"hljs-keyword\">@tailwindcss<\/span>\/vite<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\">Configure the Vite plugin<\/h3>\n\n\n\n<p>Add the <code>@tailwindcss\/vite<\/code> plugin to your Vite configuration.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">import<\/span> { defineConfig } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'vite'<\/span>\n\n<span class=\"hljs-keyword\">import<\/span> tailwindcss <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'@tailwindcss\/vite'<\/span>\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> defineConfig({\n\n\u00a0<span class=\"hljs-attr\">plugins<\/span>: &#91;\n\n\u00a0\u00a0\u00a0tailwindcss(),\n\n\u00a0],\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\n\n<p>Now in the index.css file, add the following:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-16\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css shcb-wrap-lines\"><span class=\"hljs-keyword\">@import<\/span> <span class=\"hljs-string\">\"tailwindcss\"<\/span>;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-16\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\">Part 4: Test the App<\/h2>\n\n\n\n<p>The first thing we have to do is to run<strong> <\/strong><code>npm install<\/code><strong> <\/strong>in the root of your project to install the frontend and backend dependencies.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Run the Frontend Server<\/h3>\n\n\n\n<p>Open your other terminal, run <code>npm run dev<\/code>,<strong> <\/strong>and navigate to <a href=\"http:\/\/localhost:5173\/\">http:\/\/localhost:5173\/<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>Building an automated invite maker with React and Cloudinary streamlines the invitation process, saving time and effort while ensuring a seamless user experience. By leveraging Cloudinary for image storage, optimization, text overlays, and downloadable assets, we\u2019ve created a solution that efficiently generates personalized and automated invitations.<\/p>\n\n\n\n<p>This approach demonstrates how powerful <a href=\"https:\/\/cloudinary.com\/guides\/automations\/media-automation\">media automation<\/a> can be. Try it out, expand on it, and take your event management workflow to the next level!<\/p>\n\n\n\n<p>To stay updated with the latest product features, follow <a href=\"https:\/\/twitter.com\/cloudinary\">Cloudinary<\/a> on Twitter or explore other <a href=\"https:\/\/app-gallery.cloudinary.com\/\">sample apps<\/a>. <a href=\"https:\/\/cloudinary.com\/users\/register_free\">Sign up for a free Cloudinary account<\/a> today to get started.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Resources<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>GitHub Repo<\/strong>: <a href=\"https:\/\/github.com\/cloudinary-devs\/Cloudinary-ImageMaker-React\">&nbsp;Cloudinary-React-Image-to-Blog-AI<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Adding names to invitations can be tedious, especially when dealing with large guest lists. But what if you could automate the process with just a simple file upload? In this blog post, we\u2019ll build an invite maker using React and Cloudinary, allowing users to upload a CSV file and automatically populate invitations with invitee names. [&hellip;]<\/p>\n","protected":false},"author":87,"featured_media":37346,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[25,246,373],"class_list":["post-37344","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-asset-management","tag-react","tag-upload"],"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>Smart Invites Made Easy: Automate With Cloudinary and React<\/title>\n<meta name=\"description\" content=\"Adding names to invitations can be tedious, especially when dealing with large guest lists. But what if you could automate the process with just a simple\" \/>\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\/smart-invites-made-easy-automate-react\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Smart Invites Made Easy: Automate With Cloudinary and React\" \/>\n<meta property=\"og:description\" content=\"Adding names to invitations can be tedious, especially when dealing with large guest lists. But what if you could automate the process with just a simple\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/smart-invites-made-easy-automate-react\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2025-04-02T14:00:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2026-03-08T16:11:51+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1743187987\/Blog_Smart_Invites_Made_Easy\/Blog_Smart_Invites_Made_Easy.jpg?_i=AA\" \/>\n\t<meta property=\"og:image:width\" content=\"2000\" \/>\n\t<meta property=\"og:image:height\" content=\"1100\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"melindapham\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"NewsArticle\",\"@id\":\"https:\/\/cloudinary.com\/blog\/smart-invites-made-easy-automate-react#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/smart-invites-made-easy-automate-react\"},\"author\":{\"name\":\"melindapham\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9\"},\"headline\":\"Smart Invites Made Easy: Automate With Cloudinary and React\",\"datePublished\":\"2025-04-02T14:00:00+00:00\",\"dateModified\":\"2026-03-08T16:11:51+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/smart-invites-made-easy-automate-react\"},\"wordCount\":1324,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/smart-invites-made-easy-automate-react#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1743187987\/Blog_Smart_Invites_Made_Easy\/Blog_Smart_Invites_Made_Easy.jpg?_i=AA\",\"keywords\":[\"Asset Management\",\"React\",\"Upload\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2025\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/smart-invites-made-easy-automate-react\",\"url\":\"https:\/\/cloudinary.com\/blog\/smart-invites-made-easy-automate-react\",\"name\":\"Smart Invites Made Easy: Automate With Cloudinary and React\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/smart-invites-made-easy-automate-react#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/smart-invites-made-easy-automate-react#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1743187987\/Blog_Smart_Invites_Made_Easy\/Blog_Smart_Invites_Made_Easy.jpg?_i=AA\",\"datePublished\":\"2025-04-02T14:00:00+00:00\",\"dateModified\":\"2026-03-08T16:11:51+00:00\",\"description\":\"Adding names to invitations can be tedious, especially when dealing with large guest lists. But what if you could automate the process with just a simple\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/smart-invites-made-easy-automate-react#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/smart-invites-made-easy-automate-react\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/smart-invites-made-easy-automate-react#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1743187987\/Blog_Smart_Invites_Made_Easy\/Blog_Smart_Invites_Made_Easy.jpg?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1743187987\/Blog_Smart_Invites_Made_Easy\/Blog_Smart_Invites_Made_Easy.jpg?_i=AA\",\"width\":2000,\"height\":1100},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/smart-invites-made-easy-automate-react#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Smart Invites Made Easy: Automate With Cloudinary and 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\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9\",\"name\":\"melindapham\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g\",\"caption\":\"melindapham\"}}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Smart Invites Made Easy: Automate With Cloudinary and React","description":"Adding names to invitations can be tedious, especially when dealing with large guest lists. But what if you could automate the process with just a simple","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\/smart-invites-made-easy-automate-react","og_locale":"en_US","og_type":"article","og_title":"Smart Invites Made Easy: Automate With Cloudinary and React","og_description":"Adding names to invitations can be tedious, especially when dealing with large guest lists. But what if you could automate the process with just a simple","og_url":"https:\/\/cloudinary.com\/blog\/smart-invites-made-easy-automate-react","og_site_name":"Cloudinary Blog","article_published_time":"2025-04-02T14:00:00+00:00","article_modified_time":"2026-03-08T16:11:51+00:00","og_image":[{"width":2000,"height":1100,"url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1743187987\/Blog_Smart_Invites_Made_Easy\/Blog_Smart_Invites_Made_Easy.jpg?_i=AA","type":"image\/jpeg"}],"author":"melindapham","twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/cloudinary.com\/blog\/smart-invites-made-easy-automate-react#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/smart-invites-made-easy-automate-react"},"author":{"name":"melindapham","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9"},"headline":"Smart Invites Made Easy: Automate With Cloudinary and React","datePublished":"2025-04-02T14:00:00+00:00","dateModified":"2026-03-08T16:11:51+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/smart-invites-made-easy-automate-react"},"wordCount":1324,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/smart-invites-made-easy-automate-react#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1743187987\/Blog_Smart_Invites_Made_Easy\/Blog_Smart_Invites_Made_Easy.jpg?_i=AA","keywords":["Asset Management","React","Upload"],"inLanguage":"en-US","copyrightYear":"2025","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/smart-invites-made-easy-automate-react","url":"https:\/\/cloudinary.com\/blog\/smart-invites-made-easy-automate-react","name":"Smart Invites Made Easy: Automate With Cloudinary and React","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/smart-invites-made-easy-automate-react#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/smart-invites-made-easy-automate-react#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1743187987\/Blog_Smart_Invites_Made_Easy\/Blog_Smart_Invites_Made_Easy.jpg?_i=AA","datePublished":"2025-04-02T14:00:00+00:00","dateModified":"2026-03-08T16:11:51+00:00","description":"Adding names to invitations can be tedious, especially when dealing with large guest lists. But what if you could automate the process with just a simple","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/smart-invites-made-easy-automate-react#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/smart-invites-made-easy-automate-react"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/smart-invites-made-easy-automate-react#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1743187987\/Blog_Smart_Invites_Made_Easy\/Blog_Smart_Invites_Made_Easy.jpg?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1743187987\/Blog_Smart_Invites_Made_Easy\/Blog_Smart_Invites_Made_Easy.jpg?_i=AA","width":2000,"height":1100},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/smart-invites-made-easy-automate-react#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Smart Invites Made Easy: Automate With Cloudinary and 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":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9","name":"melindapham","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g","caption":"melindapham"}}]}},"jetpack_featured_media_url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1743187987\/Blog_Smart_Invites_Made_Easy\/Blog_Smart_Invites_Made_Easy.jpg?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/37344","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\/87"}],"replies":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/comments?post=37344"}],"version-history":[{"count":4,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/37344\/revisions"}],"predecessor-version":[{"id":39855,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/37344\/revisions\/39855"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/37346"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=37344"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=37344"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=37344"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}