<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rebeccca Peltz</title>
    <description>The latest articles on DEV Community by Rebeccca Peltz (@rebeccapeltz).</description>
    <link>https://dev.to/rebeccapeltz</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F117080%2Fa57076ee-5194-49a5-b094-7f8615bb452f.jpeg</url>
      <title>DEV Community: Rebeccca Peltz</title>
      <link>https://dev.to/rebeccapeltz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rebeccapeltz"/>
    <language>en</language>
    <item>
      <title>daily javascript challenge</title>
      <dc:creator>Rebeccca Peltz</dc:creator>
      <pubDate>Wed, 01 Jan 2025 00:41:39 +0000</pubDate>
      <link>https://dev.to/rebeccapeltz/daily-javascript-challenge-432p</link>
      <guid>https://dev.to/rebeccapeltz/daily-javascript-challenge-432p</guid>
      <description></description>
      <category>javascript</category>
    </item>
    <item>
      <title>Stop Phishing by Analyzing the Bait</title>
      <dc:creator>Rebeccca Peltz</dc:creator>
      <pubDate>Fri, 12 Jul 2024 21:10:44 +0000</pubDate>
      <link>https://dev.to/rebeccapeltz/stop-phishing-by-analyzing-the-bait-3f0f</link>
      <guid>https://dev.to/rebeccapeltz/stop-phishing-by-analyzing-the-bait-3f0f</guid>
      <description>&lt;p&gt;CyberSecurityNews.com published a gif on X showing &lt;a href="https://x.com/The_Cyber_News/status/1769244926984765858" rel="noopener noreferrer"&gt;Top 8 Cyber Attacks 2024&lt;/a&gt; last spring that shows the top 8 cybersecurity threats for 2024.  This chart has popped up in many places.  I like the way it's paired with Beethoven's Moonlight Sonata here:&lt;/p&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
      &lt;div class="c-embed__cover"&gt;
        &lt;a href="https://www.youtube.com/watch?si=ilOytMBbtNXxKWhg&amp;amp;v=aVPfexDpikM&amp;amp;feature=youtu.be" class="c-link s:max-w-50 align-middle" rel="noopener noreferrer"&gt;
          &lt;img alt="" src="https://res.cloudinary.com/practicaldev/image/fetch/s--mEYx7z2J--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.ytimg.com/vi/aVPfexDpikM/maxresdefault.jpg" height="450" class="m-0" width="800"&gt;
        &lt;/a&gt;
      &lt;/div&gt;
    &lt;div class="c-embed__body"&gt;
      &lt;h2 class="fs-xl lh-tight"&gt;
        &lt;a href="https://www.youtube.com/watch?si=ilOytMBbtNXxKWhg&amp;amp;v=aVPfexDpikM&amp;amp;feature=youtu.be" rel="noopener noreferrer" class="c-link"&gt;
          Top 8 Cyber Attacks - 2024 [4K] - YouTube
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;p class="truncate-at-3"&gt;
          Top 8 Cyber Attacks (2024):1. Phishing2. Ransomware3. Denial-of-Service (DoS)4. Man in the Middle (MitM)5. SQL Injection6. Cross-site Scripting (XSS)7. Zero-...
        &lt;/p&gt;
      &lt;div class="color-secondary fs-s flex items-center"&gt;
          &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://res.cloudinary.com/practicaldev/image/fetch/s--FV5taQCC--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://www.youtube.com/s/desktop/86d8f362/img/favicon.ico" width="16" height="16"&gt;
        youtube.com
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;I've worked on product releases where we just scoured the front end to try to eliminate any HTML that might lead to a security issue&lt;/p&gt;

&lt;p&gt;If you look closely at the list 3 out 8 of these attacks rely on front end software, that is, a vulnerability in the browser.  Fullstack application developers can use this awareness to help prevent these problems.  The list of attacks is shown below. Attacks 1, 5, and 6 are attacks that occur in the browser.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Phishing&lt;/li&gt;
&lt;li&gt;Ransomware&lt;/li&gt;
&lt;li&gt;Denial of Service (DoS)&lt;/li&gt;
&lt;li&gt;Man-in-the-middle (MiTM)&lt;/li&gt;
&lt;li&gt;SQL Injection&lt;/li&gt;
&lt;li&gt;Cross Site Scripting (XSS)&lt;/li&gt;
&lt;li&gt;Zero-Day Exploits&lt;/li&gt;
&lt;li&gt;DNS Spoofing&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both Phishing and Cross Site Scripting rely on links that a user clicks on to carry out the attack. In the case of Phishing the link is in often in an email.  &lt;/p&gt;

&lt;p&gt;In this post, I'm going to focus on Phishing.  Both Phishing and Cross Site Scripting are forms of Social Engineering that rely on the user's trust and possibly lack of knowledge.  I'm going to focus on Phishing in this post because it is one of the most prevalent types of attack.&lt;/p&gt;

&lt;p&gt;Companies are aware of the losses that result from cyber crimes, and instruct employees in what to look for to prevent attacks that involve links. However, the nature and attributes of the HTML anchor element that is used to create links might make it difficult for users to spot a problem with the link.&lt;/p&gt;

&lt;p&gt;I've worked on product releases where the goal is rework any HTML that could create a vulnerability.  But in the end, if you tell a user to 'hover over each link on the email and make sure that the underlying URL looks OK', you're asking a lot.&lt;/p&gt;

&lt;p&gt;Let's look at the anchor tag attribute variations that might make it difficult for a user to just hover over the link to determine if the link is OK.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Expected Anchor Element.&lt;/strong&gt;&lt;br&gt;
    The HTML anchor tag was created to make navigation between web &lt;br&gt;
    pages easier.  The structure of a basic anchor element does &lt;br&gt;
    this includes a URL assigned to an href attribute and text &lt;br&gt;
    content that describes where the URL will navigate to.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```html
&amp;lt;a href="https://www.example.com" target="_blank"&amp;gt;Example&amp;lt;/a&amp;gt;
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;The design of the anchor element strongly suggests that the 
user shouldn't have to know what the URL is.  If the URL is to 
be known, it could be copied in the text content.  If all the 
URLs were spelled out, it would make for an unattractive and 
probably messy looking web page.  This variation is the most 
commonly used.  If the user hovers over `Example` they will see 
`https://www.example.com`.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;2. Using &lt;code&gt;onclick&lt;/code&gt; in an Anchor Element.&lt;/strong&gt; &lt;br&gt;
    We can introduce more complex instructions into the anchor tag &lt;br&gt;
    by adding an &lt;code&gt;onclick&lt;/code&gt; event handler and JavaScript.  Here are &lt;br&gt;
    three examples of what we can do when we want to run some &lt;br&gt;
    JavaScript when the link is clicked.  In all cases, hovering &lt;br&gt;
    over the link won't show you the &lt;code&gt;onclick&lt;/code&gt; event.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;**a. `onclick` returns false.**

When you the JavaScript in the onclick handler returns false, 
the navigation to the URL in href is not executed.  But if 
there is a problem from the JavaScript itself, it's too late 
once you've clicked on a link in which hovering over it showed 
a URL that was OK
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```html
&amp;lt;a href="https://www.example.com" target="_blank" 
onclick="alert('hello'); return false;"&amp;gt;Say Hello&amp;lt;/a&amp;gt;
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;**b. `onclick` doesn't return false.**

If the `onclick` handler JavaScript code doesn't return false, 
then the link will navigate as intended.  Hovering over the 
link will return an OK URL and the code will navigate to the 
URL.  In this case, all will seem well, but if the JavaScript 
contained a problem, it's too late to fix it.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```html
&amp;lt;a href="https://www.example.com" onClick="alert('found me')" 
target="_blank"&amp;gt;onClick me&amp;lt;/a&amp;gt;
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;**c. anchor tag doesn't contain an href attribute.**  

