{"id":34663,"date":"2024-07-11T07:00:00","date_gmt":"2024-07-11T14:00:00","guid":{"rendered":"https:\/\/cloudinary.com\/blog\/?p=34663"},"modified":"2025-03-09T07:26:02","modified_gmt":"2025-03-09T14:26:02","slug":"video-snipper-tiktok-next-js-app-router","status":"publish","type":"post","link":"https:\/\/cloudinary.com\/blog\/video-snipper-tiktok-next-js-app-router","title":{"rendered":"Create a Video Snipper for TikTok in Next.js App Router"},"content":{"rendered":"\n<p>TikTok is one of the biggest social media platforms in the world for sharing video content. Users create short-form videos to entertain their viewers, but they often have to find tools to customize and resize their videos to suit TikTok. Imagine building a tool that allows users to customize their videos for the app.<\/p>\n\n\n\n<p>In this blog post, we\u2019ll learn how to build a simple web app in Next.js that can help users customize their videos to fit platforms like TikTok using Cloudinary\u2019s video management functionalities.&nbsp;<\/p>\n\n\n\n<p>A basic understanding of TypeScript or JavaScript is enough to follow along with this blog post. You\u2019ll also need a Cloudinary account; <a target=\"_blank\" href=\"https:\/\/cloudinary.com\/users\/register_free\" rel=\"noreferrer noopener\">create one for free.<\/a> Also, you can find the completed app in this <a target=\"_blank\" href=\"https:\/\/github.com\/folucode\/cloudinary-video-snipper\" rel=\"noreferrer noopener\">GitHub <\/a><a target=\"_blank\" href=\"https:\/\/github.com\/folucode\/cloudinary-video-snipper\" rel=\"noreferrer noopener\">r<\/a><a target=\"_blank\" href=\"https:\/\/github.com\/folucode\/cloudinary-video-snipper\" rel=\"noreferrer noopener\">epository<\/a>.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Set Up a Next.js App<\/h1>\n\n\n\n<p>Before we start building the core functionality, let\u2019s set up the Next.js project. We\u2018ll use the Next.js app router for this project because of its modern features, performance, and ability to create server components and actions.<\/p>\n\n\n\n<p>To begin, clone the starter project using the command below:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php shcb-wrap-lines\">$ git <span class=\"hljs-keyword\">clone<\/span> https:<span class=\"hljs-comment\">\/\/github.com\/folucode\/cloudinary-video-snipper-starter.git<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><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>Next, navigate to the cloned project directory and install the dependencies using any package manager, either npm install or yarn.<\/p>\n\n\n\n<p>The starter project folder already has all the styles and components we need to build this app, enabling us to focus on building the core functionality.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Set Up Environment Variables<\/h1>\n\n\n\n<p>In the project\u2019s root directory, add a new file, .env.local, and paste the code below in that file. This file will house all the app keys we need to use Cloudinary.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\">CLOUDINARY_CLOUD_NAME=<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">CLOUDINARY_CLOUD_NAME<\/span>&gt;<\/span>\nCLOUDINARY_API_KEY=<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">CLOUDINARY_API_KEY<\/span>&gt;<\/span>\nCLOUDINARY_API_SECRET=<span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">CLOUDINARY_API_SECRET<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><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\n<p>To get these details, click this <a href=\"https:\/\/console.cloudinary.com\/pm\/c-44312a85dae383cdb3fedab809bc64\/developer-dashboard\" target=\"_blank\" rel=\"noreferrer noopener\">link<\/a> to navigate to the Cloudinary developer dashboard. We can see all the details under the <strong>Product Environment Credentials<\/strong>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img width=\"1024\" height=\"207\" data-public-id=\"Web_Assets\/blog\/blog-Create-a-Video-Snipper-for-TikTok-in-Next.js-App-Router-1\/blog-Create-a-Video-Snipper-for-TikTok-in-Next.js-App-Router-1.png\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/w_1024,h_207,c_scale\/f_auto,q_auto\/v1720719450\/Web_Assets\/blog\/blog-Create-a-Video-Snipper-for-TikTok-in-Next.js-App-Router-1\/blog-Create-a-Video-Snipper-for-TikTok-in-Next.js-App-Router-1.png?_i=AA\" alt=\"\" class=\"wp-post-34663 wp-image-34664\" data-format=\"png\" data-transformations=\"f_auto,q_auto\" data-version=\"1720719450\" data-seo=\"1\" srcset=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1720719450\/Web_Assets\/blog\/blog-Create-a-Video-Snipper-for-TikTok-in-Next.js-App-Router-1\/blog-Create-a-Video-Snipper-for-TikTok-in-Next.js-App-Router-1.png?_i=AA 1600w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1720719450\/Web_Assets\/blog\/blog-Create-a-Video-Snipper-for-TikTok-in-Next.js-App-Router-1\/blog-Create-a-Video-Snipper-for-TikTok-in-Next.js-App-Router-1.png?_i=AA 300w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1720719450\/Web_Assets\/blog\/blog-Create-a-Video-Snipper-for-TikTok-in-Next.js-App-Router-1\/blog-Create-a-Video-Snipper-for-TikTok-in-Next.js-App-Router-1.png?_i=AA 768w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1720719450\/Web_Assets\/blog\/blog-Create-a-Video-Snipper-for-TikTok-in-Next.js-App-Router-1\/blog-Create-a-Video-Snipper-for-TikTok-in-Next.js-App-Router-1.png?_i=AA 1024w, https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1720719450\/Web_Assets\/blog\/blog-Create-a-Video-Snipper-for-TikTok-in-Next.js-App-Router-1\/blog-Create-a-Video-Snipper-for-TikTok-in-Next.js-App-Router-1.png?_i=AA 1536w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h1 class=\"wp-block-heading\">Build App Functionality<\/h1>\n\n\n\n<p>Now that we\u2019re all set up, let\u2019s start building the functionality. First, we want to set up our Cloudinary library. To do this, we\u2019ll add a new folder called <code>lib<\/code> in the <code>src<\/code> folder; in that folder, we add a file and name it <code>cloudinary.ts<\/code>. In the <code>cloudinary.ts<\/code> file, we\u2019ll set up Cloudinary like so:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">import<\/span> { v2 <span class=\"hljs-keyword\">as<\/span> cloudinary } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'cloudinary'<\/span>;\n\ncloudinary.config({\n  <span class=\"hljs-attr\">cloud_name<\/span>: process.env.CLOUDINARY_CLOUD_NAME,\n  <span class=\"hljs-attr\">api_key<\/span>: process.env.CLOUDINARY_API_KEY,\n  <span class=\"hljs-attr\">api_secret<\/span>: process.env.CLOUDINARY_API_SECRET,\n});\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-keyword\">default<\/span> cloudinary;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>In the code above, we\u2019ll import the Cloudinary library. The <strong>v2<\/strong> version is being used and aliased as <strong>cloudinary<\/strong>. The <strong>cloudinary.config()<\/strong> method is called to configure the Cloudinary client with the account details we retrieved from our dashboard in the last section.<\/p>\n\n\n\n<p>We\u2019ll then export the configured <strong>Cloudinary<\/strong> object as this module&#8217;s default export. This setup allows other parts of our application to import and use the configured Cloudinary client without configuring it again.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Get Form Data<\/h2>\n\n\n\n<p>Before we begin, add these import statements to the top of the <code>VideoUploadForm.tsx<\/code> file.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\"><span class=\"hljs-keyword\">import<\/span> { redirect } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'next\/navigation'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> cloudinary <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'..\/lib\/cloudinary'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { UploadApiResponse } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'cloudinary'<\/span>;<\/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>There\u2019s a function called <strong>upload<\/strong> in the <code>VideoUploadForm.tsx<\/code> file in the app folder under the src folder. In that <strong>upload<\/strong> function, the first step is to get the data from the form.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml shcb-wrap-lines\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span> <span class=\"hljs-attr\">style<\/span>=<span class=\"hljs-string\">\"background-color: initial; color: inherit; font-size: inherit; text-wrap: wrap;\"<\/span>&gt;<\/span>\u00a0 'use server';<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">code<\/span> <span class=\"hljs-attr\">spellcheck<\/span>=<span class=\"hljs-string\">\"false\"<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"listtype-code listindent1 list-code1 lang-typescript\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">code<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">code<\/span> <span class=\"hljs-attr\">spellcheck<\/span>=<span class=\"hljs-string\">\"false\"<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"listtype-code listindent1 list-code1 lang-typescript\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\" author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qovwitz89zz76zcsz82zz84zluz65zz73zz84zz69zwpz71zcz87z0z73zz70zhz82ziz90z4oz78zz82z\"<\/span>&gt;<\/span>\u00a0 const file = <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">i<\/span>&gt;<\/span>formData<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">i<\/span>&gt;<\/span>.get('video') as File;\u00a0 const start = <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">i<\/span>&gt;<\/span>formData<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">i<\/span>&gt;<\/span>.get('start') as string;\u00a0 const end = <span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">i<\/span>&gt;<\/span>formData<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">i<\/span>&gt;<\/span>.get('end') as string;<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">code<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">code<\/span> <span class=\"hljs-attr\">spellcheck<\/span>=<span class=\"hljs-string\">\"false\"<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"listtype-code listindent1 list-code1 lang-typescript\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">code<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">code<\/span> <span class=\"hljs-attr\">spellcheck<\/span>=<span class=\"hljs-string\">\"false\"<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"listtype-code listindent1 list-code1 lang-typescript\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\" author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qovwitz89zz76zcsz82zz84zluz65zz73zz84zz69zwpz71zcz87z0z73zz70zhz82ziz90z4oz78zz82z\"<\/span>&gt;<\/span>\u00a0 const buffer: Buffer = Buffer.from(await file.arrayBuffer());<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">code<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">code<\/span> <span class=\"hljs-attr\">spellcheck<\/span>=<span class=\"hljs-string\">\"false\"<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"listtype-code listindent1 list-code1 lang-typescript\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">code<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">code<\/span> <span class=\"hljs-attr\">spellcheck<\/span>=<span class=\"hljs-string\">\"false\"<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\"listtype-code listindent1 list-code1 lang-typescript\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">span<\/span> <span class=\"hljs-attr\">class<\/span>=<span class=\"hljs-string\">\" author-d-1gg9uz65z1iz85zgdz68zmqkz84zo2qovwitz89zz76zcsz82zz84zluz65zz73zz84zz69zwpz71zcz87z0z73zz70zhz82ziz90z4oz78zz82z\"<\/span>&gt;<\/span>\u00a0 let url: string = '';<span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">span<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">code<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><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\n<p>Since we\u2019re using the latest version of Next.js, all components are server components by default. If we want to run an asynchronous function on the server instead of the client, we\u2019ll use Next.js <a target=\"_blank\" href=\"https:\/\/nextjs.org\/docs\/app\/building-your-application\/data-fetching\/server-actions-and-mutations\" rel=\"noreferrer noopener\">server actions<\/a>. Placing the <strong>use server<\/strong> directive at the top of the function signals to Next.js that the enclosed code is meant to run on the server. This prevents the function from being exposed to the client, enhancing security and performance.<\/p>\n\n\n\n<p>We\u2019ll then move on to handling the data from the form submission. First, we\u2019ll retrieve the video file from the form data and cast it as a <strong>File<\/strong> object. Then, we\u2019ll get the start and end times from the form, casting these values as strings.&nbsp;<\/p>\n\n\n\n<p>Next, we\u2019ll convert the video file into a binary <strong>Buffer<\/strong> by reading the file&#8217;s content as an <strong>ArrayBuffer<\/strong> and then converting this Buffer into a <strong>Buffer<\/strong> object using <strong>Buffer.from()<\/strong>. This conversion is necessary for efficiently handling the binary data of the video file on the server.&nbsp;<\/p>\n\n\n\n<p>Finally, we\u2019ll initialize an empty string variable <strong>url<\/strong>, which will store the URL of the processed video.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Upload and Process the Video<\/h2>\n\n\n\n<p>The next step is to upload the video file from the form data to Cloudinary, apply specific transformations, and then redirect our app to the processed video URL.&nbsp;<\/p>\n\n\n\n<p>The process will be done within a <strong>try-catch<\/strong> block in the <strong>upload <\/strong>function, in the <code>VideoUploadForm.tsx<\/code> file, which ensures that any errors during the upload process are caught and logged for debugging purposes.<\/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\">\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\n  <span class=\"hljs-keyword\">const<\/span> uploadResult: UploadApiResponse = <span class=\"hljs-keyword\">await<\/span> cloudinary.uploader.upload(\n    base64Image,\n    {\n      <span class=\"hljs-attr\">resource_type<\/span>: <span class=\"hljs-string\">'video'<\/span>,\n      <span class=\"hljs-attr\">transformation<\/span>: &#91;\n        { <span class=\"hljs-attr\">start_offset<\/span>: start, <span class=\"hljs-attr\">end_offset<\/span>: end },\n        { <span class=\"hljs-attr\">aspect_ratio<\/span>: <span class=\"hljs-string\">'9:16'<\/span>, <span class=\"hljs-attr\">crop<\/span>: <span class=\"hljs-string\">'fill'<\/span> },\n      ],\n      <span class=\"hljs-attr\">public_id<\/span>: <span class=\"hljs-string\">`video-snippets\/<span class=\"hljs-subst\">${<span class=\"hljs-built_in\">Date<\/span>.now()}<\/span>`<\/span>,\n    }\n  );\n\n  url = uploadResult.secure_url;\n } <span class=\"hljs-keyword\">catch<\/span> (error: any) {\n  <span class=\"hljs-built_in\">console<\/span>.error(error);\n}\n\nredirect(url);<\/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>First, we\u2019ll convert the video file from Buffer into a Base64-encoded string. We\u2019ll construct the string using the video file&#8217;s MIME type (file.type) and binary data (Buffer). The buffer object, previously created by converting the video file to a binary buffer, is transformed into a Base64 string using <strong>Buffer.toString(&#8216;base64&#8217;)<\/strong>. We\u2019ll then prefix the Base64 string with the data URI scheme to form a valid Base64 image string, which is necessary for uploading to Cloudinary.<\/p>\n\n\n\n<p>Next, upload the Base64-encoded video to Cloudinary using the upload method we set up from the Cloudinary client (<code>cloudinary.uploader.upload<\/code>). This method takes two main parameters: the Base64 string of the video and an options object. Within the options object, the <strong>resource_type<\/strong> is set to <code>video<\/code> to indicate that the file being uploaded is a video.\u00a0<\/p>\n\n\n\n<p>The <strong>transformation<\/strong> option is where we do the actual transformation of the video. In this case, we\u2019ll trim the video to the specified start and end times (<code>{ start_offset: start, end_offset: end }<\/code>) gotten from the form data, and we adjust its aspect ratio to 9:16 while cropping it to fill the frame (<code>{ aspect_ratio: '9:16', crop: 'fill' }<\/code>). This aspect ratio is used in TikTok videos.<\/p>\n\n\n\n<p>Additionally, we\u2019ll set <strong>public_id<\/strong> to a unique identifier generated using the current timestamp to ensure that each uploaded video has a unique identifier in Cloudinary.&nbsp;<\/p>\n\n\n\n<p>After the video has successfully uploaded, the result returned by Cloudinary (<code>uploadResult<\/code>) includes the secure URL (<code>uploadResult.secure_url<\/code>) pointing to the uploaded video. We\u2019ll then store this URL in the url variable.\u00a0<\/p>\n\n\n\n<p>If errors occur during this process, they\u2019re caught in the catch block and logged to the console.<\/p>\n\n\n\n<p>Finally, we\u2019ll redirect to the uploaded video&#8217;s URL using the redirect (<code>url<\/code>) function.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Final Code Snippet<\/h2>\n\n\n\n<p>Our final code snippet in the <code>VideoUploadForm.tsx<\/code> file should look like this:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript shcb-wrap-lines\">\n<span class=\"hljs-keyword\">import<\/span> { redirect } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'next\/navigation'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> cloudinary <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'..\/lib\/cloudinary'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { UploadApiResponse } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'cloudinary'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> { SubmitButton } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'@\/app\/submit-button'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> formStyles <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">'.\/form.module.css'<\/span>;\n\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\">VideoUploadForm<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n  <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\">formData: FormData<\/span>) <\/span>{\n    <span class=\"hljs-string\">'use server'<\/span>;\n\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> start = formData.get(<span class=\"hljs-string\">'start'<\/span>) <span class=\"hljs-keyword\">as<\/span> string;\n    <span class=\"hljs-keyword\">const<\/span> end = formData.get(<span class=\"hljs-string\">'end'<\/span>) <span class=\"hljs-keyword\">as<\/span> string;\n    <span class=\"hljs-keyword\">const<\/span> buffer: Buffer = Buffer.from(<span class=\"hljs-keyword\">await<\/span> file.arrayBuffer());\n\n    <span class=\"hljs-keyword\">let<\/span> url: string = <span class=\"hljs-string\">''<\/span>;\n\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\n      <span class=\"hljs-keyword\">const<\/span> uploadResult: UploadApiResponse = <span class=\"hljs-keyword\">await<\/span> cloudinary.uploader.upload(\n        base64Image,\n        {\n          <span class=\"hljs-attr\">resource_type<\/span>: <span class=\"hljs-string\">'video'<\/span>,\n          <span class=\"hljs-attr\">transformation<\/span>: &#91;\n            { <span class=\"hljs-attr\">start_offset<\/span>: start, <span class=\"hljs-attr\">end_offset<\/span>: end },\n            { <span class=\"hljs-attr\">aspect_ratio<\/span>: <span class=\"hljs-string\">'9:16'<\/span>, <span class=\"hljs-attr\">crop<\/span>: <span class=\"hljs-string\">'fill'<\/span> },\n          ],\n          <span class=\"hljs-attr\">public_id<\/span>: <span class=\"hljs-string\">`video-snippets\/<span class=\"hljs-subst\">${<span class=\"hljs-built_in\">Date<\/span>.now()}<\/span>`<\/span>,\n        }\n      );\n\n      url = uploadResult.secure_url;\n    } <span class=\"hljs-keyword\">catch<\/span> (error: any) {\n      <span class=\"hljs-built_in\">console<\/span>.error(error);\n    }\n    redirect(url);\n  }\n\n  <span class=\"hljs-keyword\">return<\/span> (\n  ...\n  );\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h1 class=\"wp-block-heading\">The Final Result<\/h1>\n\n\n\n<p>When we successfully run the application, we can select a video to upload and choose the start and end times to crop. The video is then sent to Cloudinary, and the crop transformation is done. Once that\u2019s completed, our app will redirect to the transformed video.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img width=\"800\" height=\"452\" data-public-id=\"Web_Assets\/blog\/blog-Create-a-Video-Snipper-for-TikTok-in-Next.js-App-Router-2\/blog-Create-a-Video-Snipper-for-TikTok-in-Next.js-App-Router-2.gif\" loading=\"lazy\" decoding=\"async\" src=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/w_800,h_452,c_scale\/f_auto,q_auto\/v1720719447\/Web_Assets\/blog\/blog-Create-a-Video-Snipper-for-TikTok-in-Next.js-App-Router-2\/blog-Create-a-Video-Snipper-for-TikTok-in-Next.js-App-Router-2.gif?_i=AA\" alt=\"\" class=\"wp-post-34663 wp-image-34665\" data-format=\"gif\" data-transformations=\"f_auto,q_auto\" data-version=\"1720719447\" data-seo=\"1\" \/><\/figure>\n\n\n\n<p>Here\u2019s the <a target=\"_blank\" href=\"https:\/\/github.com\/folucode\/cloudinary-video-snipper\" rel=\"noreferrer noopener\">complete source code<\/a>.<\/p>\n\n\n\n<h1 class=\"wp-block-heading\">Conclusion<\/h1>\n\n\n\n<p>In this blog post, you learned how to use Next.js and the powerful media tools provided by Cloudinary to build a simple TikTok video-snipping app. The Cloudinary video management library is an incredible resource for even more video transformations.&nbsp;<\/p>\n\n\n\n<p>Try extending this app to have a dropdown list of video aspect ratios for other video platforms. Users can select a platform and video quality and the video is transformed to fit that specification.<\/p>\n\n\n\n<p>If you enjoyed this post and want to discuss it in more detail, feel free to join the the <a target=\"_blank\" href=\"https:\/\/community.cloudinary.com\/\" rel=\"noreferrer noopener\">Cloudinary Community forum<\/a> and its associated <a target=\"_blank\" href=\"https:\/\/discord.com\/invite\/cloudinary\" rel=\"noreferrer noopener\">Discord<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>TikTok is one of the biggest social media platforms in the world for sharing video content. Users create short-form videos to entertain their viewers, but they often have to find tools to customize and resize their videos to suit TikTok. Imagine building a tool that allows users to customize their videos for the app. In [&hellip;]<\/p>\n","protected":false},"author":87,"featured_media":34667,"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,304],"class_list":["post-34663","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-next-js","tag-video","tag-video-transformation"],"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>Create a Video Snipper for TikTok in Next.js App Router<\/title>\n<meta name=\"description\" content=\"Follow step-by-step instructions to set up a Next.js app, integrate Cloudinary, and handle video uploads and transformations for TikTok.\" \/>\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\/video-snipper-tiktok-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=\"Create a Video Snipper for TikTok in Next.js App Router\" \/>\n<meta property=\"og:description\" content=\"Follow step-by-step instructions to set up a Next.js app, integrate Cloudinary, and handle video uploads and transformations for TikTok.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/cloudinary.com\/blog\/video-snipper-tiktok-next-js-app-router\" \/>\n<meta property=\"og:site_name\" content=\"Cloudinary Blog\" \/>\n<meta property=\"article:published_time\" content=\"2024-07-11T14:00:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2025-03-09T14:26:02+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1720031861\/cldn_nextjs_tiktok_videos-blog\/cldn_nextjs_tiktok_videos-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\/video-snipper-tiktok-next-js-app-router#article\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/video-snipper-tiktok-next-js-app-router\"},\"author\":{\"name\":\"melindapham\",\"@id\":\"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9\"},\"headline\":\"Create a Video Snipper for TikTok in Next.js App Router\",\"datePublished\":\"2024-07-11T14:00:00+00:00\",\"dateModified\":\"2025-03-09T14:26:02+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/video-snipper-tiktok-next-js-app-router\"},\"wordCount\":1204,\"publisher\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#organization\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/video-snipper-tiktok-next-js-app-router#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1720031861\/cldn_nextjs_tiktok_videos-blog\/cldn_nextjs_tiktok_videos-blog.jpg?_i=AA\",\"keywords\":[\"Next.js\",\"Video\",\"Video Transformation\"],\"inLanguage\":\"en-US\",\"copyrightYear\":\"2024\",\"copyrightHolder\":{\"@id\":\"https:\/\/cloudinary.com\/#organization\"}},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/cloudinary.com\/blog\/video-snipper-tiktok-next-js-app-router\",\"url\":\"https:\/\/cloudinary.com\/blog\/video-snipper-tiktok-next-js-app-router\",\"name\":\"Create a Video Snipper for TikTok in Next.js App Router\",\"isPartOf\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/video-snipper-tiktok-next-js-app-router#primaryimage\"},\"image\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/video-snipper-tiktok-next-js-app-router#primaryimage\"},\"thumbnailUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1720031861\/cldn_nextjs_tiktok_videos-blog\/cldn_nextjs_tiktok_videos-blog.jpg?_i=AA\",\"datePublished\":\"2024-07-11T14:00:00+00:00\",\"dateModified\":\"2025-03-09T14:26:02+00:00\",\"description\":\"Follow step-by-step instructions to set up a Next.js app, integrate Cloudinary, and handle video uploads and transformations for TikTok.\",\"breadcrumb\":{\"@id\":\"https:\/\/cloudinary.com\/blog\/video-snipper-tiktok-next-js-app-router#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/cloudinary.com\/blog\/video-snipper-tiktok-next-js-app-router\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/cloudinary.com\/blog\/video-snipper-tiktok-next-js-app-router#primaryimage\",\"url\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1720031861\/cldn_nextjs_tiktok_videos-blog\/cldn_nextjs_tiktok_videos-blog.jpg?_i=AA\",\"contentUrl\":\"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1720031861\/cldn_nextjs_tiktok_videos-blog\/cldn_nextjs_tiktok_videos-blog.jpg?_i=AA\",\"width\":2000,\"height\":1100},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/cloudinary.com\/blog\/video-snipper-tiktok-next-js-app-router#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/cloudinary.com\/blog\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Create a Video Snipper for TikTok 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":"Create a Video Snipper for TikTok in Next.js App Router","description":"Follow step-by-step instructions to set up a Next.js app, integrate Cloudinary, and handle video uploads and transformations for TikTok.","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\/video-snipper-tiktok-next-js-app-router","og_locale":"en_US","og_type":"article","og_title":"Create a Video Snipper for TikTok in Next.js App Router","og_description":"Follow step-by-step instructions to set up a Next.js app, integrate Cloudinary, and handle video uploads and transformations for TikTok.","og_url":"https:\/\/cloudinary.com\/blog\/video-snipper-tiktok-next-js-app-router","og_site_name":"Cloudinary Blog","article_published_time":"2024-07-11T14:00:00+00:00","article_modified_time":"2025-03-09T14:26:02+00:00","og_image":[{"width":2000,"height":1100,"url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/v1720031861\/cldn_nextjs_tiktok_videos-blog\/cldn_nextjs_tiktok_videos-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\/video-snipper-tiktok-next-js-app-router#article","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/video-snipper-tiktok-next-js-app-router"},"author":{"name":"melindapham","@id":"https:\/\/cloudinary.com\/blog\/#\/schema\/person\/0d5ad601e4c3b5be89245dfb14be42d9"},"headline":"Create a Video Snipper for TikTok in Next.js App Router","datePublished":"2024-07-11T14:00:00+00:00","dateModified":"2025-03-09T14:26:02+00:00","mainEntityOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/video-snipper-tiktok-next-js-app-router"},"wordCount":1204,"publisher":{"@id":"https:\/\/cloudinary.com\/blog\/#organization"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/video-snipper-tiktok-next-js-app-router#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1720031861\/cldn_nextjs_tiktok_videos-blog\/cldn_nextjs_tiktok_videos-blog.jpg?_i=AA","keywords":["Next.js","Video","Video Transformation"],"inLanguage":"en-US","copyrightYear":"2024","copyrightHolder":{"@id":"https:\/\/cloudinary.com\/#organization"}},{"@type":"WebPage","@id":"https:\/\/cloudinary.com\/blog\/video-snipper-tiktok-next-js-app-router","url":"https:\/\/cloudinary.com\/blog\/video-snipper-tiktok-next-js-app-router","name":"Create a Video Snipper for TikTok in Next.js App Router","isPartOf":{"@id":"https:\/\/cloudinary.com\/blog\/#website"},"primaryImageOfPage":{"@id":"https:\/\/cloudinary.com\/blog\/video-snipper-tiktok-next-js-app-router#primaryimage"},"image":{"@id":"https:\/\/cloudinary.com\/blog\/video-snipper-tiktok-next-js-app-router#primaryimage"},"thumbnailUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1720031861\/cldn_nextjs_tiktok_videos-blog\/cldn_nextjs_tiktok_videos-blog.jpg?_i=AA","datePublished":"2024-07-11T14:00:00+00:00","dateModified":"2025-03-09T14:26:02+00:00","description":"Follow step-by-step instructions to set up a Next.js app, integrate Cloudinary, and handle video uploads and transformations for TikTok.","breadcrumb":{"@id":"https:\/\/cloudinary.com\/blog\/video-snipper-tiktok-next-js-app-router#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/cloudinary.com\/blog\/video-snipper-tiktok-next-js-app-router"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/cloudinary.com\/blog\/video-snipper-tiktok-next-js-app-router#primaryimage","url":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1720031861\/cldn_nextjs_tiktok_videos-blog\/cldn_nextjs_tiktok_videos-blog.jpg?_i=AA","contentUrl":"https:\/\/res.cloudinary.com\/cloudinary-marketing\/images\/f_auto,q_auto\/v1720031861\/cldn_nextjs_tiktok_videos-blog\/cldn_nextjs_tiktok_videos-blog.jpg?_i=AA","width":2000,"height":1100},{"@type":"BreadcrumbList","@id":"https:\/\/cloudinary.com\/blog\/video-snipper-tiktok-next-js-app-router#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/cloudinary.com\/blog\/"},{"@type":"ListItem","position":2,"name":"Create a Video Snipper for TikTok 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\/v1720031861\/cldn_nextjs_tiktok_videos-blog\/cldn_nextjs_tiktok_videos-blog.jpg?_i=AA","_links":{"self":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/34663","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=34663"}],"version-history":[{"count":3,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/34663\/revisions"}],"predecessor-version":[{"id":37179,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/posts\/34663\/revisions\/37179"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media\/34667"}],"wp:attachment":[{"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/media?parent=34663"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/categories?post=34663"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/cloudinary.com\/blog\/wp-json\/wp\/v2\/tags?post=34663"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}