{"id":34764,"date":"2024-07-17T07:00:00","date_gmt":"2024-07-17T14:00:00","guid":{"rendered":"https:\/\/cloudinary.com\/blog\/?p=34764"},"modified":"2025-03-02T06:55:25","modified_gmt":"2025-03-02T14:55:25","slug":"upload-video-to-cloudinary-next-js-app-router","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/upload-video-to-cloudinary-next-js-app-router","title":{"rendered":"How to Upload a Video to Cloudinary in Next.js App Router"},"content":{"rendered":"\n<p>This blog post introduces a developer-friendly approach to client-side media handling challenges, leveraging Next.js App Router for video uploads to <a target=\"_blank\" href=\"https:\/\/cloudinary.com\/\" rel=\"noreferrer noopener\">Cloudinary<\/a>.&nbsp;<\/p>\n\n\n\n<p>The complete source code of this project is on <a target=\"_blank\" href=\"https:\/\/github.com\/ugwutotheeshoes\/cloudinary-video-nextjs\" rel=\"noreferrer noopener\">Git<\/a><a target=\"_blank\" href=\"https:\/\/github.com\/ugwutotheeshoes\/cloudinary-video-nextjs\" rel=\"noreferrer noopener\">H<\/a><a target=\"_blank\" href=\"https:\/\/github.com\/ugwutotheeshoes\/cloudinary-video-nextjs\" rel=\"noreferrer noopener\">ub<\/a>.&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Working With Next.js App Router<\/h2>\n\n\n\n<p>In the context of video uploads, using Next.js App Router with server components offers the following advantages:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Enhanced security.<\/strong> By managing essential functionality on the server, we minimize the exposure of sensitive data on the client side.<\/li>\n\n\n\n<li><strong>Improved <\/strong><strong>p<\/strong><strong>erformance<\/strong><strong>.<\/strong> Data fetching and video upload processing are done on the server, minimizing the strain on client-side resources, particularly for larger video files.<\/li>\n\n\n\n<li><strong>Streamlined <\/strong><strong>d<\/strong><strong>ata <\/strong><strong>f<\/strong><strong>low<\/strong><strong>.<\/strong>Server components handle form submissions and data manipulation directly, making the codebase cleaner and more maintainable.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Prerequisites<\/h2>\n\n\n\n<p>You must have the following:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A <a href=\"https:\/\/cloudinary.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Cloudinary<\/a> platform account.<\/li>\n\n\n\n<li>A basic understanding of Next.js and Typescript.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Project Setup and Installation<\/h2>\n\n\n\n<p>With our Cloudinary account and credentials ready, let\u2019s bootstrap a Next.js app.<\/p>\n\n\n\n<p><code>npx create-next-app@latest<\/code><\/p>\n\n\n\n<p>We\u2019ll receive prompts to set up our new app. Select Typescript, Tailwind CSS, and App Router, as they\u2019ll be needed to build this project. Then, install the necessary dependencies using the command below.&nbsp;<\/p>\n\n\n\n<p><code>npm install cloudinary next-cloudinary multer<\/code><\/p>\n\n\n\n<p>Running npm run dev should render our project at <a target=\"_blank\" href=\"https:\/\/localhost:3000\/\" rel=\"noreferrer noopener\">https:\/\/localhost:3000\/<\/a> in our browser.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Setting Up a Cloudinary Account<\/h2>\n\n\n\n<p>After creating an account on Cloudinary, we\u2019ll have access to the dashboard, where we\u2019ll find all the required credentials.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img width=\"1024\" height=\"302\" data-public-id=\"Web_Assets\/blog\/blog-How-to-Upload-a-Video-to-Cloudinary-in-Next.js-App-Router-1\/blog-How-to-Upload-a-Video-to-Cloudinary-in-Next.js-App-Router-1.png\" decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/w_1024,h_302,c_scale\/f_auto,q_auto\/v1721249752\/Web_Assets\/blog\/blog-How-to-Upload-a-Video-to-Cloudinary-in-Next.js-App-Router-1\/blog-How-to-Upload-a-Video-to-Cloudinary-in-Next.js-App-Router-1.png?_i=AA\" alt=\"\" class=\"wp-post-34764 wp-image-34765\" loading=\"lazy\" data-format=\"png\" data-transformations=\"f_auto,q_auto\" data-version=\"1721249752\" data-seo=\"1\" srcset=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1721249752\/Web_Assets\/blog\/blog-How-to-Upload-a-Video-to-Cloudinary-in-Next.js-App-Router-1\/blog-How-to-Upload-a-Video-to-Cloudinary-in-Next.js-App-Router-1.png?_i=AA 1600w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1721249752\/Web_Assets\/blog\/blog-How-to-Upload-a-Video-to-Cloudinary-in-Next.js-App-Router-1\/blog-How-to-Upload-a-Video-to-Cloudinary-in-Next.js-App-Router-1.png?_i=AA 300w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1721249752\/Web_Assets\/blog\/blog-How-to-Upload-a-Video-to-Cloudinary-in-Next.js-App-Router-1\/blog-How-to-Upload-a-Video-to-Cloudinary-in-Next.js-App-Router-1.png?_i=AA 768w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1721249752\/Web_Assets\/blog\/blog-How-to-Upload-a-Video-to-Cloudinary-in-Next.js-App-Router-1\/blog-How-to-Upload-a-Video-to-Cloudinary-in-Next.js-App-Router-1.png?_i=AA 1024w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1721249752\/Web_Assets\/blog\/blog-How-to-Upload-a-Video-to-Cloudinary-in-Next.js-App-Router-1\/blog-How-to-Upload-a-Video-to-Cloudinary-in-Next.js-App-Router-1.png?_i=AA 1536w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>Next, create a <code>.env.local<\/code> file in the root folder of our project to store our Cloudinary credentials.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\">\/\/ .env.local\nNEXT_PUBLIC_CLOUDINARY_CLOUD_NAME=<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">cloudinary_api_key<\/span>&gt;<\/span>\nNEXT_PUBLIC_CLOUDINARY_API_KEY=<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">cloudinary_api_secret<\/span>&gt;<\/span>\nCLOUDINARY_API_SECRET=<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">cloudinary_cloud_name<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<div class='c-callout  c-callout--inline-title c-callout--note'><strong class='c-callout__title'>Note:<\/strong> <p>Never share your credentials publicly.<\/p>\n<\/div>\n\n\n<h2 class=\"wp-block-heading\">Building the App Functionality<\/h2>\n\n\n\n<p>We&#8217;ll build a form that allows users to select a video file to upload and include a button to initiate the upload process. Add the code snippet below to the <code>src\/app\/pages.tsx<\/code> file.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">\n<span class=\"hljs-comment\">\/\/src\/app\/pages.tsx<\/span>\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">Home<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n  <span class=\"hljs-keyword\">return<\/span> (\n  <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n    <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">'text-xl font-bold text-center pb-10'<\/span>&gt;<\/span>\n      How to upload a video to Cloudinary in Next.js App Router\n    <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"flex justify-center mt-10 items-center\"<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">form<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"file\"<\/span> <span class=\"hljs-attr\">accept<\/span>=<span class=\"hljs-string\">\"video\/*\"<\/span>  \/&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">'submit'<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">'bg-blue-800 text-white p-2 rounded-md'<\/span>&gt;<\/span>Upload<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">form<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n  );\n}<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The next step involves integrating the useFormState hook to manage the form submission state in our project. Update the file with the code snippet below:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-wrap-lines\">\n<span class=\"hljs-comment\">\/\/ src\/app\/pages.tsx<\/span>\n+ import { useFormState } from <span class=\"hljs-string\">'react-dom'<\/span>;\nexport <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">Home<\/span><span class=\"hljs-params\">()<\/span> <\/span>{\n+  <span class=\"hljs-keyword\">const<\/span> &#91;url, formAction] = useFormState(upload, <span class=\"hljs-keyword\">null<\/span>);\n<span class=\"hljs-keyword\">return<\/span>(\n ...\n &lt;form action={formAction}&gt;\n  ...\n&lt;\/form\n  )\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The hook takes two arguments: an initial state and an action function \u2014 upload \u2014 to handle form submissions and upload requests. Then, it returns an array with two elements:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The current state (the uploaded video URL in our case).<\/li>\n\n\n\n<li>A new action function, formAction, that triggers the upload process.<\/li>\n<\/ul>\n\n\n<div class='c-callout  c-callout--inline-title c-callout--note'><strong class='c-callout__title'>Note:<\/strong> <p>The <code>useFormState<\/code> hook is currently marked as deprecated, which means it will be replaced with a new hook called <code>useActionState<\/code> in a future update. To try out <code>useActionState<\/code> features early, use the Next.js Canary version <code>14.3.0-canary.45<\/code>, which already includes support for <code>useActionState<\/code>. However, remember that Canary versions are unstable and not recommended for production use.<\/p>\n<\/div>\n\n\n<p>Let\u2019s create the action function: upload. Go to the src folder and create <code>actions\/upload.ts<\/code> with the following content.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">\n<span class=\"hljs-comment\">\/\/ src\/actions\/upload.ts<\/span>\n<span class=\"hljs-string\">\"use server\"<\/span>\n<span class=\"hljs-keyword\">import<\/span> { v2 <span class=\"hljs-keyword\">as<\/span> cloudinary } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'cloudinary'<\/span>;\ncloudinary.config({\n  <span class=\"hljs-attr\">cloud_name<\/span>: process.env.NEXT_PUBLIC_CLOUDINARY_CLOUD_NAME,\n  <span class=\"hljs-attr\">api_key<\/span>: process.env.NEXT_PUBLIC_CLOUDINARY_API_KEY,\n  <span class=\"hljs-attr\">api_secret<\/span>: process.env.CLOUDINARY_API_SECRET,\n});\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">upload<\/span>(<span class=\"hljs-params\">previousState: string | undefined | null, formData: FormData<\/span>) <\/span>{\n  <span class=\"hljs-keyword\">const<\/span> file = formData.get(<span class=\"hljs-string\">'video'<\/span>) <span class=\"hljs-keyword\">as<\/span> File;\n  <span class=\"hljs-keyword\">const<\/span> buffer: Buffer = Buffer.from(<span class=\"hljs-keyword\">await<\/span> file.arrayBuffer());\n  <span class=\"hljs-keyword\">try<\/span> {\n    <span class=\"hljs-keyword\">const<\/span> base64Image: string = <span class=\"hljs-string\">`data:<span class=\"hljs-subst\">${file.type}<\/span>;base64,<span class=\"hljs-subst\">${buffer.toString(\n      <span class=\"hljs-string\">'base64'<\/span>\n    )}<\/span>`<\/span>;\n    <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">`The file: <span class=\"hljs-subst\">${previousState}<\/span> is uploading...`<\/span>);\n    <span class=\"hljs-keyword\">const<\/span> response = <span class=\"hljs-keyword\">await<\/span> cloudinary.uploader.upload(base64Image, {\n      <span class=\"hljs-attr\">resource_type<\/span>: <span class=\"hljs-string\">'video'<\/span>,\n      <span class=\"hljs-attr\">public_id<\/span>: <span class=\"hljs-string\">'my_video'<\/span>,\n    });\n    previousState = response.secure_url;\n    <span class=\"hljs-keyword\">return<\/span> previousState\n  } <span class=\"hljs-keyword\">catch<\/span> (error: any) {\n    <span class=\"hljs-built_in\">console<\/span>.error(error);\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Let\u2019s break down what the code snippet above does:&nbsp;<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Line<\/strong><strong>s<\/strong><strong> 3-8<\/strong><strong>.<\/strong> This part of the code imports the Cloudinary Node.js SDK (version 2) and configures it using our environment variables.<\/li>\n\n\n\n<li><strong>Line 9. <\/strong>This section accepts two arguments from <code>src\/app\/pages.tsx<\/code>: <code>previousState<\/code> and <code>formData<\/code>. The previousState argument represents the previous state of the form that will be updated, and the <code>formData<\/code> argument contains the video file from the form.<\/li>\n\n\n\n<li><strong>Line<\/strong><strong>s<\/strong><strong> 10-15<\/strong><strong>.<\/strong> This section retrieves the video file from the form data using the key \u201c<strong>video<\/strong>,\u201d converts the video file into a buffer object, and creates a Base64-encoded string representation of the video file.<\/li>\n\n\n\n<li><strong>Line<\/strong><strong>s<\/strong><strong> 17-20<\/strong><strong>.<\/strong> This snippet uploads the Base64 encoded video data to Cloudinary.<\/li>\n\n\n\n<li><strong>Line<\/strong><strong>s<\/strong><strong> 21 and 22<\/strong><strong>. <\/strong>These lines retrieve and return the secure URL of the uploaded video from the Cloudinary response.<\/li>\n\n\n\n<li><strong>Line<\/strong><strong>s<\/strong><strong> 23-25<\/strong><strong>. <\/strong>This part of the code catches any errors that might occur during the upload process.<\/li>\n<\/ul>\n\n\n\n<p>Then, return to the <code>src\/app\/pages.tsx<\/code> file and import the <code>upload.ts<\/code> function.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-comment\">\/\/ src\/app\/pages.tsx<\/span>\n<span class=\"hljs-keyword\">import<\/span> { upload } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'..\/actions\/upload'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { useFormState } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'react-dom'<\/span>;\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">Home<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n  ...\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>With all the pieces set up, let\u2019s upload the video. First, we\u2019ll select the video we want to upload, then click the Upload &nbsp;button to upload the video to Cloudinary. Upon successful upload, we&#8217;ll also need to render the video on our UI using the URL from Cloudinary\u2019s response.&nbsp;<\/p>\n\n\n\n<p>To make the uploaded video playable, we&#8217;ll use a special component from Cloudinary called <a href=\"https:\/\/next.cloudinary.dev\/cldvideoplayer\/basic-usage\" target=\"_blank\" rel=\"noreferrer noopener\">CldVideoPlayer<\/a> inside the <code>src\/app\/pages.tsx<\/code> file as shown below.&nbsp;<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-string\">\"use client\"<\/span>\n<span class=\"hljs-keyword\">import<\/span> { CldVideoPlayer } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'next-cloudinary'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'next-cloudinary\/dist\/cld-video-player.css'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { upload } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'.\/actions\/upload'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { useFormState } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'react-dom'<\/span>;\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">Home<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n  <span class=\"hljs-keyword\">const<\/span> &#91;url, formAction] = useFormState(upload, <span class=\"hljs-literal\">null<\/span>);\n<span class=\"hljs-keyword\">return<\/span>(\n    <span class=\"xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">'min-h-screen flex-col items-center justify-between p-10 mt-20'<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">h1<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">'text-xl font-bold text-center pb-10'<\/span>&gt;<\/span>How to upload a video to Cloudinary in Next.js App Router<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">h1<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">\"flex justify-center my-10 items-center\"<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">form<\/span> <span class=\"hljs-attr\">action<\/span>=<span class=\"hljs-string\">{formAction}<\/span>&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">input<\/span> <span class=\"hljs-attr\">type<\/span>=<span class=\"hljs-string\">\"file\"<\/span> <span class=\"hljs-attr\">name<\/span>=<span class=\"hljs-string\">'video'<\/span> <span class=\"hljs-attr\">accept<\/span>=<span class=\"hljs-string\">\"video\/*\"<\/span> \/&gt;<\/span>\n          <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">button<\/span> <span class=\"hljs-attr\">className<\/span>=<span class=\"hljs-string\">'bg-blue-800 text-white p-2 rounded-md'<\/span>&gt;<\/span>Upload<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">button<\/span>&gt;<\/span>\n        <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">form<\/span>&gt;<\/span>\n      <span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n    {url &amp;&amp; <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">CldVideoPlayer<\/span> <span class=\"hljs-attr\">width<\/span>=<span class=\"hljs-string\">\"860\"<\/span> <span class=\"hljs-attr\">height<\/span>=<span class=\"hljs-string\">\"470\"<\/span> <span class=\"hljs-attr\">src<\/span>=<span class=\"hljs-string\">{url}<\/span> \/&gt;<\/span>}\n  )\n}<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The useFormState hook updates the url state variable in the component with the returned video URL. Then, it displays the <code>CldVideoPlayer<\/code> component using the uploaded video URL. After applying the necessary configurations our application will look like <a href=\"https:\/\/www.loom.com\/share\/a8eecb719123446aa6cfa176bd8a17bd?sid=4ee66d0e-6a7f-441e-b29b-358b06da2b41\">this<\/a>.<\/p>\n\n\n\n<p>to show the video\u2019s upload progress.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Selecting a Suitable Routing Method<\/h2>\n\n\n\n<p>As a developer, choosing a suitable routing method depends on your needs and features. Server components and pages are great on search engines with a good initial page load speed. If a dynamic, app-like experience is the priority, we can explore the client components. With both strengths, you can combine server and client components for a hybrid approach. Ultimately, the key to making the right decision lies in understanding the strengths and limitations of both methods.&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>This blog post discussed uploading videos using Next.js App Router, a server component, and Cloudinary&#8217;s powerful video management features. Combining these, we achieved a secure solution for video uploads in our Next.js application with a great developer experience.&nbsp;<\/p>\n\n\n\n<p>You can implement other beneficial features like progress indication, improved error handling, and resumable uploads.<\/p>\n\n\n\n<p>To learn more about Cloudinary\u2019s video management features, <a target=\"_blank\" href=\"https:\/\/www.dropbox.com\/scl\/fi\/fh8cnfwki30cnzcr4exyw\/How-to-Upload-a-Video-to-Cloudinary-in-Next.js-App-Router.paper?rlkey=om22u5zwtuiobi2wyrmgvjjj7&amp;dl=0\" rel=\"noreferrer noopener\">contact us<\/a> today.<\/p>\n\n\n\n<p>If you found this post helpful and would like to learn more, feel free to join the <a target=\"_blank\" href=\"https:\/\/community.cloudinary.com\/\" rel=\"noreferrer noopener\"><u>Cloudinary Community forum<\/u><\/a> and its associated <a target=\"_blank\" href=\"https:\/\/discord.com\/invite\/cloudinary\" rel=\"noreferrer noopener\"><u>Discord<\/u><\/a>.&nbsp;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Resources<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/next.cloudinary.dev\/\" target=\"_blank\" rel=\"noreferrer noopener\">Next Cloudinary Docs<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/cloudinary.com\/documentation\/cloudinary_video_player\" target=\"_blank\" rel=\"noreferrer noopener\">Cloudinary Video Player<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/cloudinary.com\/documentation\/video_manipulation_and_delivery\" target=\"_blank\" rel=\"noreferrer noopener\">Cloudinary Video Documentation<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>This blog post introduces a developer-friendly approach to client-side media handling challenges, leveraging Next.js App Router for video uploads to Cloudinary.&nbsp; The complete source code of this project is on GitHub.&nbsp; Working With Next.js App Router In the context of video uploads, using Next.js App Router with server components offers the following advantages: Prerequisites You [&hellip;]<\/p>\n","protected":false},"author":87,"featured_media":34767,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_cloudinary_featured_overwrite":false,"footnotes":""},"categories":[1],"tags":[212,303],"class_list":["post-34764","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-next-js","tag-video"],"acf":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO Premium plugin v25.6 (Yoast SEO v26.9) - https:\/\/yoast.com\/product\/yoast-seo-premium-wordpress\/ -->\n<title>Upload a Video to Cloudinary in Next.js App Router<\/title>\n<meta name=\"description\" content=\"Read how to securely upload videos using Next.js App Router, a server component, and Cloudinary&#039;s powerful video management features.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/cloudinary.com\/blog\/upload-video-to-cloudinary-next-js-app-router\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"How to Upload a Video to Cloudinary in Next.js App Router\" \/>\n<meta property=\"og:description\" content=\"Read how to securely upload videos using Next.js App Router, a server component, and Cloudinary&#039;s powerful video management features.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/upload-video-to-cloudinary-next-js-app-router\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2024-07-17T14:00:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-03-02T14:55:25+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1720044907\/upload_video_nextjs-blog\/upload_video_nextjs-blog-jpg?_i=AA\" \/>\n\t<meta property=\"og:image:width\" content=\"2000\" \/>\n\t<meta property=\"og:image:height\" content=\"1100\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/jpeg\" \/>\n<meta name=\"author\" content=\"melindapham\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"NewsArticle\",\"@id\":\"https:\/\/cloudinary.com\/blog\/upload-video-to-cloudinary-next-js-app-router#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/upload-video-to-cloudinary-next-js-app-router\"},\"author\":{\"name\":\"melindapham\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9\"},\"headline\":\"How to Upload a Video to Cloudinary in Next.js App Router\",\"datePublished\":\"2024-07-17T14:00:00+00:00\",\"dateModified\":\"2025-03-02T14:55:25+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/upload-video-to-cloudinary-next-js-app-router\"},\"wordCount\":862,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/upload-video-to-cloudinary-next-js-app-router#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1720044907\/upload_video_nextjs-blog\/upload_video_nextjs-blog.jpg?_i=AA\",\"keywords\":[\"Next.js\",\"Video\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2024\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/upload-video-to-cloudinary-next-js-app-router\",\"url\":\"https:\/\/cloudinary.com\/blog\/upload-video-to-cloudinary-next-js-app-router\",\"name\":\"Upload a Video to Cloudinary in Next.js App Router\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/upload-video-to-cloudinary-next-js-app-router#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/upload-video-to-cloudinary-next-js-app-router#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1720044907\/upload_video_nextjs-blog\/upload_video_nextjs-blog.jpg?_i=AA\",\"datePublished\":\"2024-07-17T14:00:00+00:00\",\"dateModified\":\"2025-03-02T14:55:25+00:00\",\"description\":\"Read how to securely upload videos using Next.js App Router, a server component, and Cloudinary's powerful video management features.\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/upload-video-to-cloudinary-next-js-app-router#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/upload-video-to-cloudinary-next-js-app-router\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/upload-video-to-cloudinary-next-js-app-router#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1720044907\/upload_video_nextjs-blog\/upload_video_nextjs-blog.jpg?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1720044907\/upload_video_nextjs-blog\/upload_video_nextjs-blog.jpg?_i=AA\",\"width\":2000,\"height\":1100},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/upload-video-to-cloudinary-next-js-app-router#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"How to Upload a Video to Cloudinary in Next.js App Router\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"name\":\"Cloudinary Blog\",\"description\":\"\",\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/cloudinary.com\/blog\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Organization\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\",\"name\":\"Cloudinary Blog\",\"url\":\"https:\/\/cloudinary.com\/blog\/\",\"logo\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA\",\"width\":312,\"height\":60,\"caption\":\"Cloudinary Blog\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/\"}},{\"@type\":\"Person\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9\",\"name\":\"melindapham\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g\",\"caption\":\"melindapham\"}}]}<\/script>\n<!-- \/ Yoast SEO Premium plugin. -->","yoast_head_json":{"title":"Upload a Video to Cloudinary in Next.js App Router","description":"Read how to securely upload videos using Next.js App Router, a server component, and Cloudinary's powerful video management features.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/cloudinary.com\/blog\/upload-video-to-cloudinary-next-js-app-router","og_locale":"en_US","og_type":"article","og_title":"How to Upload a Video to Cloudinary in Next.js App Router","og_description":"Read how to securely upload videos using Next.js App Router, a server component, and Cloudinary's powerful video management features.","og_url":"https:\/\/cloudinary.com\/blog\/upload-video-to-cloudinary-next-js-app-router","og_site_name":"Cloudinary Blog","article_published_time":"2024-07-17T14:00:00+00:00","article_modified_time":"2025-03-02T14:55:25+00:00","og_image":[{"width":2000,"height":1100,"url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1720044907\/upload_video_nextjs-blog\/upload_video_nextjs-blog-jpg?_i=AA","type":"image\/jpeg"}],"author":"melindapham","twitter_card":"summary_large_image","schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"NewsArticle","@id":"https:\/\/cloudinary.com\/blog\/upload-video-to-cloudinary-next-js-app-router#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/upload-video-to-cloudinary-next-js-app-router"},"author":{"name":"melindapham","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9"},"headline":"How to Upload a Video to Cloudinary in Next.js App Router","datePublished":"2024-07-17T14:00:00+00:00","dateModified":"2025-03-02T14:55:25+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/upload-video-to-cloudinary-next-js-app-router"},"wordCount":862,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/upload-video-to-cloudinary-next-js-app-router#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1720044907\/upload_video_nextjs-blog\/upload_video_nextjs-blog.jpg?_i=AA","keywords":["Next.js","Video"],"inLanguage":"en-US","copyrightYear":"2024","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/upload-video-to-cloudinary-next-js-app-router","url":"https:\/\/cloudinary.com\/blog\/upload-video-to-cloudinary-next-js-app-router","name":"Upload a Video to Cloudinary in Next.js App Router","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/upload-video-to-cloudinary-next-js-app-router#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/upload-video-to-cloudinary-next-js-app-router#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1720044907\/upload_video_nextjs-blog\/upload_video_nextjs-blog.jpg?_i=AA","datePublished":"2024-07-17T14:00:00+00:00","dateModified":"2025-03-02T14:55:25+00:00","description":"Read how to securely upload videos using Next.js App Router, a server component, and Cloudinary's powerful video management features.","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/upload-video-to-cloudinary-next-js-app-router#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/upload-video-to-cloudinary-next-js-app-router"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/upload-video-to-cloudinary-next-js-app-router#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1720044907\/upload_video_nextjs-blog\/upload_video_nextjs-blog.jpg?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1720044907\/upload_video_nextjs-blog\/upload_video_nextjs-blog.jpg?_i=AA","width":2000,"height":1100},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/upload-video-to-cloudinary-next-js-app-router#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"How to Upload a Video to Cloudinary in Next.js App Router"}]},{"@type":"WebSite","@id":"https:\/\/cloudinary.com\/blog\/#website","url":"https:\/\/cloudinary.com\/blog\/","name":"Cloudinary Blog","description":"","publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/cloudinary.com\/blog\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Organization","@id":"https:\/\/cloudinary.com\/blog\/#organization","name":"Cloudinary Blog","url":"https:\/\/cloudinary.com\/blog\/","logo":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1649718331\/Web_Assets\/blog\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877\/cloudinary_logo_for_white_bg_1937437aa7_19374666c7_193742f877.png?_i=AA","width":312,"height":60,"caption":"Cloudinary Blog"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/logo\/image\/"}},{"@type":"Person","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9","name":"melindapham","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/e6f989fa97fe94be61596259d8629c3df65aec4c7da5c0000f90d810f313d4f4?s=96&d=mm&r=g","caption":"melindapham"}}]}},"jetpack_featured_media_url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1720044907\/upload_video_nextjs-blog\/upload_video_nextjs-blog.jpg?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/34764","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/users\/87"}],"replies":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/comments?post=34764"}],"version-history":[{"count":4,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/34764\/revisions"}],"predecessor-version":[{"id":37067,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/34764\/revisions\/37067"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/34767"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=34764"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=34764"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=34764"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}