If the `href` is not included in the anchor tag element will 
render without text decoration, but will still be clickable if 
an `onclick` is included.  For users that are used to clickable 
items showing some sign of click-ability like `text-decoration` 
or a CSS that style to clickable text to look like a button, 
there may be some confusion here.  A user instructed to hover 
over links would likely ignore clicking on this entirely.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```html
&amp;lt;a onClick="alert('found me')" target="_blank"&amp;gt;onclick me&amp;lt;/a&amp;gt;
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. No encryption in URL.&lt;/strong&gt; &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;If the URL references a site that doesn't use encryption, like 
`http://www.example.com` vs. `https://www.example.com`, this 
could a raise a question.  These days browser will often 
encrypt the web page even if the scheme is `http`.  There are 
extensions and settings to make browsers automatically encrypt, 
but if the user hovers, and finds that HTTP instead of HTTPS is
used, this could be the sign of a problem.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```html
&amp;lt;a href="http://info.cern.ch/" target="_blank"
onclick="alert('my href is unencrypted but chrome will 
make it secure')"; 
return true;&amp;gt;info.cern.ch is unencrypted&amp;lt;/a&amp;gt;
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;4. &lt;code&gt;href&lt;/code&gt; refers to a URI with a &lt;code&gt;javascript:&lt;/code&gt; pseudo scheme.&lt;/strong&gt;  &lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Although it's most common to see a scheme in a URI like 
`https:`, other schemes are in use.  For example `mailto:` and 
`tel:` are used to send message via email and SMS.  These 
schemes are followed by email addresses and telephone numbers. 
These are refereed to as URI's instead of URL's.  The scheme is 
provided an **identifier** instead of a **location**.  Both 
`mailto:` and `tel:` are visible when hovering on a link.  

It's also possible to use `javascript:` as a scheme.  It is 
then followed by a call to a function in the context of the 
HTML.  In the example below it is calling the function 
`doSomething`.  This will be visible upon hovering, but because 
the contents of the JavaScript code is not known, it may raise 
a question.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;```html
&amp;lt;a href="javascript:doSomething()"&amp;gt;Javascript me&amp;lt;/a&amp;gt;  
```
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I hope the examples above show how difficult it could be for a user to determine if a link in their email should be clicked on or reported as a possible threat.  What's the solution?&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a Browser Extension
&lt;/h2&gt;

&lt;p&gt;What is needed is a full analysis and report on what is going on within the links on a webpage.  There needs to be a way to quickly generate some information that shows the links without hovering and provides alerts as to which links may have some unexpected attribute settings.  We need a Phish Bait analyzer report.  It would be nice if browsers had this feature, but until they do we can create a Chrome extension to do it.&lt;/p&gt;

&lt;p&gt;I created an extension named &lt;a href="https://chromewebstore.google.com/detail/link-reveal/fmmmcdmfaadidngnfnidhlmpgigmnhjh" rel="noopener noreferrer"&gt;Link Reveal&lt;/a&gt; and published it in the Chrome Web Store. I'm not the only one that thought of this.  If you search the Store for terms like "link" or "phish", you'll find many extensions that relate to the anchor tag.  If you're interested, you can also look at the &lt;a href="https://github.com/rebeccapeltz/link-reveal-ext" rel="noopener noreferrer"&gt;code&lt;/a&gt;.  There are a couple of test pages you can use:  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.beckypeltz.me/link-reveal-ext/" rel="noopener noreferrer"&gt;Page with a lot of different anchor tags&lt;/a&gt;.&lt;br&gt;&lt;br&gt;
&lt;a href="https://www.beckypeltz.me/link-reveal-ext/nolinks.html" rel="noopener noreferrer"&gt;Page with no anchor tags&lt;/a&gt;. &lt;/p&gt;

&lt;h2&gt;
  
  
  Other Solutions that Preventing Phishing
&lt;/h2&gt;

&lt;p&gt;The Chrome extension is useful for getting a better look at the attributes of rendered anchor elements.  In some cases, once an email web page is opened, the malware has already been installed. There are tools to investigate emails before they are opened.  Any links containing suspicious URLs or URI can be removed before the user can access them. This &lt;a href="https://www.leadfeeder.com/blog/best-email-tracking-tools/" rel="noopener noreferrer"&gt;blog&lt;/a&gt; summarizes some of those tools and how they work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn More About Cybersecurity
&lt;/h2&gt;

&lt;p&gt;The chart below shows that phishing is on the rise.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpzevx1bofghytcm6olnv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpzevx1bofghytcm6olnv.png" alt="Phishing attacks" width="800" height="550"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.thesslstore.com/blog/phishing-statistics/" rel="noopener noreferrer"&gt;credit: phishing-statistics&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cybersecurity awareness is important for developers.  I joined &lt;a href="https://www.isc2.org/" rel="noopener noreferrer"&gt;ICS2&lt;/a&gt; and learned a lot. &lt;/p&gt;

&lt;p&gt;See this &lt;a href="https://rpeltz.gitbook.io/create-and-publish-a-chrome-browser-extension" rel="noopener noreferrer"&gt;guide&lt;/a&gt; for creating and publishing the browser extension described in this post.&lt;/p&gt;

</description>
      <category>cybersecurity</category>
      <category>phishing</category>
      <category>browserextension</category>
    </item>
    <item>
      <title>Earth Day Frontend Challenge</title>
      <dc:creator>Rebeccca Peltz</dc:creator>
      <pubDate>Wed, 24 Apr 2024 18:19:02 +0000</pubDate>
      <link>https://dev.to/rebeccapeltz/earth-day-frontend-challenge-4m9i</link>
      <guid>https://dev.to/rebeccapeltz/earth-day-frontend-challenge-4m9i</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/rebeccapeltz/earth" rel="noopener noreferrer"&gt;code&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.beckypeltz.me/earth/" rel="noopener noreferrer"&gt;demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3imsfvzvnxhv33jof5fm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3imsfvzvnxhv33jof5fm.png" alt="Earth Day Challenge Frontend" width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Dynamically Inserting Video/Image
&lt;/h2&gt;

&lt;p&gt;I used JavaScript to dynamically insert a Video on larger viewports and an image for smaller view ports.  I organized the HTML with flexbox.&lt;/p&gt;

&lt;p&gt;There were some challenges organizing the HTML for styling.  Thinking about container layouts is helpful when writing HTML.&lt;/p&gt;

&lt;p&gt;The video is from Pexels and there is a credit in the code README.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>frontendchallenge</category>
      <category>css</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Earth Day Frontend Challenge</title>
      <dc:creator>Rebeccca Peltz</dc:creator>
      <pubDate>Wed, 24 Apr 2024 18:19:02 +0000</pubDate>
      <link>https://dev.to/rebeccapeltz/earth-day-frontend-challenge-71k</link>
      <guid>https://dev.to/rebeccapeltz/earth-day-frontend-challenge-71k</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/rebeccapeltz/earth" rel="noopener noreferrer"&gt;code&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.beckypeltz.me/earth/" rel="noopener noreferrer"&gt;demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3imsfvzvnxhv33jof5fm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3imsfvzvnxhv33jof5fm.png" alt="Earth Day Challenge Frontend" width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Dynamically Inserting Video/Image
&lt;/h2&gt;

&lt;p&gt;I used JavaScript to dynamically insert a Video or larger viewports and an image for smaller view ports.  I organized the HTML with flexbox.&lt;/p&gt;

&lt;p&gt;There were some challenges organizing the HTML for styling.  Thinking about container layouts is helpful when writing HTML.&lt;/p&gt;

&lt;p&gt;The video is from Pexels and there is a credit in the code README.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>frontendchallenge</category>
      <category>css</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Recipe Generator</title>
      <dc:creator>Rebeccca Peltz</dc:creator>
      <pubDate>Fri, 12 Apr 2024 01:52:03 +0000</pubDate>
      <link>https://dev.to/rebeccapeltz/recipe-generator-25c5</link>
      <guid>https://dev.to/rebeccapeltz/recipe-generator-25c5</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6migek9ih9a7gkze4nmi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6migek9ih9a7gkze4nmi.png" alt="Recipe Generator Code REAMDE"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/devteam/join-us-for-the-cloudflare-ai-challenge-3000-in-prizes-5f99"&gt;Cloudflare AI Challenge&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;Looking up a recipe on the web can take a lot longer than you would link. The ingredients and the instructions are often buried in a blog with a lot of ads popping up throughout.&lt;/p&gt;

&lt;p&gt;In this application, using Cloudflare models for text and image generation, you can ask for a recipe based on a food category and a description. The food category is used to enhance the prompt, instead of just allowing an open ended text input for the user to describe the recipe they are looking for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://recipe-generator.rebeccapeltz.workers.dev/" rel="noopener noreferrer"&gt;https://recipe-generator.rebeccapeltz.workers.dev/&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  My Code
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/rebeccapeltz/recipe-generator" rel="noopener noreferrer"&gt;https://github.com/rebeccapeltz/recipe-generator&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Journey
&lt;/h2&gt;

&lt;p&gt;I've been frustrated with Recipe websites, so I'm using Cloudflare AI Web Workers to generate recipes.  You no longer need to read a blog and close obstructing ads to get to the recipe. The recipe can be printed without having to make a lot of decisions about what to print.&lt;/p&gt;

&lt;p&gt;I was inspired by an early submission: &lt;a href="https://dev.to/bobbyiliev/my-cloudflare-ai-challenge-submission-story-and-image-generator-4fc3"&gt;Story and Image Generator&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I set my code up a little differently, but I'm using the same AI models as the story generator.&lt;/p&gt;

&lt;p&gt;I wanted to make the prompt a little more structured so I'm having the user choose a category and then add a description.  I could enhance this by adding more categories. I wanted to get away from a open ended prompt.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Multiple Models and/or Triple Task Types&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Im using "&lt;a class="mentioned-user" href="https://dev.to/hf"&gt;@hf&lt;/a&gt;/thebloke/llama-2-13b-chat-awq" for the recipe and "&lt;a class="mentioned-user" href="https://dev.to/cf"&gt;@cf&lt;/a&gt;/bytedance/stable-diffusion-xl-lightning" to generate the image of the what the recipe will prepare.&lt;/p&gt;

</description>
      <category>cloudflarechallenge</category>
      <category>devchallenge</category>
      <category>ai</category>
    </item>
    <item>
      <title>Form Processing</title>
      <dc:creator>Rebeccca Peltz</dc:creator>
      <pubDate>Mon, 01 Apr 2024 03:04:31 +0000</pubDate>
      <link>https://dev.to/rebeccapeltz/form-processing-4eia</link>
      <guid>https://dev.to/rebeccapeltz/form-processing-4eia</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for DEV Challenge v24.03.20, Glam Up My Markup: Camp Activities&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;I created a form that process data about a camp goer's interest, allergies and general info.   &lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;The code is in replit.com:&lt;br&gt;&lt;br&gt;
&lt;a href="https://replit.com/@RebeccaPeltz/SubtleFinishedGraph" rel="noopener noreferrer"&gt;Front end form processing&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Journey
&lt;/h2&gt;

&lt;p&gt;I didn't notice at first that the works was "Which camp activities" was plural. After I saw that, I changed my select to allow for multiple selections.  If this was full stack, I would have set up a way to search google for items entered in the form. &lt;br&gt;
Since it's Easter, you get a bunny when you submit.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/@rebeccapeltz"&gt;https://dev.to/@rebeccapeltz&lt;/a&gt;&lt;/p&gt;

</description>
      <category>frontendchallenge</category>
      <category>devchallenge</category>
      <category>css</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Using Web APIs to Create a Camera Application</title>
      <dc:creator>Rebeccca Peltz</dc:creator>
      <pubDate>Wed, 25 Oct 2023 04:04:14 +0000</pubDate>
      <link>https://dev.to/rebeccapeltz/using-web-apis-to-create-a-camera-application-oip</link>
      <guid>https://dev.to/rebeccapeltz/using-web-apis-to-create-a-camera-application-oip</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkhtc522k3oknzgiknr6r.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkhtc522k3oknzgiknr6r.jpg" alt="Camera" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/rebeccapeltz/webapi-camera" rel="noopener noreferrer"&gt;code&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.beckypeltz.online/webapi-camera/" rel="noopener noreferrer"&gt;demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I enjoy working on applications that connect the physical and digital worlds. I'll show you how to build a camera application in this post.&lt;/p&gt;
&lt;h2&gt;
  
  
  Web APIs
&lt;/h2&gt;

&lt;p&gt;If you were working as a web developer in 2010, you might remember &lt;a href="https://alistapart.com/article/responsive-web-design/" rel="noopener noreferrer"&gt;the blog post in A List Apart&lt;/a&gt; that introduced the idea of Responsive layouts to a community that was wondering how to work with so many devices and screen sizes. Media queries, fluid layouts, and flexible images allowed developers to create "m-dot" websites like &lt;code&gt;https://m.mydomain.com&lt;/code&gt; which were served to mobile browsers. CSS has continued to evolve to address the needs of mobile web development.&lt;/p&gt;

&lt;p&gt;Native frameworks and languages that could take advantage of the technology available only on mobile devices like cameras and GPS sensors and the ability to provide access without network service led to applications with features not available in web applications. The mobile user could use cameras to take pictures, GPS to navigate the roads, and access the application even when no network was available.&lt;/p&gt;

&lt;p&gt;Over that last decade, the W3C's Web Applications Working Group has created specs that, when implemented in a browser, enable web developers to create many of the features found on mobile devices. In this blog, I will describe how to create a web application that makes it easy for a user to take a picture on a desktop or mobile device and then download or share it using Web APIs.&lt;/p&gt;

&lt;p&gt;Web APIs vary with the implementation per browser. It is wise to check &lt;a href="https://caniuse.com/" rel="noopener noreferrer"&gt;caniuse.com&lt;/a&gt; to verify that the browsers we expect our application to run in are supported. Often, additional research or experimentation is needed to see differences in implementation.&lt;/p&gt;
&lt;h2&gt;
  
  
  A Camera Application for Desktop and Mobile
&lt;/h2&gt;

&lt;p&gt;The images below demonstrate the operation of the camera. Buttons are enabled based on the state of the camera. Access the camera demo &lt;a href="https://www.beckypeltz.me/webapi-camera/" rel="noopener noreferrer"&gt;here&lt;/a&gt;, and try it out before digging into the code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7muabc14mt4jaj07nucy.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7muabc14mt4jaj07nucy.jpg" alt="Press start to open video camera" width="800" height="195"&gt;&lt;/a&gt; &lt;/p&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsb2ibhjtlq16lj3ssb0z.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsb2ibhjtlq16lj3ssb0z.jpeg" alt="Press snapshot to take a picture" width="800" height="450"&gt;&lt;/a&gt; &lt;/p&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmljq41f8l0ylvo26q8ry.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmljq41f8l0ylvo26q8ry.jpeg" alt="Press Flip Location to Swap the Location of the Video and Canvas Containers&amp;lt;br&amp;gt;
" width="800" height="450"&gt;&lt;/a&gt; &lt;/p&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faz7fye7b7iepwefkif3z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faz7fye7b7iepwefkif3z.png" alt="Flip Camera Location is Helpful on a Mobile Device&amp;lt;br&amp;gt;
" width="800" height="448"&gt;&lt;/a&gt; &lt;/p&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2n4t86j1rzjenpue2sn8.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2n4t86j1rzjenpue2sn8.jpeg" alt="Back and Front-Facing Devices on a Mobile Phone&amp;lt;br&amp;gt;
" width="800" height="450"&gt;&lt;/a&gt;  &lt;/p&gt;



&lt;p&gt;Press Start to Open the Video CameraPress Snapshot to Take a PicturePress Flip Location to Swap the Location of the Video and Canvas ContainersFlip Camera Location is Helpful on a Mobile DeviceBack and Front-Facing Devices on a Mobile Phone&lt;/p&gt;

&lt;p&gt;Let's see how to create this web application using Web APIs.&lt;/p&gt;
&lt;h2&gt;
  
  
  Web APIs
&lt;/h2&gt;

&lt;p&gt;Documentation of &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API" rel="noopener noreferrer"&gt;Web APIs and interfaces&lt;/a&gt; is located on the MDN (Mozilla Developer Network) website. The term "Web API" can refer to browser or server APIs. In this article, it refers to &lt;strong&gt;Browser APIs&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The Web API Camera Application is built using these Web APIs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  DOM&lt;/li&gt;
&lt;li&gt;  HTML DOM&lt;/li&gt;
&lt;li&gt;  Media Capture and Streams&lt;/li&gt;
&lt;li&gt;  Canvas&lt;/li&gt;
&lt;li&gt;  Web Share&lt;/li&gt;
&lt;li&gt;  Permissions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We'll also see how browser permissions require a response from the user because they must allow the application to access hardware.&lt;/p&gt;
&lt;h1&gt;
  
  
  DOM and DOM HTML
&lt;/h1&gt;

&lt;p&gt;The DOM (Document Object Model) is the first Web API. The interfaces in the DOM are accessible using JavaScript. Styling is provided by Cascading Stylesheets (CSS).&lt;/p&gt;

&lt;p&gt;The DOM is a data structure the browser constructs when it parses HTML. This structure represents a Document. The Document object is loaded into the Window object. The Document and Window provide the interfaces for querying and maintaining the application in the browser.&lt;/p&gt;

&lt;p&gt;The camera application uses JavaScript to interact with the DOM. This interaction includes adding and removing elements, adding and removing styles, and firing events when the user interacts with the Document.&lt;/p&gt;

&lt;p&gt;The application comprises a set of buttons turned on and off according to what the user can do at any given time. The image created from the video stream is drawn onto a canvas HTML element.&lt;/p&gt;

&lt;p&gt;The application consists of five buttons enabled based on the user's interaction with the Document. The &lt;strong&gt;Start&lt;/strong&gt; button reveals a container loaded with a video stream, representing the camera view. The first time the user opens the video stream, there will be a prompt for permission because of a request to access media devices. A select element is loaded and displayed. A single option will open a back-facing camera if the application is run on a desktop computer. On a mobile device, there will also be an option to use a front-facing camera. Some mobile devices have multiple back and front cameras, and all these options will be available.&lt;/p&gt;

&lt;p&gt;When the user clicks the &lt;strong&gt;Snapshot&lt;/strong&gt; button, a container with a canvas element is revealed, and the current video frame is loaded into the canvas element. This also enables the &lt;strong&gt;Download&lt;/strong&gt; button and displays a &lt;strong&gt;Share&lt;/strong&gt; button. If the browser is allowed to &lt;strong&gt;Share&lt;/strong&gt;, clicking this button will open up sharing options using different network applications like email and messaging. The &lt;strong&gt;Download&lt;/strong&gt; button downloads the image to the local hard drive. Finally, a &lt;strong&gt;Stop&lt;/strong&gt; button turns off all the other buttons and hides the containers while enabling the &lt;strong&gt;Start&lt;/strong&gt; button.&lt;/p&gt;
&lt;h2&gt;
  
  
  DOM Click Event Listeners
&lt;/h2&gt;

&lt;p&gt;The code below shows how to initialize the buttons when the Document is loaded. The &lt;strong&gt;DOMContentLoaded&lt;/strong&gt; event is fired after the HTML tags have been parsed and the elements rendered. We use the &lt;code&gt;querySelector&lt;/code&gt; interface to get references to DOM HTML elements identified by their id or class name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;document.addEventListener("DOMContentLoaded", (e) =&amp;gt; {  
 ...  

  // set video  
  data.videoEl = document.querySelector("#video");  
  data.canvasEl = document.querySelector("#canvas");  

  // attach click event listeners  
  document.querySelector("#camera").addEventListener("click", (e) =&amp;gt; {  
    start();  
  });  
  document.querySelector("#snapshot").addEventListener("click", (e) =&amp;gt; {  
    snapShot();  
  });  
  document.querySelector("#stop").addEventListener("click", (e) =&amp;gt; {  
    stop();  
  });  
  document.querySelector("#download").addEventListener("click", (e) =&amp;gt; {  
    download();  
  });  
  document.querySelector("#share").addEventListener("click", (e) =&amp;gt; {  
    share();  
  });  
  document.querySelector("#flip").addEventListener("click", (e) =&amp;gt; {  
    flip();  
  });  

 ...  
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  DOM Adding and Removing Elements
&lt;/h2&gt;

&lt;p&gt;The video device selector that allows users to select front or back-facing cameras on mobile devices is dynamically loaded based on the available devices. Once we have found available devices, we can add them to the DOM using &lt;code&gt;document.createElement&lt;/code&gt; and &lt;code&gt;appendChild&lt;/code&gt; to load the Select element options.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; let option = {};  
  option.text = device.label;  
  option.value = device.deviceId;  
  data.options.push(option);  
  var selection = document.createElement("option");  
  selection.value = option.value;  
  selection.text = option.text;  
  document.querySelector("#device-option").appendChild(selection);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;Download&lt;/strong&gt; functionality is implemented using hidden anchor tags, which are added and removed as needed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// add a link  
 let a = document.createElement("a");  
 document.querySelector("body").append(a);  

// remove  
 document.querySelector("body").remove(hiddenLink);  


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  DOM Adding and Removing Styles
&lt;/h2&gt;

&lt;p&gt;As the user moves through the steps needed to access the camera, take a Snapshot, and then download or share, elements are displayed and hidden using two different classes.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;hidden&lt;/code&gt; class is &lt;code&gt;display:none&lt;/code&gt; which means that when this class is added to an element, it is removed from the DOM. This is used to hide and show the video and canvas containers. The canvas container holds the &lt;strong&gt;Snapshot&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function hide(id) {  
  let el = document.querySelector(\`#${id}\`);  
  if (!el.classList.contains("hidden")) {  
    el.classList.add("hidden");  
  }  
}  
function show(id) {  
  let el = document.querySelector(\`#${id}\`);  
  if (el.classList.contains("hidden")) {  
    el.classList.remove("hidden");  
  }  
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;hidden-link&lt;/code&gt; class uses &lt;code&gt;visibility:hidden&lt;/code&gt; which leaves the element in the DOM, but it is not visible. This hidden anchor tag will enable the download of the &lt;strong&gt;Snapshot&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; a.classList.add("hidden-link")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;strong&gt;Flip&lt;/strong&gt; button, made visible when the user clicks on &lt;strong&gt;Snapshot,&lt;/strong&gt; causes the video and canvas containers to change location. This is especially helpful on a mobile screen if you want to look at the image after you've created a &lt;strong&gt;Snapshot&lt;/strong&gt;. The canvas container is initially rendered to the right or below the video container on a smaller viewport.&lt;/p&gt;

&lt;p&gt;The video and canvas containers are rendered using the &lt;code&gt;display: flex&lt;/code&gt; command. We add toggle logic to flip the containers by adding and removing the CSS command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (videoCanvasContainer.classList.contains("flex-row")){  
    videoCanvasContainer.classList.remove("flex-row");  
    videoCanvasContainer.classList.remove("flex-wrap");  
    videoCanvasContainer.classList.add("flex-row-reverse");  
    videoCanvasContainer.classList.add("flex-wrap-reverse");  
  } else{  
    videoCanvasContainer.classList.remove("flex-row-reverse");  
    videoCanvasContainer.classList.remove("flex-wrap-reverse");  
    videoCanvasContainer.classList.add("flex-row");  
    videoCanvasContainer.classList.add("flex-wrap");  
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Managing State for the Application
&lt;/h2&gt;

&lt;p&gt;The JavaScript for this application can be found in a single file: &lt;em&gt;script.js&lt;/em&gt;. The application is both data and user-driven. User actions result in system changes maintained in a JavaScript object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const data = {  
  videoEl: null,  
  canvasEl: null,  
  fileData: null,  
  currentStream: null,  
  constraints: {},  
  selectedDevice: null,  
  options: \[\],  
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Navigator
&lt;/h2&gt;

&lt;p&gt;The Navigator interface is accessible from the Windows object. The Navigator gets information about the user request in the User Agent (UA) header. The UA represents the person making the request. The UA header is a string that provides information like this :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Mozilla/5.0 (Macintosh; Intel Mac OS X 10\_15\_7)   
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll use the Navigator to access the user's media stream. We can discover our users' video and audio devices from the media stream. For this camera application, we'll be interested in the video devices.&lt;/p&gt;

&lt;h3&gt;
  
  
  Permissions
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;Media Capture and Streams API,&lt;/strong&gt; which opens up the video stream in the video container, can query the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Permissions_API#permission-aware_apis" rel="noopener noreferrer"&gt;&lt;strong&gt;Permissions API&lt;/strong&gt;&lt;/a&gt; to determine if the user allows the application access to the video.&lt;/p&gt;

&lt;p&gt;The rule for prompting for permission when the &lt;code&gt;navigator.getUserMedia&lt;/code&gt; function is called varies among browsers. The image below shows the user permission request on Safari. The choice to allow access to the camera will remain in effect on Safari until there is a page refresh. On Chrome, the permission remains in effect between page loads.&lt;/p&gt;

&lt;h3&gt;
  
  
  Media Capture and Streams
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;Media Capture and Streams API&lt;/strong&gt; is part of the &lt;strong&gt;Web Real Time Communication API (WebRTC).&lt;/strong&gt; WebRTC  is used for user video and audio communication. We'll use just the video capture to provide a stream of video frames from which we can create a &lt;strong&gt;Snapshot&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;Start&lt;/strong&gt; function, we call a &lt;code&gt;getDevices()&lt;/code&gt; function. This function aims to discover the devices available and load them in the &lt;strong&gt;Video Device&lt;/strong&gt; Select element.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function getDevices() {  
  // does the browser support media devices and enumerateDevices?  
  if (!navigator.mediaDevices || !navigator.mediaDevices.enumerateDevices) {  
    console.log("enumerated devices not supported");  
    return false;  
  }  

  // call getUserMedia to trigger check and prompt for permissions  
  await navigator.mediaDevices.getUserMedia({ video: true });  

  // get the list of video devices  
  let allDevices = await navigator.mediaDevices.enumerateDevices();  
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Initial Selected Device
&lt;/h3&gt;

&lt;p&gt;Once the devices are loaded, we set them &lt;code&gt;selectedDevice&lt;/code&gt; to the first, and sometimes only, device in the list. Recall that the desktop video is not back-facing.&lt;/p&gt;

&lt;p&gt;Next, we place constraints for the video stream we will render. The &lt;code&gt;setConstraints&lt;/code&gt; function will be called when the user first clicks &lt;strong&gt;Start&lt;/strong&gt; and later when they select different devices if multiple devices are available.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function setConstraints() {  
  const videoConstraints = {};  
  videoConstraints.deviceId = {  
    exact: data.selectedDevice,  
  };  

  data.constraints = {  
    video: videoConstraints,  
    audio: false,  
  };  
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the constraints are set, the &lt;code&gt;getMedia&lt;/code&gt; function is called to start streaming media from the device's camera. The stream &lt;code&gt;window.stream&lt;/code&gt; is bound to the video element.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function getMedia() {  
  try {  
    data.stream = await navigator.mediaDevices.getUserMedia(data.constraints);  
    window.stream = data.stream;  
    data.currentStream = window.stream;  
    data.videoEl.srcObject = window.stream;  
    return true;  
  } catch (err) {  
    throw err;  
  }  
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the video streaming, the &lt;strong&gt;Start&lt;/strong&gt; button is disabled, and the &lt;strong&gt;Snapshot&lt;/strong&gt; and &lt;strong&gt;Stop&lt;/strong&gt; buttons are enabled.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (resultMedia) {  
    disableBtn("camera");  
    enableBtn("stop");  
    enableBtn("snapshot");  
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Device Change
&lt;/h3&gt;

&lt;p&gt;If the user has the option to change the selected device, the &lt;code&gt;deviceOptionChange&lt;/code&gt; function calls &lt;code&gt;deviceChange&lt;/code&gt; to stop the camera, close the canvas container, and then call &lt;code&gt;getMedia&lt;/code&gt; to reopen the camera using the newly established device.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;async function deviceChange() {  
  stopVideoAndCanvas();  
  setConstraints();  
  const result = await getMedia();  
  console.log("device change:", result);  
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Calling GetMedia Twice
&lt;/h3&gt;

&lt;p&gt;You may have noticed that the GetMedia function is called twice when the user presses Start. To access devices from the Navigator, the user must get permission. The function call to getUserMedia triggers the permission prompt*&lt;em&gt;:&lt;/em&gt;*&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; await navigator.mediaDevices.getUserMedia({ video: true });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We want to get the list of devices before loading the video, so the first call to &lt;code&gt;getUserMedia&lt;/code&gt; takes care of access to the devices. We use the selected device to set the constraints that provide the video stream. The second call opens the video stream and assigns it to our data state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;data.stream = await navigator.mediaDevices.getUserMedia(data.constraints);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Snapshot
&lt;/h2&gt;

&lt;p&gt;We'll use the &lt;strong&gt;Canvas API&lt;/strong&gt; and element to create a snapshot. The Snapshot is made from the video stream frame visible when the &lt;strong&gt;Snapshot&lt;/strong&gt; button is pressed.&lt;/p&gt;

&lt;p&gt;When the user clicks &lt;strong&gt;Snapshot,&lt;/strong&gt; we show the canvas container and the &lt;strong&gt;Flip&lt;/strong&gt; button. Clicking on the &lt;strong&gt;Flip&lt;/strong&gt; button when using a mobile device makes it easier to see what was captured from the video stream.&lt;/p&gt;

&lt;p&gt;The canvas HTML element is assigned a width and height equal to the dimensions of the video element. We then use the &lt;strong&gt;Canvas API&lt;/strong&gt; to draw a 2-dimensional image from the video element onto the canvas element. The signature we use passes the &lt;strong&gt;video&lt;/strong&gt; element (image), &lt;strong&gt;dx&lt;/strong&gt; (horizontal distance of the image from the left side of the canvas container), &lt;strong&gt;dy&lt;/strong&gt; (vertical) distance of the image from the top of the canvas container, the &lt;strong&gt;width&lt;/strong&gt; of the container, and the &lt;strong&gt;height&lt;/strong&gt; of the container.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;drawImage(image, dx, dy, dWidth, dHeight)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Finally, we use the &lt;code&gt;toDataURL&lt;/code&gt; to capture the data that makes up the frame from the video. Looking at the string created, we can see the type and then base64 values that represent the image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;data:image/jpeg;base64,/9j/4AAQSk...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function snapShot() {  
  show("canvas-container");  
  show("flip-button");  
  data.canvasEl.width = data.videoEl.videoWidth;  
  data.canvasEl.height = data.videoEl.videoHeight;  
  data.canvasEl  
    .getContext("2d")  
    .drawImage(data.videoEl, 0, 0, data.canvasEl.width, data.canvasEl.height);  
  data.fileData = data.canvasEl.toDataURL("image/jpeg");  
  let hiddenLinks = document.querySelectorAll(".hidden\_links");  
  for (let hiddenLink of hiddenLinks) {  
    document.querySelector("body").remove(hiddenLink);  
  }  
  enableBtn("download");  
  enableBtn("share");  
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Download
&lt;/h2&gt;

&lt;p&gt;We're using a hidden link to download the image. This technique involves creating a hidden anchor tag with a download attribute and programmatically clicking it. We start by removing any previously hidden links.&lt;/p&gt;

&lt;p&gt;Then, we use the &lt;strong&gt;DOM API&lt;/strong&gt; to add a new anchor element to the Document. We set the hypertext reference &lt;code&gt;href&lt;/code&gt;to the data from the image and the &lt;code&gt;textContent&lt;/code&gt; to &lt;code&gt;none&lt;/code&gt; . We set the&lt;code&gt;target&lt;/code&gt; to &lt;code&gt;_blank&lt;/code&gt; , an unnamed window, because we are not navigating anywhere. We sent the download element to a filename. The download attribute tells the browser to respond to a click event by downloading the data in the &lt;code&gt;href&lt;/code&gt; . By calling the anchor tag's click event, we trigger the download of the image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;function download() {  
  // cleanup any existing hidden links  
  let hiddenLinks = document.querySelectorAll(".hidden\_links");  
  for (let hiddenLink of hiddenLinks) {  
    document.querySelector("body").remove(hiddenLink);  
  }  

  if (data.fileData) {  
    let a = document.createElement("a");  
    a.classList.add("hidden-link");  
    a.href = data.fileData;  
    a.textContent = "";  
    a.target = "\_blank";  
    a.download = "photo.jpeg";  
    document.querySelector("body").append(a);  
    a.click();  
  }  
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Web Share
&lt;/h2&gt;

&lt;p&gt;The &lt;strong&gt;Share API&lt;/strong&gt; allows web applications to share images and text. This API doesn't work in Chrome Desktop but does work in Safari, Edge, and Android Chrome. You can see in the image below, taken from an instance of Safari on a Mac, that it will allow you to share the Snapshot to Mail, Message, AirDrop, Notes, Photos, Reminders, and Simulator. This is an instance where caniuse.com can help plan the feature set for an application.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next?
&lt;/h2&gt;

&lt;p&gt;New features, JavaScript frameworks, and enhanced styling will help to build a better camera application. While the processing of composing an application using Web APIs can be an end in itself, it's also worth noting that this application is waiting for a step up.&lt;/p&gt;

&lt;h3&gt;
  
  
  Modifying the Image
&lt;/h3&gt;

&lt;p&gt;Offer options to modify the image drawn into the canvas element. This &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement/toDataURL#example_dynamically_change_images" rel="noopener noreferrer"&gt;example&lt;/a&gt; shows you how to convert the image to grayscale.&lt;/p&gt;

&lt;h3&gt;
  
  
  Modifying Exif Data
&lt;/h3&gt;

&lt;p&gt;Use the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API" rel="noopener noreferrer"&gt;Geolocation API&lt;/a&gt; to get the latitude and longitude of the data. Then, a library like exif-js adds this data to the image before downloading or sharing it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Port the Code
&lt;/h3&gt;

&lt;p&gt;Port the code from this camera application to React, Angular, or Vue. This is always a good exercise and will result in a better understanding of the state and logic.&lt;/p&gt;

&lt;h3&gt;
  
  
  Style
&lt;/h3&gt;

&lt;p&gt;Look for ways to improve the layout and usability of this application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Take Some Pictures
&lt;/h3&gt;

&lt;p&gt;Use the camera you create. Find out if there are any features you would like to add and add them!&lt;/p&gt;

</description>
      <category>webapis</category>
      <category>canvas</category>
      <category>mediacaptureandstreams</category>
      <category>camera</category>
    </item>
    <item>
      <title>React 18 + React Router v6 + Sidebar Navigation and a Sandpack Component</title>
      <dc:creator>Rebeccca Peltz</dc:creator>
      <pubDate>Sun, 29 Jan 2023 21:12:24 +0000</pubDate>
      <link>https://dev.to/rebeccapeltz/react-18-react-router-v6-sidebar-navigation-and-a-sandpack-component-5c02</link>
      <guid>https://dev.to/rebeccapeltz/react-18-react-router-v6-sidebar-navigation-and-a-sandpack-component-5c02</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/cloudinary-training/cld-intro-react-sdk-training-tool" rel="noopener noreferrer"&gt;code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://cloudinary-training.github.io/cld-intro-react-sdk-training-tool/#/url-gen" rel="noopener noreferrer"&gt;demo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I take advantage of the ease of not having to set up a router for React by using &lt;a href="https://nextjs.org/docs/getting-started" rel="noopener noreferrer"&gt;Next.js&lt;/a&gt;.  Sometimes, Next.js is not the appropriate framework because mixing SSR with components that contain frames or otherwise interact with the window object causes a lot of extra steps. That’s when I turned in a different direction and installed React with the React Router.&lt;/p&gt;

&lt;p&gt;I needed to set up a React application with routing and a component that accesses a frame.  My project uses Sandpack, which creates a frame element connected to codesandbox.io.    I dove into the current documentation on React 18 and how to integrate the v6 React Router with it.  It was confusing, so I want to share what I ultimately came up with.  I’m also using tailwind because I like this sidebar for Next.js, and it uses tailwind.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create React App
&lt;/h2&gt;

&lt;p&gt;You’ll start by running &lt;code&gt;npx create-react-app test-app&lt;/code&gt;.   Look at your &lt;/p&gt;

&lt;p&gt;src/index.js.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;ReactDOM&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-dom/client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./index.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./App&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;reportWebVitals&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./reportWebVitals&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ReactDOM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createRoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
 &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;StrictMode&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
 &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/React.StrictMode&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// If you want to start measuring performance in your app, pass a function&lt;/span&gt;
&lt;span class="c1"&gt;// to log results (for example: reportWebVitals(console.log))&lt;/span&gt;
&lt;span class="c1"&gt;// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals&lt;/span&gt;
&lt;span class="nf"&gt;reportWebVitals&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
. &lt;/p&gt;

&lt;p&gt;Replace the code above with the code below&lt;/p&gt;

&lt;p&gt;index.js.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// import ReactDOM from 'react-dom';&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./index.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./App&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// );&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createRoot&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-dom/client&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;container&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;root&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createRoot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;container&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// createRoot(container!) if you use TypeScript&lt;/span&gt;
&lt;span class="nx"&gt;root&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your App.js should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;logo&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./logo.svg&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./App.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;


&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;App&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;App-header&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
       &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;img&lt;/span&gt; &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;logo&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;App-logo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;alt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;logo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
       &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
         &lt;span class="nx"&gt;Edit&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;code&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/code&amp;gt; and save to reload&lt;/span&gt;&lt;span class="err"&gt;.
&lt;/span&gt;       &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;       &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;
         &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;App-link&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
         &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://reactjs.org&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
         &lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;_blank&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
         &lt;span class="nx"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;noopener noreferrer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
       &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
         &lt;span class="nx"&gt;Learn&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;
       &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/a&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/header&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;   &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt; &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We’ll also be replacing the code in App.js, but first, let’s decide on some routes and install the React Router: react-router-dom.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install and Configure Router
&lt;/h2&gt;

&lt;p&gt;For simplicity, we’ll create the three routes we are familiar with: Home, About, and Contact.  These represent three pages in our single-page app.&lt;/p&gt;

&lt;p&gt;These pages can be created by just adding src/Home.js, src/About.js, and src/Contact.js.  They all have the same code other than self-identifying text. Repeat what you see for About.js and Contact.js and change the H1 element text to identify as “About” or “Contact”&lt;/p&gt;

&lt;p&gt;.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
       &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;This&lt;/span&gt; &lt;span class="nx"&gt;is&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;home&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;   &lt;span class="p"&gt;);&lt;/span&gt;
 &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Home&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we have three routes.  The Home page will be accessed as “/”,  the About page will be accessed as “/about”, and the Contact page will be accessed as “/contact”.  With this mapping understood, we can create our Route configuration and Sidebar navigation.&lt;/p&gt;

&lt;p&gt;33 Install react-router-dom&lt;/p&gt;

&lt;p&gt;Start by adding the router to your project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;react-router-dom
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now replace the code in your App.js with this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Routes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Route&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;BrowserRouter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-router-dom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Home&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./Home&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;About&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./About&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Contact&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./Contact&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Layout&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./components/Layout&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;App&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
   &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;App&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;BrowserRouter&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
       &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Routes&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
         &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Layout&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
           &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Home&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;           &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;about&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;About&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;           &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Route&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;contact&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Contact&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;         &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Route&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;       &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Routes&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;     &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/BrowserRouter&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;   &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt; &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;App&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
. &lt;/p&gt;

&lt;p&gt;Notice that we wrap the individual Routes in a singular Route component.  Then we wrap the Route component in a BrowserRouter component.   For the Home page route, we use “/” to indicate the path.  For the non-root paths, we just use the path's name without a “/”.&lt;br&gt;
This seems simple now, but it took me a lot of trial and error until I came across this blog which helped a lot.&lt;/p&gt;

&lt;p&gt;To build on what the blogger helped me with, let me show you how to add sidebar navigation and Sandpack, which is helpful if you want to manage code sandboxes on your website.&lt;br&gt;
Create Sidebar Navigation&lt;br&gt;
As mentioned above, this Sidebar component is adapted from a blogger’s Next.js sidebar.  It uses React Router, which means you will need to use the &lt;code&gt;&amp;lt;Outlet /&amp;gt;&lt;/code&gt; component to provide a place for your page to render as you navigate through the sidebar.  Read more about the Outlet component here.&lt;/p&gt;

&lt;p&gt;The Layout component that container sidebar navigation relies on tailwind.  Here are the steps to getting tailwind installed.&lt;/p&gt;
&lt;h2&gt;
  
  
  Tailwind Install
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;npm install -D tailwindcss&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Create the config files which include tailwind.config.js and postcss.config.js.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npx tailwindcss init -p&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;These file should look like this:&lt;/p&gt;

&lt;p&gt;tailwind.config.js&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="cm"&gt;/** @type {import('tailwindcss').Config} */&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./src/**/*.{js,jsx,ts,tsx}&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./public/index.html&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="na"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
 &lt;span class="p"&gt;},&lt;/span&gt;
 &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;postcss&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;js&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
 &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="na"&gt;tailwindcss&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
   &lt;span class="na"&gt;autoprefixer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt;
 &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then add&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;base&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;components&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="n"&gt;utilities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;to your src/index.css file.&lt;/p&gt;

&lt;p&gt;If you’re using Visual Studio Code and it flags the entries above, you can get the PostCSS Language Support extension to clear that up.&lt;/p&gt;

&lt;p&gt;Now we’re ready to write the code for the Layout component.&lt;/p&gt;

&lt;p&gt;Layout with Sidebar Navigation and Outlet “Placeholder”&lt;/p&gt;

&lt;p&gt;Here’s the code I used to create the Layout component that includes NavLinks and Outlet from the React router.&lt;/p&gt;

&lt;p&gt;It’s helpful to create an array of data about your routes and use the JavaScript &lt;code&gt;map&lt;/code&gt; command to create the navigation code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NavLink&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Outlet&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react-router-dom&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Layout&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;testMenuItems&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
       &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Introduction&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="p"&gt;},&lt;/span&gt;
       &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;about&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;About&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="p"&gt;},&lt;/span&gt;
       &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="na"&gt;href&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;contact&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Contact&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;];&lt;/span&gt;

   &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;NavLink&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Home&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/NavLink&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;min-h-screen flex flex-col&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
           &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bg-gray-200 text-black sticky top-0 h-14 flex justify-center items-center font-semibold uppercase&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
               &lt;span class="nx"&gt;Cloudinary&lt;/span&gt; &lt;span class="nx"&gt;Actions&lt;/span&gt;
           &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/header&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;           &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;flex flex-col md:flex-row flex-1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
               &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;aside&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bg-gray-100 w-full md:w-60&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                   &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;nav&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                       &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ul&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                           &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;testMenuItems&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt; &lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
                               &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;li&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;m-2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                                   &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;NavLink&lt;/span&gt; &lt;span class="nx"&gt;to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;href&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                                       &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;text-black&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                                   &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/NavLink&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                               &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/li&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                           &lt;span class="p"&gt;))}&lt;/span&gt;
                       &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/ul&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;                   &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/nav&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;               &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/aside&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;               &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;main&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;flex-1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
                   &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Outlet&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
               &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/main&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;           &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;       &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;   &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Add a Sandpack Component
&lt;/h2&gt;

&lt;p&gt;Now, the whole reason I got into adding a router is to use React with routing on pages that contain the Sandbag component provided by codesandbox.io.  I want to host my own code sandboxes.  I’m so pleased that codesandbox.io is making this possible.&lt;/p&gt;

&lt;p&gt;Here’s a sample Sandpack component.  It produces two code sandboxes on a web page.  These sandboxes can be modified, and the code can be run by the user.  I’m a curriculum developer at Cloudinary, so they are used to create image elements.  Notice that you don’t need to install the Cloudinary library to have Sandbag run Cloudinary code because the codesandbox.io  frame will take care of that.  There’s a lot of code in the component below because I’ve set up two sandboxes for this page, and I’m trying out Sandbag options.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm install @cloudinary/url-gen&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;CldSample Component&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../App.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Sandpack&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@codesandbox/sandpack-react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cloudinaryAdvancedImage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
import {AdvancedImage} from '@cloudinary/react';
import {Cloudinary} from "@cloudinary/url-gen";
import {Transformation} from "@cloudinary/url-gen";
export default function App() {
 const cld = new Cloudinary({
   cloud: {
     cloudName: 'demo'
   }
 });
 const myImage = cld.image('front_face');
 // return &amp;lt;h1&amp;gt;Hello Sandpack&amp;lt;/h1&amp;gt;
 return (
   &amp;lt;div&amp;gt;
     &amp;lt;AdvancedImage cldImg={myImage} /&amp;gt;
   &amp;lt;/div&amp;gt;
 )
}`&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cloudinaryAdvancedImageWithTransformations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
import {AdvancedImage} from '@cloudinary/react';
import {Cloudinary} from "@cloudinary/url-gen";
import {Transformation} from "@cloudinary/url-gen";

// Import required actions.
import {thumbnail, scale} from "@cloudinary/url-gen/actions/resize";
import {byRadius} from "@cloudinary/url-gen/actions/roundCorners";
import {sepia} from "@cloudinary/url-gen/actions/effect";
import {source} from "@cloudinary/url-gen/actions/overlay";
import {opacity,brightness} from "@cloudinary/url-gen/actions/adjust";
import {byAngle} from "@cloudinary/url-gen/actions/rotate"

// Import required qualifiers.
import {image} from "@cloudinary/url-gen/qualifiers/source";
import {Position} from "@cloudinary/url-gen/qualifiers/position";
import {compass} from "@cloudinary/url-gen/qualifiers/gravity";
import {focusOn} from "@cloudinary/url-gen/qualifiers/gravity";
import {FocusOn} from "@cloudinary/url-gen/qualifiers/focusOn";
export default function App() {
 const cld = new Cloudinary({
   cloud: {
     cloudName: 'demo'
   }
 });
 const myImage = cld.image('front_face');
 // Apply the transformation.
 myImage
 .resize(thumbnail().width(150).height(150).gravity(focusOn(FocusOn.face())))  // Crop the image.
 .roundCorners(byRadius(20))    // Round the corners.
 .effect(sepia())  // Apply a sepia effect.
 .overlay(   // Overlay the Cloudinary logo.
   source(
     image('cloudinary_icon_blue')
       .transformation(new Transformation()
       .resize(scale(50)) // Resize the logo.
         .adjust(opacity(60))  // Adjust the opacity of the logo.
       .adjust(brightness(200)))  // Adjust the brightness of the logo.      
   )
   .position(new Position().gravity(compass('south_east')).offsetX(5).offsetY(5))   // Position the logo.
 )
 .rotate(byAngle(10))  // Rotate the result.
 .format('png');   // Deliver as PNG.
 return (
   &amp;lt;div&amp;gt;
     &amp;lt;AdvancedImage cldImg={myImage} /&amp;gt;
   &amp;lt;/div&amp;gt;
 )
};`&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;CldSample&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
       &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;code-container&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
           &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h1&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Cloudinary&lt;/span&gt; &lt;span class="nx"&gt;Transformations&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h1&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;           &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Render&lt;/span&gt; &lt;span class="nx"&gt;an&lt;/span&gt; &lt;span class="nx"&gt;Image&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;Advanced&lt;/span&gt; &lt;span class="nx"&gt;Image&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h2&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;           &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Sandpack&lt;/span&gt;
               &lt;span class="c1"&gt;// You can change these examples!&lt;/span&gt;
               &lt;span class="c1"&gt;// Try uncommenting any of these lines&lt;/span&gt;
               &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
               &lt;span class="c1"&gt;// theme="light"&lt;/span&gt;
               &lt;span class="c1"&gt;// theme="auto"&lt;/span&gt;
               &lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
               &lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
                   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/App.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cloudinaryAdvancedImage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
               &lt;span class="p"&gt;}}&lt;/span&gt;
               &lt;span class="nx"&gt;customSetup&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
                   &lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                       &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@cloudinary/react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;^1.9.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                       &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@cloudinary/url-gen&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;^1.8.7&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                   &lt;span class="p"&gt;},&lt;/span&gt;
               &lt;span class="p"&gt;}}&lt;/span&gt;
               &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
                   &lt;span class="na"&gt;showNavigator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                   &lt;span class="na"&gt;showTabs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                   &lt;span class="na"&gt;showLineNumbers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// default - true&lt;/span&gt;
                   &lt;span class="na"&gt;showInlineErrors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// default - false&lt;/span&gt;
                   &lt;span class="na"&gt;wrapContent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// default - false&lt;/span&gt;
                   &lt;span class="na"&gt;editorHeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;350&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// default - 300&lt;/span&gt;
                   &lt;span class="na"&gt;editorWidthPercentage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// default - 50&lt;/span&gt;
                   &lt;span class="na"&gt;autorun&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                   &lt;span class="na"&gt;recompileMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;delayed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;//default is immediate&lt;/span&gt;
                   &lt;span class="na"&gt;recompileDelay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                   &lt;span class="na"&gt;resizablePanels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;//default&lt;/span&gt;
               &lt;span class="p"&gt;}}&lt;/span&gt;
           &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;           &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Create&lt;/span&gt; &lt;span class="nx"&gt;Transformations&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h2&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;           &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Sandpack&lt;/span&gt;
               &lt;span class="c1"&gt;// You can change these examples!&lt;/span&gt;
               &lt;span class="c1"&gt;// Try uncommenting any of these lines&lt;/span&gt;
               &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
               &lt;span class="c1"&gt;// theme="light"&lt;/span&gt;
               &lt;span class="c1"&gt;// theme="auto"&lt;/span&gt;
               &lt;span class="nx"&gt;template&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
               &lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
                   &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/App.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cloudinaryAdvancedImageWithTransformations&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
               &lt;span class="p"&gt;}}&lt;/span&gt;
               &lt;span class="nx"&gt;customSetup&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
                   &lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                       &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@cloudinary/react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;^1.9.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                       &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@cloudinary/url-gen&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;^1.8.7&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                   &lt;span class="p"&gt;},&lt;/span&gt;
               &lt;span class="p"&gt;}}&lt;/span&gt;
               &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
                   &lt;span class="na"&gt;showNavigator&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                   &lt;span class="na"&gt;showTabs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                   &lt;span class="na"&gt;showLineNumbers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// default - true&lt;/span&gt;
                   &lt;span class="na"&gt;showInlineErrors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// default - false&lt;/span&gt;
                   &lt;span class="na"&gt;wrapContent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// default - false&lt;/span&gt;
                   &lt;span class="na"&gt;editorHeight&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;350&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// default - 300&lt;/span&gt;
                   &lt;span class="na"&gt;editorWidthPercentage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// default - 50&lt;/span&gt;
                   &lt;span class="na"&gt;autorun&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                   &lt;span class="na"&gt;recompileMode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;delayed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;//default is immediate&lt;/span&gt;
                   &lt;span class="na"&gt;recompileDelay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                   &lt;span class="na"&gt;resizablePanels&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;//default&lt;/span&gt;
               &lt;span class="p"&gt;}}&lt;/span&gt;
           &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;       &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;   &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is what your page will look like if you get this running.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3z9q6i7l0asapvywkf1l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3z9q6i7l0asapvywkf1l.png" alt="Project File System Structure" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I hope this helps anyone working with React 18 and the React Router&lt;/p&gt;

&lt;p&gt;Project Structure&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs17jpijy6zhnamuo99tp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs17jpijy6zhnamuo99tp.png" alt="Demo Web Page" width="800" height="367"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Webhook-Driven Email Notifications for Media Management</title>
      <dc:creator>Rebeccca Peltz</dc:creator>
      <pubDate>Sat, 12 Mar 2022 18:08:50 +0000</pubDate>
      <link>https://dev.to/rebeccapeltz/webhook-driven-email-notifications-for-media-management-e80</link>
      <guid>https://dev.to/rebeccapeltz/webhook-driven-email-notifications-for-media-management-e80</guid>
      <description>&lt;p&gt;Building low-code flows with APIs is steadily gaining popularity, and &lt;a href="https://www.gartner.com/reviews/market/enterprise-low-code-application-platform" rel="noopener noreferrer"&gt;related application platforms&lt;/a&gt; abound. Developers would find it a plus to gain the expertise of using low-code to implement webhooks, that is, APIs that effectively “hook” features together in different systems.&lt;/p&gt;

&lt;p&gt;This article shows you how to build a webhook API and deploy it in a Netlify Function. The webhook API is called by the  Cloudinary Upload API when a long-running process completes. The webhook API forwards the response via email to designated parties. Specifically, you run a script—an asynchronous process—to apply the transformations in preparation for the deployment of the video with adaptive bitrate (ABR) streaming. To enable you and other parties concerned to know when the process completes, the webhook API notifies you by email of the status—along with the URLs of the derived video files. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Problem:&lt;/strong&gt; We Need to Get Notified When a Process Completes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Design&lt;/strong&gt; a Webhook API for Media Processing With the &lt;a href="https://sendgrid.com/solutions/email-api/" rel="noopener noreferrer"&gt;SendGrid API&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code and Deploy&lt;/strong&gt; a Webhook API With Node.js and &lt;a href="https://www.netlify.com/products/functions" rel="noopener noreferrer"&gt;Netlify Functions&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Test&lt;/strong&gt; the Upload Script&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secure&lt;/strong&gt; the Media-Processing Webhook&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Problem: We Need to Get Notified When a Process Completes
&lt;/h2&gt;

&lt;p&gt;Video processing can take time to complete. We run a script to create the transformations for serving up a video with Adaptive Bitrate Streaming (ABR)—a process that runs asynchronously. After executing the script, we get a response from Cloudinary that the transformations are proceeding. Ideally, add a notification URL to the script so that we can tackle other tasks during the process, assured that we'll be notified when the process is complete and the video is deployment ready.&lt;/p&gt;

&lt;p&gt;Let’s code a webhook API and deploy it in a Netlify function. That API receives the upload response and emails it to the parties in a recipient list.&lt;/p&gt;

&lt;h2&gt;
  
  
  Design a Webhook API With the SendGrid API
&lt;/h2&gt;

&lt;p&gt;We can find out if a long-running asynchronous process has completed by means of webhooks. This flowchart illustrates the automated steps:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxzzxgu73txfk2xvn0hc7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxzzxgu73txfk2xvn0hc7.png" alt="Email flow" width="800" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here’s a more detailed rundown:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;We execute a script that calls the Cloudinary Upload API and that applies eager transformations, which generate derived video files. The script also specifies the &lt;code&gt;eager_async&lt;/code&gt; option. Since the upload process runs asynchronously, we need not wait for it to complete. Finally, we specify in the script a notification URL, i.e., a webhook that forwards the response in an email. Thus, when eager processing is complete, the response posts to the URL.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Asynchronous processing creates the manifest files and the HLS video-fragment files required for ABR streaming. Below is the code built with Cloudinary’s Node.js SDK for asynchronous processing. If you want to run this script, you would replace the &lt;code&gt;&amp;lt;WEBHOOK&amp;gt;&lt;/code&gt; with a URL.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;cloudinary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;uploader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://res.cloudinary.com/cloudinary-training/video/upload/v1626130641/mountain.mov&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="na"&gt;resource_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;video&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;upload&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;eager&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
         &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;streaming_profile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;full_hd&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;m3u8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
         &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mp4&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
           &lt;span class="na"&gt;transformation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;quality&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
         &lt;span class="p"&gt;},&lt;/span&gt;
       &lt;span class="p"&gt;],&lt;/span&gt;
       &lt;span class="na"&gt;eager_async&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="na"&gt;eager_notification_url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;WEBHOOK&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
 &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When processing is complete, Cloudinary posts the response to the webhook API. The response contains the URLs of the derived files. A sample response that we would see in the email is shown below&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;notification_type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;eager&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;timestamp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2022-03-12T17:41:22+00:00&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;request_id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;c8450346e4d31ff9b3b7b4e169aecb26&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;eager&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;transformation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;sp_full_hd/m3u8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bytes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1007&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;format&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;m3u8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;url&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://res.cloudinary.com/ac-self-service/video/upload/sp_full_hd/v1647106802/e7osszr9rn0zx43vuysm.m3u8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;secure_url&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://res.cloudinary.com/ac-self-service/video/upload/sp_full_hd/v1647106802/e7osszr9rn0zx43vuysm.m3u8&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;transformation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;q_auto/mp4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;width&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1280&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;height&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;720&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;bytes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8807563&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;format&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;mp4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;url&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://res.cloudinary.com/ac-self-service/video/upload/q_auto/v1647106802/e7osszr9rn0zx43vuysm.mp4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;secure_url&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://res.cloudinary.com/ac-self-service/video/upload/q_auto/v1647106802/e7osszr9rn0zx43vuysm.mp4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;batch_id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;6f006cbe147ac3ff22bf610e4e3f7482b5f3201625c5c87ccb1fdf2bb105e4a3&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;asset_id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;69745e7be77ffdc2c9ad14491074d58d&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;public_id&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;e7osszr9rn0zx43vuysm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The &lt;a href="https://github.com/rebeccapeltz/cld-webhooks/blob/main/functions/webhook_notify_email.js" rel="noopener noreferrer"&gt; notification webhook API&lt;/a&gt; emails the information in the response to the designated parties through an API of SendGrid, a cloud service for email.  See below for the code of the webhook API, which is structured as an asynchronous handler, a common pattern for low-code server functions. Note that clickTracking is disabled so that the parties notified can browse those URLs in the email.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;function &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;sgMail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setApiKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SENDGRID_API_KEY&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;sgMail&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sendMultiple&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TO_RECIPIENTS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;FROM_VERIFIED_SENDER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Webhook Notification&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="na"&gt;trackingSettings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;clickTracking&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;enable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}});&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;statusCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The email recipients proof and deploy the video.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Code and Deploy the Webhook API With Node.js and Netlify Functions
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/cloudinary-training/cld-webhooks" rel="noopener noreferrer"&gt;Code repository&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that the design of the automated flow is in place, code and deploy it. Perform the two steps below to build a webhook API that notifies designated parties of the Cloudinary Upload API’s eager response by email:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Create two accounts, one for &lt;a href="https://sendgrid.com/" rel="noopener noreferrer"&gt;SendGrid&lt;/a&gt; and the other for &lt;a href="https://www.netlify.com/" rel="noopener noreferrer"&gt;Netlify&lt;/a&gt;, and then deploy this &lt;a href="https://github.com/cloudinary-training/cld-webhooks/blob/main/functions/webhook_notify_email.js" rel="noopener noreferrer"&gt;code&lt;/a&gt; as a Netlify Function.  &lt;a href="https://github.com/rebeccapeltz/cld-webhooks/blob/main/NETLIFY_DEPLOY.md" rel="noopener noreferrer"&gt;Detailed setup instructions&lt;/a&gt; will help you with deployment.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Upload a video by running the Node.js script &lt;a href="https://github.com/cloudinary-training/cld-webhooks/blob/main/use-cases/notification/upload-video-eager-abr.js" rel="noopener noreferrer"&gt;upload-video-eager-abr&lt;/a&gt;, which creates manifests and video “chunks” that enable ABR streaming with Cloudinary transformations bundled in a profile, in which—&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;eager_async&lt;/code&gt; option causes Cloudinary to process the video asynchronously.&lt;/li&gt;
&lt;li&gt;The URL assigned to &lt;code&gt;eager_async_notification_url&lt;/code&gt; is the webhook API that we created in Netlify.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Afterward, Cloudinary posts the eager response, which reports the success or failure of the transformation process and lists the URLs of the derived assets, to the webhook API. The API then sends all that information to the designated parties by email.&lt;/p&gt;

&lt;h2&gt;
  
  
  Test the Upload Script
&lt;/h2&gt;

&lt;p&gt;To test the &lt;a href="https://github.com/cloudinary-training/cld-webhooks/blob/main/use-cases/notification/upload-video-eager-abr.js" rel="noopener noreferrer"&gt;upload-video-eager-abr&lt;/a&gt; script, go to &lt;a href="https://webhook.site" rel="noopener noreferrer"&gt;https://webhook.site&lt;/a&gt; to obtain a free URL on which to view the eager response. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tip:&lt;/strong&gt; Start by experimenting with webhook.site’s tracking page. Once we’ve verified that the upload process is posting correctly there, proceed with the steps of having our webhook API send email notifications with the details in the eager response. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fafm20ru6soawmaalu67r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fafm20ru6soawmaalu67r.png" alt="webhook.site" width="800" height="421"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;h2&gt;
  
  
  Secure the Media Processing Webhook
&lt;/h2&gt;

&lt;p&gt;Be sure to guard the webhook API against unauthorized access. An ideal way to do that is by leveraging Cloudinary features like signatures and timestamps, as well as a Cloudinary utility that ensures that the API is called by Cloudinary only for legitimate purposes. &lt;/p&gt;

&lt;p&gt;For details, see the Cloudinary documentation on &lt;a href="https://cloudinary.com/documentation/notifications#verifying_notification_signatures" rel="noopener noreferrer"&gt;verifying notification signatures&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automate Low-Code Flows With Webhooks.
&lt;/h2&gt;

&lt;p&gt;The above example shows you how to write low-code and package it in a simple and modular webhook API that receives a response from Cloudinary. To facilitate tracking, that API notifies you and other interested parties of the status of the process through SendGrid (for dispatching email) and Netlify (for serving APIs).&lt;br&gt;
Webhooks are a handy starting point for automated low-code flows, which are hosted by many popular services. For more details on building similar flows for media, check out MediaFlows, a product of Cloudinary Labs. &lt;/p&gt;

</description>
      <category>webhook</category>
      <category>cloudinary</category>
      <category>sendgrid</category>
      <category>netlify</category>
    </item>
    <item>
      <title>Transforming Media With Java Code</title>
      <dc:creator>Rebeccca Peltz</dc:creator>
      <pubDate>Fri, 07 May 2021 16:45:38 +0000</pubDate>
      <link>https://dev.to/rebeccapeltz/transforming-media-with-java-code-4863</link>
      <guid>https://dev.to/rebeccapeltz/transforming-media-with-java-code-4863</guid>
      <description>&lt;h1&gt;
  
  
  Transforming Media With Java
&lt;/h1&gt;

&lt;p&gt;Transforming media on Cloudinary in Java is fun because of the fluent interface. Cloudinary transformations are used to produce optimized media as well as aesthetic effects.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fluent Interface
&lt;/h2&gt;

&lt;p&gt;In 2005, Martin Fowler created an API design pattern called the &lt;a href="https://martinfowler.com/bliki/FluentInterface.html" rel="noopener noreferrer"&gt;fluent interface, which makes use of &lt;/a&gt;method chaining and a domain-specific language and&lt;a href="https://martinfowler.com/bliki/FluentInterface.html" rel="noopener noreferrer"&gt; which has been adopted &lt;/a&gt;in many popular APIs, such as .NET’s SQL API &lt;a href="https://docs.microsoft.com/en-us/dotnet/standard/linq/" rel="noopener noreferrer"&gt;Linq. &lt;/a&gt;With Linq, .NET programmers can write code that generates SQL.&lt;/p&gt;

&lt;p&gt;Since then, Fowler has introduced many &lt;a href="https://martinfowler.com/eaaCatalog/" rel="noopener noreferrer"&gt;design patterns&lt;/a&gt; for object-oriented programming that have been embraced by OO developers for building APIs. &lt;/p&gt;

&lt;h3&gt;
  
  
  Method Chaining
&lt;/h3&gt;

&lt;p&gt;Method chaining refers to a method that returns an instance of the object that encapsulates it,  as in this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Hello {
    private string name;
    Hello()
    {
        System.out.println("Calling The Constructor");
    }
    public Hello setName(String name)
    {
        this.name = name;
        return this;
    }
    void say()
    {
        System.out.println("Hello " + name);
    }
}

public class Hello {
    public static void main(String[] args)
    {
        // "method chaining".
        new Hello().setName("Martin").say();
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Fun fact:&lt;/strong&gt;  The popular jQuery library, which leverages method chaining, was developed around the time Martin Fowler launched the fluent interface.  &lt;/p&gt;

&lt;h3&gt;
  
  
  Domain-Specific Language
&lt;/h3&gt;

&lt;p&gt;Since the fluent interface comprises both object chaining and a domain-specific language, the code flows and is easy to read.  &lt;/p&gt;

&lt;h3&gt;
  
  
  Fluent Interface for Transformations
&lt;/h3&gt;

&lt;p&gt;We're going to see here how to use the Cloudinary Java SDK's fluent interface to code fun and useful transformations.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Cropping of Media
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Scale
&lt;/h3&gt;

&lt;p&gt;Cloudinary performs simple cropping transformations with a fluent interface. An example is to apply a single dimension to an image or video and then scale the media item to maintain the aspect ratio, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;new Transformation().width(300).crop("scale")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here’s an example of single-dimension scaling:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/cloudinary-training/image/upload/w_300,c_scale/dog.jpg" rel="noopener noreferrer"&gt;https://res.cloudinary.com/cloudinary-training/image/upload/w_300,c_scale/dog.jpg&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;tr&gt;
   &lt;td&gt;Original: 2519 x 2501
   &lt;/td&gt;
   &lt;td&gt;Scaled: 300 x 298
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;




&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--u4w_golY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/cloudinary-training/image/upload/f_auto%2Cq_auto/blog/java/dog1.jpg" alt="dog original photo" title="dog original photo" width="800" height="1005"&gt;

   &lt;/td&gt;
   &lt;td&gt;




&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--TElI_cPc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/cloudinary-training/image/upload/f_auto%2Cq_auto/blog/java/dog2.jpg" alt="dog scaled photo" title="dog scaled photo" width="800" height="696"&gt;

   &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The transformed image adjusts the unspecified dimension (in this case, the height) so that the aspect ratio stays the same.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fit
&lt;/h3&gt;

&lt;p&gt;To create a media item, say, an image, with a certain width and height, specify them and maintain the aspect ratio with the &lt;code&gt;crop&lt;/code&gt; type &lt;code&gt;fit&lt;/code&gt;. Cloudinary then changes the dimensions and maintains aspect ration, but the image remains within the bounding box based on the two specified dimensions. See the example below.&lt;br&gt;
dim&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;new Transformation().width(300).height(200).crop("fit")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;tr&gt;
   &lt;td&gt;

Fit: 201 x 200
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;



&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KMSJMxpy--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/cloudinary-training/image/upload/f_auto%2Cq_auto/blog/java/dog3.jpg" alt="resize with fit " title="resize with fit" width="800" height="587"&gt;

   &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The transformed image might not have the exact dimensions as specified but will not exceed them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pad
&lt;/h3&gt;

&lt;p&gt;You can set exact dimensions with the &lt;code&gt;crop&lt;/code&gt; type &lt;code&gt;pad&lt;/code&gt;. To maintain the aspect ratio, &lt;code&gt;pad&lt;/code&gt; scales the image to fit within the specified dimensions and creates a padding for any dimension that must be made smaller than the value specified. The color of the pad will default to white. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;new Transformation().width(300).height(200).crop("pad")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;tr&gt;
   &lt;td&gt;

Pad: 300 x 300
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;



&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PWIoeulq--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/cloudinary-training/image/upload/f_auto%2Cq_auto/blog/java/dog4.jpg" alt="resize with pad" title="resize with pad" width="800" height="588"&gt;

   &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;To change the color of the padding, just chain the &lt;code&gt;background&lt;/code&gt; parameter to the code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;new Transformation().width(300).height(200).crop("pad").background("red")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;tr&gt;
   &lt;td&gt;
Red Padding: 300 x 200
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;



&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--M3k1_6Ei--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/cloudinary-training/image/upload/f_auto%2Cq_auto/blog/java/dog5.jpg" alt="resize with red padding" title="resize with red padding" width="800" height="586"&gt;

   &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;To have Cloudinary determine the most used color in the media item and then apply that color to the padding, set &lt;code&gt;background&lt;/code&gt; to &lt;code&gt;auto&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;new Transformation().width(300).height(200).crop("pad").background("auto")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;tr&gt;
   &lt;td&gt;

Auto Padding: 300 x 200
   &lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
   &lt;td&gt;




&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--nnn4UIwg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/cloudinary-training/image/upload/f_auto%2Cq_auto/blog/java/dog6.jpg" alt="resize with auto pad" title="resize with auto pad" width="800" height="543"&gt;

   &lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;There are many more cropping transformations and you can learn about them &lt;a href="https://cloudinary.com/documentation/transformation_reference" rel="noopener noreferrer"&gt;online&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fun With Transformations
&lt;/h2&gt;

&lt;p&gt;Once you have uploaded your media to Cloudinary, you can write Java code to create URLs, image tags, and video tags that contain transformation parameters. Below are a few fun video examples in which we create URLs with transformations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Boomerang Effect With Video
&lt;/h3&gt;

&lt;p&gt;Consider a time-lapse video of an hourglass. How would the hourglass run if time moves backwards? To find out, apply the &lt;strong&gt;boomerang&lt;/strong&gt; effect on the video as a transformation, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cloudinary.url().transformation(new Transformation().effect("boomerang"))
.resourceType("video").generate("purple-hourglass.mp4")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Click this link to play the video in your browser:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/cloudinary-training/video/upload/e_boomerang/purple-hourglass.mp4" rel="noopener noreferrer"&gt;https://res.cloudinary.com/cloudinary-training/video/upload/e_boomerang/purple-hourglass.mp4&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--vZ4v9_Y0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/cloudinary-training/image/upload/f_auto%2Cq_auto/blog/java/hourglass.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--vZ4v9_Y0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/cloudinary-training/image/upload/f_auto%2Cq_auto/blog/java/hourglass.jpg" title="boomerang effect" alt="boomerang effect" width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Concatenation of Videos
&lt;/h3&gt;

&lt;p&gt;To play two videos in sequence, try this concatenation technique:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cloudinary.url().resourceType("video")
.transformation(new Transformation().width(300).height(200).crop("filt").chain()
.overlay(
new Layer().publicId("video:purple-hourglass"))
.flags("splice").width(300).height(200).crop("fit"))
.generate("3-o-clock.mp4")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Click this link to play the concatenated video in your browser:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/cloudinary-training/video/upload/c_fit,h_200,w_300/c_fit,fl_splice,h_200,l_video:purple-hourglass,w_300/3-o-clock.mp4" rel="noopener noreferrer"&gt;https://res.cloudinary.com/cloudinary-training/video/upload/c_fit,h_200,w_300/c_fit,fl_splice,h_200,l_video:purple-hourglass,w_300/3-o-clock.mp4&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--4VHvSjEW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/cloudinary-training/image/upload/f_auto%2Cq_auto/blog/java/clock.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--4VHvSjEW--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/cloudinary-training/image/upload/f_auto%2Cq_auto/blog/java/clock.jpg" title="video concatenation" alt="video concatenation" width="800" height="392"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Progress Indicator
&lt;/h3&gt;

&lt;p&gt;How about creating a visual indicator that shows how much time is left in your video? Simply add a progress bar, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cloudinary.url().transformation(new Transformation().effect("progressbar:frame:FF0000:12"))                     .resourceType("video").generate("purple-hourglass.mp4")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Click this link to play the video with a progress bar:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/cloudinary-training/video/upload/e_progressbar:frame:FF0000:12/purple-hourglass.mp4" rel="noopener noreferrer"&gt;https://res.cloudinary.com/cloudinary-training/video/upload/e_progressbar:frame:FF0000:12/purple-hourglass.mp4&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--K5veYrQb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/cloudinary-training/image/upload/f_auto%2Cq_auto/blog/java/progress-bar-video.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--K5veYrQb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/cloudinary-training/image/upload/f_auto%2Cq_auto/blog/java/progress-bar-video.jpg" title="progress bar" alt="progress bar" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;The design of the Cloudinary Java SDK makes it easy to code in Java. As you learn more about the many ways in which you can transform media with Cloudinary, you’ll become more creative and fluent with the process. Have fun!&lt;/p&gt;

&lt;h2&gt;
  
  
  Credits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;  Hourglass-timer video by &lt;a href="https://www.pexels.com/@samerdaboul?utm_content=attributionCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=pexels" rel="noopener noreferrer"&gt;Samer Daboul&lt;/a&gt; from &lt;a href="https://www.pexels.com/photo/close-up-view-of-a-sand-timer-1196530/?utm_content=attributionCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=pexels" rel="noopener noreferrer"&gt;Pexels&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  Clock video by &lt;a href="https://www.pexels.com/@jason-h-austin-544053?utm_content=attributionCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=pexels" rel="noopener noreferrer"&gt;Jason H. Austin&lt;/a&gt; from &lt;a href="https://www.pexels.com/photo/round-wall-clock-1509518/?utm_content=attributionCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=pexels" rel="noopener noreferrer"&gt;Pexels&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;a href="https://chrome.google.com/webstore/detail/cloudinary-media-inspecto/ehnkhkglbafecknplfmjklnnjimokpkg" rel="noopener noreferrer"&gt;Cloudinary Media Inspector Extension&lt;/a&gt; for insight on images&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>cloudinary</category>
      <category>java</category>
      <category>transformations</category>
    </item>
    <item>
      <title>Creating an API With Python Flask to Upload Files to Cloudinary</title>
      <dc:creator>Rebeccca Peltz</dc:creator>
      <pubDate>Tue, 20 Apr 2021 14:20:40 +0000</pubDate>
      <link>https://dev.to/rebeccapeltz/creating-an-api-with-python-flask-to-upload-files-to-cloudinary-1a0a</link>
      <guid>https://dev.to/rebeccapeltz/creating-an-api-with-python-flask-to-upload-files-to-cloudinary-1a0a</guid>
      <description>&lt;p&gt;Creating an API With Python Flask to Upload Files to Cloudinary&lt;/p&gt;

&lt;p&gt;Meta Title: Creating an API With Python Flask to Upload Files to Cloudinary&lt;br&gt;
Meta Description: Learn how to build an API with Python Flask and Cloudinary's Python SDk to upload files of any format to Cloudinary.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/rebeccapeltz/flask-cld-upload" rel="noopener noreferrer"&gt;Code&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cloudinary offers SDKs for many programming languages and frameworks. Even though it also offers an Upload API endpoint for both back-end and front-end code, most developers find the SDKs very helpful. If you're working with a powerful back-end framework like Python Flask, you'll be happy to hear that a Python SDK is now available.&lt;br&gt;&lt;br&gt;
This tutorial walks you through the process of building an API to upload images to Cloudinary. You can also upload other file types, including video and even nonmedia files, with the API.&lt;/p&gt;
&lt;h2&gt;
  
  
  Understanding the Difference Between Back End and Front End
&lt;/h2&gt;

&lt;p&gt;Generally, code that runs on the server is &lt;strong&gt;back end&lt;/strong&gt;, and code that runs on the browser is &lt;strong&gt;front end&lt;/strong&gt;. However, since server code can render HTML, CSS,  and JavaScript, which all run on the browser, confusion sometimes results. &lt;br&gt;
In the context of the Cloudinary SDKs, the back-end ones can read secret credentials, which should not be shared in the front end. In other words, you must never expose the back-end environment variables in the front end because front-end SDKs cannot hide credentials that are meant to be kept secret. As a solution, you can upload browser code with Cloudinary’s &lt;a href="https://cloudinary.com/documentation/upload_presets" rel="noopener noreferrer"&gt;&lt;strong&gt;unsigned presets&lt;/strong&gt;&lt;/a&gt; without revealing secret credentials. A better way would be to build a back-end API for secure uploads, keeping your &lt;code&gt;API_SECRET&lt;/code&gt; credential hidden. Read on to see how to do that with Cloudinary’s Python SDK and Python Flask or Python Django.&lt;/p&gt;
&lt;h2&gt;
  
  
  Coding a Flask API to Upload to Cloudinary
&lt;/h2&gt;

&lt;p&gt;The Flask framework makes it easy to define routes and their functionalities. To get started, first create a route named &lt;code&gt;/upload&lt;/code&gt;, which accepts a &lt;code&gt;POST&lt;/code&gt; that contains &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST" rel="noopener noreferrer"&gt;multipart/form-data&lt;/a&gt;. You then package up the image file input into a &lt;code&gt;FormData&lt;/code&gt; object in a submit handler and &lt;code&gt;POST&lt;/code&gt; it to your own Flask API to call Cloudinary’s Upload API, which is configured with your full set of Cloudinary credentials.&lt;br&gt;&lt;br&gt;
Flask's &lt;code&gt;request&lt;/code&gt; option enables you to get data from the client. When submitting files, such as uploading images, you can call upon &lt;code&gt;request&lt;/code&gt; to access them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="n"&gt;render_template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;file_to_upload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;file&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The data retrieved from &lt;code&gt;request.files['file']&lt;/code&gt; is an instance of &lt;code&gt;werkzeug.FileStorage&lt;/code&gt;. The object can be handed off to the Python SDK’s Upload function. Flask then wraps &lt;a href="https://palletsprojects.com/p/werkzeug/" rel="noopener noreferrer"&gt;Werkzeug&lt;/a&gt;, which handles the details of the Web Server Gateway Interface (WSGI).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;file_to_upload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;upload_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cloudinary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uploader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_to_upload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upload_result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For uploads to Cloudinary, the default &lt;code&gt;resource_type&lt;/code&gt; is &lt;code&gt;image&lt;/code&gt;. To expand or create a new Flask API, add &lt;code&gt;resource_type: 'video'&lt;/code&gt; or &lt;code&gt;resource_type: 'raw'&lt;/code&gt; for video or raw files, respectively.  &lt;a href="https://cloudinary.com/documentation/upload_images#uploading_non_media_files_as_raw_files" rel="noopener noreferrer"&gt;Raw&lt;/a&gt; refers to nonmedia file formats, including text and JSON.&lt;br&gt;
Finally, &lt;code&gt;upload_result&lt;/code&gt; is an object that contains the upload response. To complete the actions of your &lt;code&gt;upload&lt;/code&gt; API, return that response to the client, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;jsonify&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upload_result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Below is the complete code of your &lt;code&gt;upload&lt;/code&gt; API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/upload&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;upload_file&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
  &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;in upload route&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="n"&gt;cloudinary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cloud_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;CLOUD_NAME&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;API_KEY&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
    &lt;span class="n"&gt;api_secret&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;API_SECRET&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="n"&gt;upload_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;None&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;method&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;file_to_upload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;file&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%s file_to_upload&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file_to_upload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;file_to_upload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="n"&gt;upload_result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cloudinary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;uploader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;upload&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_to_upload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;info&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upload_result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;jsonify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;upload_result&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setting Up the Flask App
&lt;/h2&gt;

&lt;p&gt;Next, follow the steps below to build the app. &lt;/p&gt;

&lt;h3&gt;
  
  
  Set Up the Virtual Environment
&lt;/h3&gt;

&lt;p&gt;The command below establishes your virtual environment. This is an important step for encapsulating the libraries you'll be using in this app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv &lt;span class="nb"&gt;env
source env&lt;/span&gt;/bin/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To  deactivate the environment, type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;deactivate
&lt;span class="nb"&gt;source env&lt;/span&gt;/bin/activate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install Flask
&lt;/h3&gt;

&lt;p&gt;Install Flask with this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install &lt;/span&gt;&lt;span class="nv"&gt;Flask&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;1.1.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add a &lt;code&gt;requirements.txt&lt;/code&gt; File
&lt;/h3&gt;

&lt;p&gt;Create a &lt;code&gt;requirements.txt&lt;/code&gt; file to keep track of all the versioned libraries you need for the app and to facilitate future deployment.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pip freeze &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Upgrade the Python Package Installer
&lt;/h3&gt;

&lt;p&gt;If necessary, upgrade the Python package installer, called PIP, with the command below. The command might vary, depending on your local Python installation. As shown below, the &lt;code&gt;freeze&lt;/code&gt; command writes the library and version to the &lt;code&gt;requirements.txt&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;usr/local/opt/python@3.9/bin/python3.9 &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--upgrade&lt;/span&gt; pip
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pip freeze &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install Cloudinary
&lt;/h3&gt;

&lt;p&gt;Next, install Cloudinary to gain access to its Upload API for the Python SDK.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install &lt;/span&gt;cloudinary
 python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pip freeze &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add CORS support
&lt;/h3&gt;

&lt;p&gt;If you want to access your &lt;code&gt;upload&lt;/code&gt; API from a client served from a different host, add Cross Origin Resource Sharing (CORS) support:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install &lt;/span&gt;flask-cors
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pip freeze &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can add code to configure CORS for all the APIs and, specifically, the &lt;code&gt;upload&lt;/code&gt; API. The code below demonstrates both options.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask_cors&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;CORS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cross_origin&lt;/span&gt;
&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# After creating the Flask app, you can make all APIs allow cross-origin access.
&lt;/span&gt;&lt;span class="nc"&gt;CORS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="c1"&gt;# or a specific API
&lt;/span&gt;&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/upload&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;methods&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;POST&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="nd"&gt;@cross_origin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Work With Environment Variables
&lt;/h3&gt;

&lt;p&gt;You can easily load environment variables with Python. Conveniently, the &lt;code&gt;python-dotenv&lt;/code&gt; library is modeled on the Node.js &lt;code&gt;dotenv&lt;/code&gt; package. You need three Cloudinary environment variables, i.e., &lt;code&gt;CLOUD_NAME&lt;/code&gt;, &lt;code&gt;API_KEY&lt;/code&gt;, and &lt;code&gt;API_SECRET&lt;/code&gt;, for your code, but don't share &lt;code&gt;API_SECRET&lt;/code&gt;.  You can export those variables to the local environment. When you deploy to Heroku, you can add them to make them available to the app when it runs in a Heroku container.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;
&lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In your &lt;code&gt;upload&lt;/code&gt; API, you read in the environment variables with the &lt;code&gt;cloudinary.config()&lt;/code&gt; function. You then access those variables with the &lt;code&gt;os.getenv()&lt;/code&gt; function in the &lt;code&gt;dotenv&lt;/code&gt; library.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;cloudinary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cloud_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;CLOUD_NAME&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="n"&gt;api_key&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;API_KEY&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; 
    &lt;span class="n"&gt;api_secret&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;API_SECRET&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When working locally, you can create a gitignore'd &lt;code&gt;.env&lt;/code&gt; file that contains your Cloudinary credentials for local testing. The values for those credentials are displayed in your Cloudinary console.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;CLOUD_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;CLOUD_NAME
&lt;span class="nv"&gt;API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;API_KEY
&lt;span class="nv"&gt;API_SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;API_SECRET
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, install the &lt;code&gt;python-dotenv&lt;/code&gt; library:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install &lt;/span&gt;python-dotenv
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pip freeze &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing the API Locally
&lt;/h2&gt;

&lt;p&gt;You're now ready to test the app locally. You can do end-to-end testing with Postman and a local form that points at a server running on &lt;code&gt;localhost&lt;/code&gt;.&lt;br&gt;
Create your app with an upload function by downloading, cloning, or copying from &lt;a href="https://github.com/rebeccapeltz/flask-cld-upload/blob/master/app.py" rel="noopener noreferrer"&gt;this GitHub repository&lt;/a&gt;. Add your credentials to a &lt;code&gt;.env&lt;/code&gt; file and then start your server on &lt;code&gt;localhost&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 app.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above command opens a server at &lt;code&gt;http://127.0.0.1:5000/&lt;/code&gt;. Note that the default port is 5000.&lt;/p&gt;

&lt;h3&gt;
  
  
  Test With Postman
&lt;/h3&gt;

&lt;p&gt;In Postman, do the following: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Set the method to &lt;code&gt;POST&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Set the body type to &lt;code&gt;form-data&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Establish Key/Value pairs for input. Set the value for the Key to &lt;code&gt;file&lt;/code&gt;. Mouse over the &lt;strong&gt;Key&lt;/strong&gt; field and choose &lt;strong&gt;Text&lt;/strong&gt; or &lt;strong&gt;File&lt;/strong&gt; from the drop-down menu. Under &lt;strong&gt;File&lt;/strong&gt; is a &lt;strong&gt;Select Files&lt;/strong&gt; button; click it to select a file from your local drive.&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Send&lt;/strong&gt; to process the request.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The Cloudinary Upload API response is displayed at the bottom of the page&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--9JbEBv2W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/cloudinary-marketing/image/upload/w_700%2Cc_fill%2Cf_auto%2Cq_auto%2Cdpr_2.0/Web_Assets/blog/localpostman.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--9JbEBv2W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/cloudinary-marketing/image/upload/w_700%2Cc_fill%2Cf_auto%2Cq_auto%2Cdpr_2.0/Web_Assets/blog/localpostman.jpg" alt="Localhost with Postman" width="800" height="623"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Test With a Local Form
&lt;/h3&gt;

&lt;p&gt;Follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the &lt;code&gt;index.html&lt;/code&gt; file with a local HTTP server. Choose a local file and click &lt;strong&gt;Upload&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Run the JavaScript code below. The &lt;code&gt;fetch&lt;/code&gt; command calls the server that runs on  &lt;code&gt;localhost&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://127.0.0.1:5000/upload&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;If the process is successful, the Cloudinary Upload API response is displayed in the console.  Don't forget to set &lt;code&gt;event.preventDefault();&lt;/code&gt; for the submit handler. Otherwise, you'll get a page refresh and won't see your log.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Post a file in a submit handler by creating a &lt;code&gt;FormData&lt;/code&gt; object and appending the file, as shown in the code below.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;formData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;file&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fileInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;files&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;POST&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;formData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The above code packages up the file you select in your form input as if you had submitted a multipart form. A multipart form is important for submitting files because each input is sent as a block of data, separated by boundaries. &lt;br&gt;
The result is then displayed in your console, like this:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--csDbGV7G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/cloudinary-marketing/image/upload/w_700%2Cc_fill%2Cf_auto%2Cq_auto%2Cdpr_2.0/Web_Assets/blog/formlocal.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--csDbGV7G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://res.cloudinary.com/cloudinary-marketing/image/upload/w_700%2Cc_fill%2Cf_auto%2Cq_auto%2Cdpr_2.0/Web_Assets/blog/formlocal.jpg" alt="local form" width="800" height="554"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Deploying the Flask App to Heroku
&lt;/h2&gt;

&lt;p&gt;Heroku is a platform as a service (PaaS) for serving prototypes or production-ready apps. Host the API you just built on Heroku by doing the following: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Create a free account on Heroku.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install &lt;a href="https://gunicorn.org/" rel="noopener noreferrer"&gt;Gunicorn&lt;/a&gt;, short for Green Unicorn, a Python WSGI HTTP server for hosting apps.&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pip &lt;span class="nb"&gt;install &lt;/span&gt;&lt;span class="nv"&gt;gunicorn&lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;20.0.4
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; pip freeze &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Create a file called &lt;code&gt;Prodfile&lt;/code&gt; with instructions for Heroku to start your app with Gunicorn:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;web: gunicorn app:app
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Create a file called &lt;code&gt;runtime.txt&lt;/code&gt; that contains the Python version you want to use:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python-3.9.1
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;Deploy with Git:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight shell"&gt;&lt;code&gt;git init
git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt;&lt;span class="s2"&gt;"my first commit"&lt;/span&gt;
&lt;span class="c"&gt;# Create a new Heroku app&lt;/span&gt;
heroku create
&lt;span class="c"&gt;# You will get a URL where the app will be hosted&lt;/span&gt;
git remote &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;span class="c"&gt;# will confirm that you have set up a remote to push to Heroku&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;/li&gt;

&lt;/ol&gt;

&lt;h1&gt;
  
  
  If you have an existing Heroku app that you created in the Heroku GUI
&lt;/h1&gt;

&lt;h1&gt;
  
  
  you can add it with this command instead of creating a new one
&lt;/h1&gt;

&lt;p&gt;heroku git:remote -a thawing-inlet-61413&lt;/p&gt;

&lt;h1&gt;
  
  
  To deploy, you can now just push to Heroku
&lt;/h1&gt;

&lt;p&gt;git push heroku master&lt;/p&gt;

&lt;h1&gt;
  
  
  Open your server (it's nice to have a GET method so you can verify like this)
&lt;/h1&gt;

&lt;p&gt;heroku open&lt;/p&gt;

&lt;h1&gt;
  
  
  To log in to Heroku from the command line, type:
&lt;/h1&gt;

&lt;p&gt;heroku login&lt;/p&gt;

&lt;h1&gt;
  
  
  You’ll then be prompted to open the webpage to log in
&lt;/h1&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;


You can build the new Heroku app with the CLI. However, as noted above, if you built it on the heroku.com dashboard, you can just add the link Heroku created for you to a `git:remote` command.

### Loading Environment Variables on Heroku

To load environment variables on Heroku, open the dashboard and navigate to your new server instance. Click the **Settings** tab and scroll down to the **Config Vars** section. Click **Reveal Config Vars**, where you can enter Key/Value pairs. This is where you load your Cloudinary environment variables: `CLOUD_NAME`, `API_KEY`, and `API_SECRET`.

![Heroku Config Vars](https://res.cloudinary.com/cloudinary-marketing/image/upload/w_700,c_fill,f_auto,q_auto,dpr_2.0/Web_Assets/blog/environmentvars.jpg)

For more options for setting up `prod` and `dev` instances, see the [Heroku documentation](https://devcenter.heroku.com/articles/git).

## Testing the API Deployed on Heroku
If you have a `GET` method API like the "Hello World!" in the `app.py` example, you can open the Heroku app in the browser.

For end-to-end testing, you can `POST` to the Heroku server link from Postman. The result is similar to that for local testing.

![Remote Post](https://res.cloudinary.com/cloudinary-marketing/image/upload/w_700,c_fill,f_auto,q_auto,dpr_2.0/Web_Assets/blog/remotepost.jpg)

Alternatively, modify the `index.html` file to post to your Heroku server. Just add your Heroku server link to the `fetch` command, like this:



```JavaScript
fetch("https://xxxx.herokuapp.com", options)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Securing the App
&lt;/h2&gt;

&lt;p&gt;You might have noticed that the Heroku deployment link has been erased from the images above. That’s for security. Deploying this app to Heroku enables you to upload data to Cloudinary. If the link is not secured for use on an authenticated webpage only, anyone can upload to your Cloudinary cloud. Even though you are hiding your environment variables, you’d have created a public method to change data on your cloud.&lt;/p&gt;

&lt;p&gt;This tutorial does not cover how to set up authentication. Many options are available for authentication of API routes. You might want to look into Auth0, Google, or GitHub. Alternatively, build your own process based on your user database for this type of authentication.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving On
&lt;/h2&gt;

&lt;p&gt;Once you get this API running, you can build other media APIs with the Cloudinary Python SDK by following the same pattern. Have fun!&lt;/p&gt;

</description>
      <category>cloudinary</category>
      <category>python</category>
      <category>flask</category>
    </item>
    <item>
      <title>Five Ways for Integrating the Cloudinary Video Player Into React Applications</title>
      <dc:creator>Rebeccca Peltz</dc:creator>
      <pubDate>Mon, 15 Mar 2021 18:50:06 +0000</pubDate>
      <link>https://dev.to/rebeccapeltz/five-ways-for-integrating-the-cloudinary-video-player-into-react-applications-2kcb</link>
      <guid>https://dev.to/rebeccapeltz/five-ways-for-integrating-the-cloudinary-video-player-into-react-applications-2kcb</guid>
      <description>&lt;p&gt;&lt;a href="https://github.com/rebeccapeltz/react-video-player-alternatives" rel="noopener noreferrer"&gt;Code&lt;/a&gt;  &lt;/p&gt;

&lt;p&gt;&lt;a href="https://amazing-khorana-ae17ab.netlify.app/" rel="noopener noreferrer"&gt;Demo&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;&lt;a href="https://codesandbox.io/s/elastic-architecture-stlk6" rel="noopener noreferrer"&gt;CodeSandbox&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As of this writing &lt;a href="https://www.npmtrends.com/react" rel="noopener noreferrer"&gt;npm trends&lt;/a&gt; reports over 10,000,000 weekly downloads of the React library—and no wonder. The &lt;a href="https://www.weforum.org/agenda/2020/05/coronavirus-covid19-consumers-shopping-goods-economics-industry" rel="noopener noreferrer"&gt;disruption created by Covid in 2020&lt;/a&gt; is expected to persist, significantly upping the consumer demand for video as a means of enhancing experiences in online work, shopping, education, healthcare, social interactions, and entertainment. Hence the many questions on how to add a video player to a React application. This post shows you five ways in which to do that with the Cloudinary Video Player in a React component.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cloudinary Video Player
&lt;/h2&gt;

&lt;p&gt;The Cloudinary Video Player is a feature-enhanced library that builds upon the popular, open-source &lt;a href="https://videojs.com/" rel="noopener noreferrer"&gt;Video JS&lt;/a&gt; player. Like that player, the Cloudinary one is implemented on a webpage by rendering a &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; tag and then binding JavaScript and CSS functionality to the video DOM element. Also, Cloudinary has added the following features to the video-playing experience:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cloudinary.com/documentation/video_player_api_reference" rel="noopener noreferrer"&gt;Transformations at the player or video level&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloudinary.com/documentation/video_player_playlists_recommendations" rel="noopener noreferrer"&gt;Multiple players per page, including playlists created by metadata tagging and recommendations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloudinary.com/documentation/video_manipulation_and_delivery#automatic_format_selection_f_auto" rel="noopener noreferrer"&gt;Auto-formatting transformations, which ensure delivery in the best format for the viewing device or browser&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloudinary.com/documentation/video_manipulation_and_delivery#adaptive_bitrate_streaming_hls_and_mpeg_dash" rel="noopener noreferrer"&gt;Suggestion of adaptive bitrate streaming for videos that are longer than one minute to provide HTTP streaming and optimize resolution in unstable networks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloudinary.com/documentation/video_player_customization#video_player_visuals" rel="noopener noreferrer"&gt;Customization&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloudinary.com/documentation/video_player_api_reference#ads_and_analytics" rel="noopener noreferrer"&gt;Monetization through interstitial ads&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://cloudinary.com/documentation/video_player_shoppable_videos" rel="noopener noreferrer"&gt;Shoppable video&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;
&lt;a href="https://cloudinary.com/documentation/video_player_events_analytics" rel="noopener noreferrer"&gt;Analytics&lt;/a&gt;
## Libraries in the Cloudinary Video Player
There are 2 Video Player libraries available: &lt;a href="https://cloudinary.com/documentation/cloudinary_video_player#installation_and_setup" rel="noopener noreferrer"&gt;&lt;strong&gt;Standard&lt;/strong&gt; and &lt;strong&gt;Light&lt;/strong&gt;&lt;/a&gt;. The Light library is smaller as it does not include the following features: Adaptive Bitrate Streaming, Video Ads and Shoppable videos. In this exercise, we'll be using the &lt;strong&gt;Light&lt;/strong&gt; version.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We'll use the &lt;a href="https://www.npmjs.com/package/cloudinary-video-player" rel="noopener noreferrer"&gt;Video Player&lt;/a&gt; library.&lt;/p&gt;

&lt;h2&gt;
  
  
  React Components
&lt;/h2&gt;

&lt;p&gt;With React, you can wrap functionality into a reusable component that renders HTML, CSS, and JavaScript.  Timing is important in many cases, however.  In the case of the Video Player, you can’t initialize it until a &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; tag has been rendered.  &lt;/p&gt;

&lt;p&gt;This exercise leverages the following React hooks:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;useState&lt;/strong&gt;
This function returns an immutable data value and a setter that can update it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;useEffect&lt;/strong&gt; 
This function is called when the component that holds it is rendered and then when stateful data within the function changes. useEffect helps determine that a component has been rendered and, in some respects, replaces the &lt;code&gt;componentDidMount&lt;/code&gt; and &lt;code&gt;componentDidUpdate&lt;/code&gt; life cycles in a Class function.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;useContext&lt;/strong&gt; 
This function, which serves as a container for stateful values, can be passed to a component function to specify the state. useContext defines a provider function made up of useState functions. Sharing context between components is one way to implement inter-component communications. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, custom hooks can refactor stateful code for reuse in many components. You’ll learn how to create a custom hook later in this post. &lt;/p&gt;

&lt;h2&gt;
  
  
  The Code
&lt;/h2&gt;

&lt;p&gt;Here are the five components that manage state, logic, and event handling and with which you can host the Cloudinary Video Player:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A function-based component for an embedded, cloud-hosted video player&lt;/li&gt;
&lt;li&gt;A class-based component&lt;/li&gt;
&lt;li&gt;A function-based component with useHooks&lt;/li&gt;
&lt;li&gt;A function-based component with Context&lt;/li&gt;
&lt;li&gt;A function-based component with a custom hook called useCloudinaryVideoPlayer for use in any component that hosts video &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The external data for all the components in this exercise is defined in App.js. An object called video options contains the Cloudinary &lt;strong&gt;cloudName&lt;/strong&gt; and &lt;strong&gt;publicId&lt;/strong&gt;. You can pass other video options to the Video Player’s components. For simplicity, however, those two are the only ones that are required to identify the video you want to host.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;videoOptions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;cloudName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;demo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;publicId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;race_road_car&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For details on the options available for the Cloudinary Video Player, see &lt;a href="https://cloudinary.com/documentation/video_player_api_reference" rel="noopener noreferrer"&gt;Video Player API Reference&lt;/a&gt;.  &lt;/p&gt;

&lt;h3&gt;
  
  
  Function-Based, Cloud-Hosted Video Player
&lt;/h3&gt;

&lt;p&gt;On the Cloudinary site is a &lt;a href="https://studio.cloudinary.com/" rel="noopener noreferrer"&gt;demo page&lt;/a&gt; on which you can experiment with Video Player options. Once you are satisfied with the features you have selected, the demo generates the JavaScript code or an embeddable &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; tag with the options set for hosting in the cloud. You’ll also get a URL with all of the settings, including &lt;strong&gt;cloudName&lt;/strong&gt; and &lt;strong&gt;publicId&lt;/strong&gt;.  &lt;/p&gt;

&lt;p&gt;In the Apps.js file is the component JSX for rendering a card that contains the name of the component and the video player hosting the video specified in the video options. In this case, the component is named VideoPlayerCloudHosted.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Video Player Cloud Hosted&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;VideoPlayerCloudHosted&lt;/span&gt; &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;videoOptions&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the code below, the URL in the src attribute contains the cloud name, the public ID, and the options specified in the Video Player demo page. Also, the cloud name and the public ID here are passed to the component through props, which is passed from the parent.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;VideoPlayerCloudHosted&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://player.cloudinary.com/embed/?public_id=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;cloud_name=&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cloudName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;amp;player%5Bfluid%5D=true&amp;amp;player%5Bcontrols%5D=true&amp;amp;source%5Bsource_types%5D%5B0%5D=mp4`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;iframe-container&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;iframe&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;responsive-iframe&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Cloud Hosted Video Player&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="nx"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;640&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="nx"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;480&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="nx"&gt;allow&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;autoplay; fullscreen; encrypted-media; picture-in-picture&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
        &lt;span class="nx"&gt;allowFullScreen&lt;/span&gt;
        &lt;span class="nx"&gt;frameBorder&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="o"&gt;&amp;gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/iframe&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;VideoPlayerCloudHosted&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All you’re doing in this functional component is rendering the &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; tag by passing along the videoOptions data.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;video-card&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Video Player Cloud Hosted&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"vp"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;VideoPlayerCloudHosted&lt;/span&gt; &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;videoOptions&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add CSS code for a responsive &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="nc"&gt;.iframe-container&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;overflow&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;hidden&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;padding-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;56.25%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c"&gt;/* 16:9 Aspect Ratio */&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.responsive-iframe&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;absolute&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100%&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;border&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;An embedded video player has a few downsides, especially in production: you might not have as much control as you would like and, depending on network conditions, loading might be slower.  &lt;/p&gt;

&lt;h3&gt;
  
  
  Class-Based Component
&lt;/h3&gt;

&lt;p&gt;In App.js, you import your class-based video player and render a card that uses the VideoPlayerClass component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;VideoPlayerClass&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./VideoPlayerClass&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"video-card"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Video Player in Class&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"vp"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;VideoPlayerClass&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Class component requires importing just one library,  &lt;strong&gt;cloudinary-video-player&lt;/strong&gt;, to create a Cloudinary Video Player within Class. Also, import the CSS code to support the video player. You’ll see those imports in another example, which will ultimately justify the creation of a custom hook.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cloudinary-video-player/dist/cld-video-player.light.min&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cloudinary-video-player/dist/cld-video-player.light.min.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;VideoPlayerClass&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;videoPlayerInit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cloudinary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;videoPlayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;some-video&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;cloud_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cloudName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;publicId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;fluid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;controls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;preload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;auto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;mute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;autoplay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nf"&gt;componentDidMount&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;videoPlayerInit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
          &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;video&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;some-video&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;VideoPlayerClass&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Video Player is then initialized with the specified options. This component implements the code required for initializing the video player and then binds the player to the DOM element, which is the first &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; tag with the class some-video in the videoPlayerInit function. The component then renders a &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; tag, after which the lifecycle function componentDidMount calls the videoPlayerInit function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;VideoPlayerClass&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;cld&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Cloudinary&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;cloud_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cloudName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;secure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nx"&gt;videoPlayerInit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cld&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cld&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;cld&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;videoPlayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;some-video&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;publicId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;fluid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;controls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;preload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;auto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;mute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;autoplay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="nf"&gt;componentDidMount&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;videoPlayerInit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
        &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;video&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;some-video&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;VideoPlayerClass&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Function Based Component
&lt;/h3&gt;

&lt;p&gt;Now load the libraries and wait for the &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; tag to render in a function-based approach.&lt;br&gt;
First,  render the component in App.js:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;video-card&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Video Player in Function&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;h2&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt; &lt;span class="na"&gt;className&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"vp"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;VideoPlayerFunction&lt;/span&gt; &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;videoOptions&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="na"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, import the libraries. Below is the same code in the class-based example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cloudinary-video-player/dist/cld-video-player.light.min&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cloudinary-video-player/dist/cld-video-player.light.min.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, set up a functional component. The &lt;code&gt;videoPlayerInit&lt;/code&gt; function looks the same as it did in the class-based approach. Note that props is passed to the function rather than being implicitly added to the class context, as in the class-based function.&lt;br&gt;
Instead of relying on the &lt;code&gt;componentDidMount&lt;/code&gt; function to notify you that the &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; tag has been rendered, you can determine that with the useEffect functional React hook and call the Video Player’s init function.&lt;br&gt;
Keep in mind that the instructions of useEffect, which  is called any time the component re-renders, are executed as if the function was called in componentDidMound and &lt;code&gt;componentDidUpdate&lt;/code&gt; in a class-based component. Since you don’t want to call initVideoPlayer except after the &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; tag is first rendered, be sure to guard against that scenario.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;VideoPlayerFunction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;videoPlayerInit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cloudinary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;videoPlayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.fn-video&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;cloud_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cloudName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;publicId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;fluid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;controls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;preload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;auto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;mute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;autoplay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;videoPlayerInit&lt;/span&gt;&lt;span class="p"&gt;(),[]);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;video&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fn-video&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;VideoPlayerFunction&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Function-Based Component With Context
&lt;/h3&gt;

&lt;p&gt;The React hook useContext includes both &lt;code&gt;useState&lt;/code&gt; and Provider functions. The convention for naming the provider is to give it the same name as the context object. In this case, you’ll have &lt;code&gt;VideoOptionsContext&lt;/code&gt; and &lt;code&gt;VideoOptionsProvider&lt;/code&gt;, which can share logic and state between components.&lt;br&gt;
Start with creating &lt;code&gt;VideoOptionsContext&lt;/code&gt; for holding and granting access to state. useState is a function that returns the current value of the state and a setter function that will set a new state value. You’ll capture the options &lt;code&gt;cloudName&lt;/code&gt; and &lt;code&gt;publicId&lt;/code&gt; in that context.&lt;br&gt;&lt;br&gt;
The data is an object that contains those two video options. Create the context and name it &lt;strong&gt;VideoOptionsContext&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;video&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;cloudName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;demo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;publicId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;race_road_car&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;  
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;VideoOptionsContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, implement and export &lt;strong&gt;VideoOptionsProvider&lt;/strong&gt;, which sets up the state for the options. Specify the default values for videoOptions, which are &lt;code&gt;cloudName&lt;/code&gt; and &lt;code&gt;publicId&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;video&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;cloudName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;demo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;publicId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;race_road_car&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;VideoOptionsContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// This context provider is passed to any component requiring the context&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;VideoOptionsProvider&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;videoOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setVideoOptions&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;video&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;VideoOptionsContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Provider&lt;/span&gt;
      &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
        &lt;span class="nx"&gt;videoOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;setVideoOptions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/VideoOptionsContext.Provider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To use this context in a functional component, import VideoOptionsContext into App.js and wrap the rendering of the VideoPlayerContext in this Context component:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;VideoPlayerContext&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./VideoPlayerContext&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;VideoOptionsProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./VideoOptionsContext&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;video-card&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Video&lt;/span&gt; &lt;span class="nx"&gt;Player&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;Function&lt;/span&gt; &lt;span class="kd"&gt;with&lt;/span&gt; &lt;span class="nx"&gt;Context&lt;/span&gt; &lt;span class="nx"&gt;Provider&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h2&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;VideoOptionsProvider&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;VideoPlayerContext&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/VideoOptionsProvider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;VideoPlayerContext&lt;/strong&gt; resembles &lt;strong&gt;VideoPlayerFunction&lt;/strong&gt;, except that the former gets the options from context rather than from props.&lt;br&gt;&lt;br&gt;
Note in the code that you import &lt;strong&gt;VideoOptionsContext&lt;/strong&gt; and then pull options with the &lt;code&gt;useContext&lt;/code&gt; hook. You can then reference the options as &lt;code&gt;options.videoOptions.cloudName&lt;/code&gt; and &lt;code&gt;options.videoOptions.publicId&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useContext&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;VideoOptionsContext&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./VideoOptionsContext&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cloudinary-video-player/dist/cld-video-player.light.min&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cloudinary-video-player/dist/cld-video-player.light.min.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;VideoPlayerContext&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;VideoOptionsContext&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;videoPlayerInit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;add video player JS&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;player&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cloudinary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;videoPlayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.context-video&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;cloud_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;videoOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cloudName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;publicId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;videoOptions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;fluid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;controls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;preload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;auto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;mute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;autoplay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nx"&gt;player&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;loadedmetadata&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;app detected&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;videoPlayerInit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;  
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;calling fn render&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;video&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;context-video&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;VideoPlayerContext&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you return &lt;code&gt;useEffect&lt;/code&gt;, pass the second parameter as an empty array to avoid re-rendering&lt;/p&gt;

&lt;h3&gt;
  
  
  Function-Based Component With Custom Hook
&lt;/h3&gt;

&lt;p&gt;You’ve now seen code duplicated across several component examples: library imports, the init video-player function, Cloudinary instantiation. And you might wonder, “How can I create a reactor?’ The answer is with a custom hook.&lt;br&gt;
Since the convention for naming hooks is to prefix the functionality you’re capturing with use, create a useCloudinaryVideoPlayer hook. Also, since you’ve specified different classes for each of the samples, the hook must work with arguments and maintain the state for the following: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The cloud name&lt;/li&gt;
&lt;li&gt;The public ID&lt;/li&gt;
&lt;li&gt;The class name of the &lt;code&gt;&amp;lt;video&amp;gt;&lt;/code&gt; tag, which serves as an element selector&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Other than pulling the values of  those three variables, the code looks like the functional components you created earlier.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cloudinary-video-player/dist/cld-video-player.light.min&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;cloudinary-video-player/dist/cld-video-player.light.min.css&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;useCloudinaryVideoPlayer&lt;/span&gt;  &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;cloudName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cloudName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;publicId&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;videoClass&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;videoPlayerInit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cloudinary&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;videoPlayer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;cloud_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cloudName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;publicId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;publicId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;fluid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;controls&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;preload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;auto&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;mute&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;autoplay&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;    
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;videoPlayerInit&lt;/span&gt;&lt;span class="p"&gt;(),[]);&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;OK&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;```



You need not capture the setters because they won’t serve any purpose. App.js will continue to pass an object with only the cloud name and public ID.



```&lt;/span&gt;&lt;span class="nx"&gt;jsx&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;video-card&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Video&lt;/span&gt; &lt;span class="nx"&gt;Player&lt;/span&gt; &lt;span class="nx"&gt;Custom&lt;/span&gt; &lt;span class="nx"&gt;Hook&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h2&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;vp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;VideoPlayerCustomHook&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;videoOptions&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the new &lt;strong&gt;VideoPlayerCustomHooks&lt;/strong&gt; component, add the class name to the object that is passed to the useCloudinaryVideoPlayer hook.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useCloudinaryVideoPlayer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./useCloudinaryVideoPlayer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;VideoPlayerCustomHook&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;videoClass&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;custom-video&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;useCloudinaryVideoPlayer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;videoClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;videoClass&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;video&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;videoClass&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;VideoPlayerCustomHook&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that it can import the reusable code from the hook, the actual code for the Video Player is much simpler. Just add the video class to a new object that includes props and that serves as a parameter for the custom hook.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recommendations
&lt;/h2&gt;

&lt;p&gt;There are many ways for creating a component with React and, therefore, one for hosting the Cloudinary Video player.&lt;br&gt;
What’s the best way, you ask? For class-based components, you might want to use the example in this exercise, but you can also introduce function-based components into an app with class-based components. If you’ll be creating components that vary by certain data, consider leveraging a custom hook. Though probably not the best use case, custom hooks enable the use of context with a functional component. In general, the direction forward with React is through function-based components with hooks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Note:
&lt;/h2&gt;

&lt;p&gt;May 2022. The cloudinary-video-player was updated to v1.9 and this removed the need to import cloudinary-core.  It changed the instantiation of the video player.  The current code in GitHub contains the updated Cloudinary Video Player code and instantiation. The markup code on this page uses the most recent libraries as well.&lt;/p&gt;

</description>
      <category>cloudinary</category>
      <category>react</category>
      <category>video</category>
    </item>
  </channel>
</rss>
