<?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: Shivam Katare</title>
    <description>The latest articles on DEV Community by Shivam Katare (@shivamkatare).</description>
    <link>https://dev.to/shivamkatare</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%2F1856627%2F345b2399-a56f-4815-abc4-d99056621299.jpg</url>
      <title>DEV Community: Shivam Katare</title>
      <link>https://dev.to/shivamkatare</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/shivamkatare"/>
    <language>en</language>
    <item>
      <title>Make User Management easier with Clerk + Supabase 🔥🔥</title>
      <dc:creator>Shivam Katare</dc:creator>
      <pubDate>Tue, 03 Dec 2024 14:51:50 +0000</pubDate>
      <link>https://dev.to/shivamkatare/make-user-management-easier-with-clerk-supabase-3am1</link>
      <guid>https://dev.to/shivamkatare/make-user-management-easier-with-clerk-supabase-3am1</guid>
      <description>&lt;p&gt;Handling user authentication and connecting to a database can be tough, especially with modern web apps. Clerk and Supabase are two great tools that make this process easier, providing smooth user management and scalable database options. In this article, we’ll learn, how to connect Clerk's authentication system with Supabase to build a strong and secure user management process.&lt;/p&gt;

&lt;p&gt;So, without wasting a bit, let’s get started.&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%2F02jwasdcdtb96aiofxu9.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%2F02jwasdcdtb96aiofxu9.png" alt="lets go" width="309" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Initial Setup
&lt;/h2&gt;

&lt;p&gt;The first thing that, we'll do is set up our Next.js project and install the necessary dependencies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Install Dependencies
&lt;/h3&gt;

&lt;p&gt;Run the following command to install Next.js on your machine.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx create-next-app@latest 

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

&lt;/div&gt;



&lt;p&gt;On installation, you'll see the following prompts:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;
What is your project named? my-app
Would you like to use TypeScript? No / Yes   =&amp;gt; I picked no
Would you like to use ESLint? No / Yes =&amp;gt; I picked yes
Would you like to use Tailwind CSS? No / Yes =&amp;gt; I picked yes
Would you like your code inside a `src/` directory? No / Yes  =&amp;gt; I picked no
Would you like to use App Router? (recommended) No / Yes  =&amp;gt; I picked yes
Would you like to use Turbopack for `next dev`?  No / Yes  =&amp;gt; I picked no
Would you like to customize the import alias (`@/*` by default)? No / Yes =&amp;gt; I picked no
What import alias would you like configured? @/* =&amp;gt; I picked @

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

&lt;/div&gt;



&lt;p&gt;Our next js has been installed, now let’s install clerk and supabase. Run this command to install clerk👇&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i @clerk/nextjs

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

&lt;/div&gt;



&lt;p&gt;and this one to install supabase👇&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm i @supabase/supabase-js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We've installed the necessary dependencies, so now let's set up the Supabase and Clerk dashboards to ensure they work together.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup Clerk Dashboard
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dashboard.clerk.com/sign-up?_gl=1*zxnoin*_gcl_au*MTczMTU2NTY0Ni4xNzMxOTI4MTM2*_ga*NzI4MzAxMTAxLjE3MzE5Mjk3MjU.*_ga_1WMF5X234K*MTczMzE4MjI1MS4xNi4xLjE3MzMxODMxOTUuMC4wLjA." rel="noopener noreferrer"&gt;Click here&lt;/a&gt; to sign up for Clerk or &lt;a href="https://dashboard.clerk.com/?_gl=1*1eca4w6*_gcl_au*MTczMTU2NTY0Ni4xNzMxOTI4MTM2*_ga*NzI4MzAxMTAxLjE3MzE5Mjk3MjU.*_ga_1WMF5X234K*MTczMzE4MjI1MS4xNi4xLjE3MzMxODMxOTUuMC4wLjA." rel="noopener noreferrer"&gt;sign in&lt;/a&gt; to your existing Clerk account.&lt;/p&gt;

&lt;p&gt;If you've just created an account for the first time, you'll go straight to the authentication setup form.&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%2F087306qs66w2pgxo8hns.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%2F087306qs66w2pgxo8hns.png" alt="clerk form" width="800" height="549"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If not, you'll be sent to your Clerk Dashboard. To make a new app, click on the Create application card to access the authentication setup form. Fill in all the details and also select social auth providers like Google, Github, Facebook, etc. whatever you want.&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%2F8lxhb2wl04d19xmyfhto.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%2F8lxhb2wl04d19xmyfhto.png" alt="clerk setup" width="800" height="589"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It will create your new clerk application on which you can start adding your users with their auth flow. Your dashboard will look like this. 👇&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%2Fye5ezmnj5r9vjcmgey7p.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%2Fye5ezmnj5r9vjcmgey7p.png" alt="Image description" width="800" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That’s it, clerk setup is done now let’s set up Supabase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup Supabase Dashboard
&lt;/h2&gt;

&lt;p&gt;Go to &lt;a href="https://supabase.com/" rel="noopener noreferrer"&gt;this link&lt;/a&gt; to sign in to your Supabase dashboard.&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%2Fkyaxyejzqx00a1qmalhj.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%2Fkyaxyejzqx00a1qmalhj.png" alt="supabase" width="800" height="357"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, go to &lt;a href="https://database.new/" rel="noopener noreferrer"&gt;this link&lt;/a&gt; to create a new Supabase project. Once your project is created, it should be visible in your &lt;a href="https://supabase.com/dashboard/projects" rel="noopener noreferrer"&gt;dashboard&lt;/a&gt; like this.&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%2Fuplqvnlx6y3y8zhbadp2.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%2Fuplqvnlx6y3y8zhbadp2.png" alt="Image description" width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Our supabase dashboard is also set. Now, let’s see the integration part.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integrate Clerk
&lt;/h2&gt;

&lt;p&gt;We've installed all our dependencies and set up both dashboards. Now, we'll integrate Clerk and Supabase into our app, one by one. We'll start with Clerk first, then move on to Supabase.&lt;/p&gt;

&lt;p&gt;Before moving forward, let's look at the folder structure. I have added a few new files and folders that we will use later in this article.&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%2Fqecz0a69quzwt2eo521h.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%2Fqecz0a69quzwt2eo521h.png" alt="Image description" width="367" height="623"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Set Clerk Provider
&lt;/h3&gt;

&lt;p&gt;The first thing we need to do is wrap our app in a &lt;code&gt;&amp;lt;ClerkProvider /&amp;gt;&lt;/code&gt; so it can manage our auth state from the top of the hierarchy. The &lt;code&gt;&amp;lt;ClerkProvider&amp;gt;&lt;/code&gt; component wraps your app to provide active session and user context to Clerk's hooks and other components.&lt;/p&gt;

&lt;p&gt;Head over to your &lt;code&gt;layout.js&lt;/code&gt; file, and wrap your main component into &lt;code&gt;&amp;lt;ClerkProvider/&amp;gt;&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;export default function RootLayout({ children }) {
  return (
    &amp;lt;ClerkProvider touchSession={false} &amp;gt;
    &amp;lt;html lang="en"&amp;gt;
      &amp;lt;body
        className={`${geistSans.variable} ${geistMono.variable} 
          antialiased`}
      &amp;gt;
        {children}
      &amp;lt;/body&amp;gt;
    &amp;lt;/html&amp;gt;
    &amp;lt;/ClerkProvider&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ClerkProvider&lt;/code&gt; has several props. As you can see, I’ve added one prop called &lt;code&gt;touchSession&lt;/code&gt;. By default, it's &lt;code&gt;true&lt;/code&gt;, but you can set it to &lt;code&gt;false&lt;/code&gt;. When it's &lt;code&gt;false&lt;/code&gt;, it won't check the user session on every click. Otherwise, it will call an API each time to see if the user’s session is active. For a full list of &lt;code&gt;ClerkProvider&lt;/code&gt; props, you can check it &lt;a href="https://clerk.com/docs/components/clerk-provider#properties" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now, let's add sign-in and sign-up buttons. Clerk provides its own UI and components to make this process easier, 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;import localFont from 'next/font/local';
import './globals.css';
import {
  ClerkProvider,
  SignedIn,
  SignedOut,
  SignInButton,
  UserButton,
} from '@clerk/nextjs';

// rest of your code

export default function RootLayout({ children }) {
  return (
    &amp;lt;ClerkProvider touchSession={false}&amp;gt;
      &amp;lt;html lang="en"&amp;gt;
        &amp;lt;body
          className={`${geistSans.variable} ${geistMono.variable} antialiased`}
        &amp;gt;
         &amp;lt;header&amp;gt;  // here you can use styling to style it a/c to your design
          &amp;lt;SignedOut&amp;gt;
            &amp;lt;SignInButton /&amp;gt;
          &amp;lt;/SignedOut&amp;gt;
          &amp;lt;SignedIn&amp;gt;
            &amp;lt;UserButton /&amp;gt;
          &amp;lt;/SignedIn&amp;gt;
        &amp;lt;/header&amp;gt;
          {children}
        &amp;lt;/body&amp;gt;
      &amp;lt;/html&amp;gt;
    &amp;lt;/ClerkProvider&amp;gt;
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it, now let’s set up env variables.&lt;/p&gt;

&lt;h3&gt;
  
  
  Setup clerk env
&lt;/h3&gt;

&lt;p&gt;In the Clerk Dashboard, navigate to the &lt;strong&gt;Configure&lt;/strong&gt; &amp;gt; &lt;strong&gt;API Keys&lt;/strong&gt; page.&lt;/p&gt;

&lt;p&gt;In the Quick Copy section, copy your Clerk publishable and secret key.&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%2Fihnkjb88l2p8qe5nny5p.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%2Fihnkjb88l2p8qe5nny5p.png" alt="Image description" width="800" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Paste your keys into your &lt;code&gt;.env.local&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=clerk_publishable_key
CLERK_SECRET_KEY=thiswillbeyoursecretkey

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Middleware Setup
&lt;/h3&gt;

&lt;p&gt;The last important step is to set up the middleware.&lt;/p&gt;

&lt;p&gt;Middleware helps us protect our routes. For example, if we have three routes named &lt;code&gt;login&lt;/code&gt;, &lt;code&gt;dashboard&lt;/code&gt;, and &lt;code&gt;about&lt;/code&gt;, and we don't want users to access the &lt;code&gt;dashboard&lt;/code&gt; and &lt;code&gt;about&lt;/code&gt; without logging in, auth middleware can help us with this.&lt;/p&gt;

&lt;p&gt;For Clerk, we'll set up our middleware based on our route structure. Create a &lt;code&gt;middleware.js&lt;/code&gt; file in your root folder and add this code 👇&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { clerkMiddleware, createRouteMatcher } from '@clerk/nextjs/server'
import { NextResponse } from 'next/server'

const isProtectedRoute = createRouteMatcher(['/user(.*)'])
const isPublicRoute = createRouteMatcher(['/'])

export default clerkMiddleware(async (auth, req) =&amp;gt; {
  const { userId } = await auth()

  if (userId &amp;amp;&amp;amp; req.nextUrl.pathname === '/') {
    const userUrl = new URL('/user', req.url)
    return NextResponse.redirect(userUrl)
  }

  if (isProtetedRoute(req)) {
    await auth.protect()
  }
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we're indicating that &lt;code&gt;/user&lt;/code&gt; routes are protected. So, if any user tries to access this route without logging in, they will first be redirected to the &lt;code&gt;/&lt;/code&gt; route to sign in or sign up.&lt;/p&gt;

&lt;p&gt;The Clerk setup is complete. You can test your sign-in and sign-up process, and all your users will be visible on your Clerk dashboard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integrate Supabase
&lt;/h2&gt;

&lt;p&gt;Now that we have our authentication flow set up, it's time to save our logged-in users in our Supabase database. When a user signs up in your app using the Clerk provider, Clerk will generate an authentication token and send it to Supabase using the &lt;em&gt;Supabase project's JWT secret key&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;You'll provide the Supabase JWT secret key to Clerk, and Clerk will share the auth token with Supabase. With this connection, you'll be able to save your user's unique &lt;code&gt;user_id&lt;/code&gt;. After that, we can perform any CRUD operations.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create Function
&lt;/h3&gt;

&lt;p&gt;First, we'll create a function in our Supabase to request the user ID from Clerk. Then, we'll pass our Supabase JWT to Clerk, to connect them.&lt;/p&gt;

&lt;p&gt;In the sidebar of your Supabase dashboard, go to Database &amp;gt; Functions.&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%2Fz8w47wnprclya6baii01.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%2Fz8w47wnprclya6baii01.png" alt="supabase function" width="653" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Click on the &lt;strong&gt;Create New Function button 👇&lt;/strong&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%2F9etm3akc952na37y59yy.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%2F9etm3akc952na37y59yy.png" alt="functions" width="800" height="173"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enter function name&lt;/p&gt;

&lt;p&gt;Return type &lt;code&gt;text&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;and add this definition&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SELECT NULLIF(
    current_setting('request.jwt.claims', true)::json-&amp;gt;&amp;gt;'sub',
    ''
)::text;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fm1s3iqw89bnh9fkboxdu.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%2Fm1s3iqw89bnh9fkboxdu.png" alt="supabase" width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Scroll a bit, and turn on the &lt;code&gt;advanced settings option&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;select language &lt;code&gt;sql&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Click &lt;code&gt;Confirm&lt;/code&gt; to save it.&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%2Fnjarc7gcah11lm2nlp38.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%2Fnjarc7gcah11lm2nlp38.png" alt="save supabase" width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, create a &lt;code&gt;user_id&lt;/code&gt; column in the table you want to secure. This column will be used in the RLS policies to only return or modify records related to the user's account. It will use the &lt;code&gt;requesting_user_id_from_clerk()&lt;/code&gt; function we just created as its default value.&lt;/p&gt;

&lt;p&gt;Go to the sidebar on the left and select &lt;code&gt;Table Editor&lt;/code&gt;.&lt;br&gt;
Select the table you wish to secure.&lt;br&gt;
In the table, select the &lt;strong&gt;+&lt;/strong&gt; column to add a new column.&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%2Fsxc00dcfufq05ft1plza.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%2Fsxc00dcfufq05ft1plza.png" alt="Image description" width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Set the &lt;strong&gt;Name&lt;/strong&gt; to &lt;code&gt;user_id&lt;/code&gt;.&lt;br&gt;
Set &lt;strong&gt;Type&lt;/strong&gt; to &lt;strong&gt;text&lt;/strong&gt;.&lt;br&gt;
Set &lt;strong&gt;Default Value&lt;/strong&gt; to &lt;code&gt;requesting_user_id_from_clerk()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Select &lt;strong&gt;Save&lt;/strong&gt; to create the column.&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%2Fg7jy2vkatki5ga0csaq1.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%2Fg7jy2vkatki5ga0csaq1.png" alt="save functions" width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Make sure your RLS policy is enabled for that particular table.&lt;/p&gt;

&lt;p&gt;In the top bar above the table, you can see the options to Enable RLS, Disable RLS, or Add New RLS Policy. Currently, I have one auth policy, so it shows 1 here.&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%2Fzd9wja34icwu2kov5g9q.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%2Fzd9wja34icwu2kov5g9q.png" alt="rls" width="800" height="132"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here you can see all your policies, click on the &lt;strong&gt;&lt;em&gt;Create New Policy button&lt;/em&gt;&lt;/strong&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%2Fe8bop3aat0l9szq07kw8.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%2Fe8bop3aat0l9szq07kw8.png" alt="rls" width="800" height="131"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this way, you can create an RLS policy for insert, delete, view, and update actions. As you can see, this RLS policy is for insert, and we are targeting public roles (&lt;em&gt;point 4 in the screenshot&lt;/em&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%2F3g4wvfj2zzjzk1rwsori.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%2F3g4wvfj2zzjzk1rwsori.png" alt="adding rls" width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Similarly, you can do the same for &lt;code&gt;SELECT&lt;/code&gt; authenticated roles. like this 👇&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%2Fo0vxms0mvpdr37sk6j5n.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%2Fo0vxms0mvpdr37sk6j5n.png" alt="rls" width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Same for the &lt;code&gt;UPDATE&lt;/code&gt;. This time, without check expression.👇&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%2F4vdhdu6sx0xxrp225ubp.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%2F4vdhdu6sx0xxrp225ubp.png" alt="rls" width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To learn more about RLS Policies, you can check out &lt;a href="https://supabase.com/features/row-level-security" rel="noopener noreferrer"&gt;this guide&lt;/a&gt; of supabase.&lt;/p&gt;
&lt;h3&gt;
  
  
  Get JWT Token
&lt;/h3&gt;

&lt;p&gt;Supabase's API requires an authentication token to allow users to access your data. Your Clerk project can generate these tokens, but it first needs the JWT secret key from your Supabase project. As we discussed earlier.&lt;/p&gt;

&lt;p&gt;To get JWT Secret:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go to Settings from the sidebar&lt;/li&gt;
&lt;li&gt;Then click on the API Section.&lt;/li&gt;
&lt;li&gt;Then, click on &lt;code&gt;Reveal&lt;/code&gt; and copy the JWT Secret.&lt;/li&gt;
&lt;/ul&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%2F85enmf93kejwpafebfxe.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%2F85enmf93kejwpafebfxe.png" alt="get jwt" width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now that we've finished the Supabase part, we'll create a Supabase JWT template on Clerk and integrate the JWT secret there.&lt;/p&gt;
&lt;h2&gt;
  
  
  Clerk JWT Template Setup
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://dashboard.clerk.com/" rel="noopener noreferrer"&gt;Go to your clerk dashboard&lt;/a&gt;, and head over to Clerk JWT Template. Click on the &lt;strong&gt;new template&lt;/strong&gt; option.&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%2F0tw4ewa0bcgbze9ifubc.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%2F0tw4ewa0bcgbze9ifubc.png" alt="clerk jwt" width="800" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select Supabase.&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%2Fs8en5flgb78azuic8x4k.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%2Fs8en5flgb78azuic8x4k.png" alt="select supabase" width="800" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Fill out 2 details here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Name: If you have multiple &lt;strong&gt;Clerk&lt;/strong&gt; projects and multiple &lt;strong&gt;JWT Templates&lt;/strong&gt; for each project, make sure to give this name a slight difference from the others.&lt;/li&gt;
&lt;li&gt;Then, paste your &lt;strong&gt;Supabase JWT Key&lt;/strong&gt; into the &lt;em&gt;signing key box&lt;/em&gt; below.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now click save.&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%2Fealigpq4c4rrn7e0fuyk.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%2Fealigpq4c4rrn7e0fuyk.png" alt="Image description" width="800" height="370"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great, now we have done almost everything important. Two things are left: setting up the environment for Supabase and integrating the Supabase client into our application. Then, we'll be ready to go.&lt;/p&gt;
&lt;h2&gt;
  
  
  Supabase Client Setup
&lt;/h2&gt;

&lt;p&gt;Create a new folder in your root directory named &lt;code&gt;lib&lt;/code&gt; and inside that create a file &lt;code&gt;supabaseClient.js&lt;/code&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%2F1rf5lrzqx3x0xafqhew3.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%2F1rf5lrzqx3x0xafqhew3.png" alt="Image description" width="415" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And, add this code to it&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { createClient } from '@supabase/supabase-js';
// Function to create a Supabase client with Clerk authentication
export function createClerkSupabaseClient(session) {  
  return createClient(
    process.env.NEXT_PUBLIC_SUPABASE_URL,
    process.env.NEXT_PUBLIC_SUPABASE_KEY,
    {
      global: {
        fetch: async (url, options = {}) =&amp;gt; {
          const clerkToken = await session?.getToken({
            template: "enter-your-jwt-clerk-template-name",
          });
          const headers = new Headers(options?.headers);
          headers.set("Authorization", `Bearer ${clerkToken}`);
          return fetch(url, { ...options, headers });
        },
      },
    }
  );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we have two &lt;code&gt;envs&lt;/code&gt;, so we'll add these to our &lt;code&gt;.env.local&lt;/code&gt; file. Go to &lt;strong&gt;Settings&lt;/strong&gt; &amp;gt; &lt;strong&gt;API&lt;/strong&gt; and copy the project URL and anon key.&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%2Fqcv4gutsj00aqr8ce8pl.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%2Fqcv4gutsj00aqr8ce8pl.png" alt="Image description" width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and paste them here in .env.local with clerk’s env variables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NEXT_PUBLIC_SUPABASE_URL=""
NEXT_PUBLIC_SUPABASE_KEY=""
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=""
CLERK_SECRET_KEY=""
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And, that’s it. We are good to go. Now you can run the server using yarn, npm, or pnpm. Whatever you choose. I am using npm.👇&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm run dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the server starts, you can click on the Sign In button to log in. Without signing in, you won't be able to access &lt;code&gt;/user/dashboard&lt;/code&gt; and &lt;code&gt;/user/about&lt;/code&gt;, as we have set this restriction in our &lt;code&gt;middleware.js&lt;/code&gt; file.&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%2Fbrxy1a0ufkczvcpfdu1v.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%2Fbrxy1a0ufkczvcpfdu1v.png" alt="output" width="800" height="457"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;That’s it for today. In this article, we learn about how we can integrate Clerk's authentication system with Supabase in a Next.js app to build a secure user management process. We covered setting up the Next.js environment, installing necessary dependencies, configuring the Clerk and Supabase dashboards, implementing authentication, and authorization using middleware.&lt;/p&gt;

&lt;p&gt;I hope you learn something from this blog, make sure to share it with your friends and community, and if you learn while reading it, make sure to give it a like and leave a comment. I write blogs and share content on JavaScript, TypeScript, Open Source, and other web development-related topics. Feel free to follow me on my socials. I'll see you in the next one.&lt;/p&gt;

&lt;p&gt;BYE 👋&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://twitter.com/Shivamkatare_27" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/shivam-katare/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Shivam-Katare" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Useful Links:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Clerk get started guide: &lt;a href="https://clerk.com/docs/quickstarts/setup-clerk" rel="noopener noreferrer"&gt;https://clerk.com/docs/quickstarts/setup-clerk&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Supabase get started guide: &lt;a href="https://supabase.com/docs/guides/getting-started" rel="noopener noreferrer"&gt;https://supabase.com/docs/guides/getting-started&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Clerk + Supabase Guide: &lt;a href="https://clerk.com/docs/integrations/databases/supabase" rel="noopener noreferrer"&gt;https://clerk.com/docs/integrations/databases/supabase&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Supabase + Clerk Guide: &lt;a href="https://supabase.com/partners/integrations/clerk" rel="noopener noreferrer"&gt;https://supabase.com/partners/integrations/clerk&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
      <category>beginners</category>
      <category>programming</category>
    </item>
    <item>
      <title>Create Beautiful Scroll Animations Using Framer Motion</title>
      <dc:creator>Shivam Katare</dc:creator>
      <pubDate>Sun, 27 Oct 2024 12:30:15 +0000</pubDate>
      <link>https://dev.to/shivamkatare/create-beautiful-scroll-animations-using-framer-motion-1a7b</link>
      <guid>https://dev.to/shivamkatare/create-beautiful-scroll-animations-using-framer-motion-1a7b</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Scrolling is something we do every day on our screens, but have you ever thought about how scrolling affects user experience? In today's world, scrolling has also become a way to give your users a smooth and satisfying experience. With tools like &lt;a href="https://www.framer.com/motion/" rel="noopener noreferrer"&gt;Framer Motion&lt;/a&gt;, developers can turn scrolling into an engaging and visually attractive experience.&lt;/p&gt;

&lt;p&gt;Today, we will explore different ways to animate scrolling. We will learn about the types of scroll animations and how they are used. We’ll understand the pattern so that we can add scroll animations to any of our projects or sites.&lt;/p&gt;

&lt;p&gt;We will create animations in Next.js using Framer Motion. If you prefer to use any other framework or library, you can use that.&lt;/p&gt;

&lt;p&gt;So, without further delay, let’s get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Types of Scroll Animations
&lt;/h2&gt;

&lt;p&gt;There are two types of scroll animations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Scroll-&lt;strong&gt;linked&lt;/strong&gt; animations: Animations that move or change based on how far you’ve scrolled, the element will update continuously as you scroll.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Scroll-&lt;strong&gt;triggered&lt;/strong&gt; animations: A normal animation is triggered when an element enters or leaves the viewport.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&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%2Fzom7b779vnvbb38k6ql4.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%2Fzom7b779vnvbb38k6ql4.png" alt="scroll types" width="800" height="266"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;With Framer motion, you can achieve both. Let’s understand both one by one.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scroll-linked animations
&lt;/h3&gt;

&lt;p&gt;Scroll-linked animations are animations where the progress or movement of an animation is directly linked up to how far the user has scrolled. In this type of animation, elements change continuously as the user scrolls.&lt;/p&gt;

&lt;p&gt;It means if you are at &lt;code&gt;point one&lt;/code&gt; then the animation will track you, and it will also be on &lt;code&gt;point one&lt;/code&gt;. And, If you scroll and go to &lt;code&gt;point two&lt;/code&gt;, the animation will follow you to that point.&lt;/p&gt;


  


&lt;p&gt;&lt;strong&gt;Key Features&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The animation is &lt;strong&gt;synchronized&lt;/strong&gt; with the scroll progress.&lt;/li&gt;
&lt;li&gt;As you scroll, the animation updates in real time based on how much you've scrolled.&lt;/li&gt;
&lt;li&gt;These animations can involve moving, scaling, rotating, or changing the appearance of elements, usually using &lt;code&gt;useScroll&lt;/code&gt; and &lt;code&gt;useTransform&lt;/code&gt; hooks(we’ll learn about these ahead) in Framer Motion.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Scroll-triggered animation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Scroll-triggered animations happen when an element enters or leaves the specific viewport as you scroll. It means the animation starts only when the user scrolls to a specific part of the page.&lt;/p&gt;

&lt;p&gt;So, if I am on &lt;code&gt;point one&lt;/code&gt; and, I set a scroll-triggered animation for &lt;code&gt;point three&lt;/code&gt; then this animation will occur at &lt;code&gt;point three&lt;/code&gt;. This means that animation will triggered only when I reach that viewport(in this case it is &lt;code&gt;point three&lt;/code&gt;)&lt;/p&gt;

&lt;p&gt;To understand this, you can check this video👇. Here, you’ll see when that &lt;code&gt;orange&lt;/code&gt; element comes in a viewport means it is completely visible on the screen and then, at that time, shows a navbar.&lt;/p&gt;


  


&lt;p&gt;&lt;strong&gt;Key Features&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Triggered by Scroll Position:&lt;/strong&gt; The animation is activated once a specific scroll position is reached, rather than being continuously synced with the scroll, like &lt;em&gt;scroll-linked&lt;/em&gt; animations. This is typically achieved using conditions like &lt;code&gt;whileInView&lt;/code&gt; or &lt;code&gt;onEnter&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Discrete Animations:&lt;/strong&gt; Unlike &lt;em&gt;scroll-linked&lt;/em&gt; animations, &lt;em&gt;scroll-triggered&lt;/em&gt; animations happen at specific moments, like when an element enters or exits the viewport. These could include &lt;code&gt;fade-ins&lt;/code&gt;, &lt;code&gt;slide-ins&lt;/code&gt;, or other visual effects that occur once as the user scrolls past a certain point.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;One-Time or Repeatable Animations:&lt;/strong&gt; &lt;em&gt;Scroll-triggered&lt;/em&gt; animations can be set to play only once when the element first comes into view, or they can repeat whenever the element enters the viewport again, depending on the use case.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  &amp;lt;motion.div
    initial="hidden"
    whileInView="visible"
    viewport={{ once: true }}  // animation will occur only once
  /&amp;gt;

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

&lt;/div&gt;



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

&lt;p&gt;Now we understand these two types of scroll animations and their differences, let's start understanding the patterns used in creating these animations. This will allow you to add scroll animations to any of your projects.&lt;/p&gt;

&lt;p&gt;First, we'll set up a project to understand this practically, and then we will create both types of animations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Setup
&lt;/h2&gt;

&lt;p&gt;Let's start by setting up our Next.js project with all the necessary dependencies. I am going to use &lt;a href="https://stackblitz.com/" rel="noopener noreferrer"&gt;StalkBlitz&lt;/a&gt; for this project but you can use VSCode too. Although all the steps I’ll tell you from the VSCode perspective so you can follow the steps.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Create a new Next.js project with TypeScript:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx create-next-app@latest
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;On installation, you'll see the following prompts:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   What is your project named? my-app
   Would you like to use TypeScript? No / Yes
   Would you like to use ESLint? No / Yes
   Would you like to use Tailwind CSS? No / Yes
   Would you like to use `src/` directory? No / Yes
   Would you like to use App Router? (recommended) No / Yes
   Would you like to customize the default import alias (@/*)? No / Yes
   What import alias would you like configured? @/*

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

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install Framer Motion&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  npm install framer-motion
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Clean up default files.&lt;br&gt;
  Remove the default code in &lt;code&gt;app/page.tsx.&lt;/code&gt; We’ll start from scratch to focus on our scroll animations. The file should look like this.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    'use client';

      export default function Home() {

          return (
           &amp;lt;div&amp;gt;

           &amp;lt;/div&amp;gt;
         );
       }

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

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Start the Development Server&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    npm run dev
&lt;/code&gt;&lt;/pre&gt;

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

&lt;h2&gt;
  
  
  Create Scroll Animation
&lt;/h2&gt;

&lt;p&gt;Now that our project is set up, we can create both types of scroll animations one by one. You can start writing code directly in &lt;code&gt;app/page.tsx&lt;/code&gt; file or create a separate component and import all of them in &lt;code&gt;app/page.tsx&lt;/code&gt; file.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating Scroll-Linked Animation
&lt;/h2&gt;

&lt;p&gt;There are several examples of scroll-linked animation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scroll Progress Bar:&lt;/strong&gt; A circular or linear progress bar that fills up as the user scrolls down the page.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Parallax Scrolling:&lt;/strong&gt; Background images or elements move at different speeds relative to the foreground, creating a depth effect as you scroll.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Fade-In Elements:&lt;/strong&gt; Content fades into view as the user scrolls.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Scale Transformations:&lt;/strong&gt; Elements gradually increase or decrease in size based on scroll progress. etc.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s create some animations. While creating any &lt;em&gt;scroll-linked&lt;/em&gt; animation you'll frequently use these methods and hooks from framer motion.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Scroll Progress Detection (&lt;code&gt;useScroll&lt;/code&gt;): Tracks the user's scroll position, typically returning a value from &lt;code&gt;0&lt;/code&gt; to &lt;code&gt;1&lt;/code&gt; that represents the progress down the page.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   const { scrollYProgress } = useScroll();
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Transforming Scroll Data&lt;/strong&gt; (&lt;code&gt;useTransform&lt;/code&gt;): Maps the scroll position to specific animation properties like opacity, scale, or position. Use this to trigger animations like fading, moving, rotating, scaling, or color changes during the scroll.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   const scaleValue = useTransform(scrollYProgress, [0, 1], [1, 2]); // Example of scaling
   const opacityValue = useTransform(scrollYProgress, [0, 0.5, 1], [1, 0.5, 0]); // Example of fading out
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Animating with Motion Elements:&lt;/strong&gt; Wrap target elements with &lt;code&gt;motion.div&lt;/code&gt; (or any &lt;code&gt;motion&lt;/code&gt; component) and link the animation to the scroll progress.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;motion.div style={{ scale: scaleValue }}&amp;gt;Animated Content&amp;lt;/motion.div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Transitions&lt;/strong&gt;: Apply smooth transitions to make animations feel fluid and natural.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;motion.div transition={{ duration: 0.8 }}&amp;gt;Smooth Transition&amp;lt;/motion.div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Initial and Final States:&lt;/strong&gt; Use &lt;code&gt;initial&lt;/code&gt;, &lt;code&gt;animate&lt;/code&gt;, or style props to define how elements look initially, and control their final states with &lt;code&gt;useTransform&lt;/code&gt; or animations linked to &lt;code&gt;scrollYProgress&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    &amp;lt;motion.div
      initial={{ opacity: 0, y: 50 }} // Starting state
      whileInView={{ opacity: 1, y: 0 }} // When the element comes 
       into view
      transition={{ duration: 0.8 }}
    &amp;gt;
      Scroll to animate
    &amp;lt;/motion.div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

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

&lt;p&gt;Scroll Progress Bar&lt;/p&gt;

&lt;p&gt;We'll start by creating a scroll progress bar. To do this, open your text editor, create a new component, and begin writing code in it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { motion, useScroll, useTransform } from 'framer-motion';

export default function ScrollProgress() {
  const { scrollYProgress } = useScroll();
  const progressValue = useTransform(scrollYProgress, [0, 1], [0, 1]);
  const strokeDasharray = useTransform(progressValue, [0, 1], [0, 283]);
  return (
    &amp;lt;div&amp;gt;
      &amp;lt;motion.svg
        className="fixed top-4 left-4"
        width="100"
        height="100"
        viewBox="0 0 100 100"
        fill="none"
        xmlns="http://www.w3.org/2000/svg"
      &amp;gt;
        &amp;lt;circle
          cx="50"
          cy="50"
          r="45"
          stroke="rgba(255,255,255,0.2)"
          strokeWidth="10"
        /&amp;gt;
        &amp;lt;motion.circle
          cx="50"
          cy="50"
          r="45"
          stroke="#00FF00"
          strokeWidth="10"
          strokeDasharray="283"
          strokeDashoffset="0"
          style={{ strokeDashoffset: strokeDasharray }}
          initial={{ strokeDashoffset: 283 }}
          animate={{ strokeDashoffset: 0 }}
        /&amp;gt;
      &amp;lt;/motion.svg&amp;gt;
    &amp;lt;/div&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;useScroll:&lt;/code&gt; Provides the scroll position as a normalized value between &lt;code&gt;0&lt;/code&gt; and &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;useTransform&lt;/code&gt;: Maps the scroll progress to the specific value ranges we need, whether it's for the &lt;code&gt;stroke dasharray&lt;/code&gt; of the progress circle or any other transformation we want to apply.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;motion&lt;/code&gt; Components: We use Framer Motion's &lt;code&gt;motion.circle&lt;/code&gt; to animate the stroke length in real-time based on scrolling.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;


  


&lt;p&gt;Dynamic Scaling Text Animation&lt;/p&gt;

&lt;p&gt;In this example, we will create a scroll-linked &lt;strong&gt;dynamic scaling effect&lt;/strong&gt; for a text element using &lt;code&gt;Framer Motion&lt;/code&gt;.&lt;/p&gt;


  


&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Tracking Scroll Progress:&lt;/strong&gt; Similar to the progress bar, we use &lt;code&gt;useScroll()&lt;/code&gt; to get the current scroll progress of the page. The &lt;code&gt;scrollYProgress&lt;/code&gt; value ranges from &lt;code&gt;0&lt;/code&gt; at the top to &lt;code&gt;1&lt;/code&gt; when fully scrolled.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;     const { scrollYProgress } = useScroll();
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Transforming the Scroll Value to Control Scale:&lt;/strong&gt; We use &lt;code&gt;useTransform()&lt;/code&gt; to convert the scroll progress into a scale value that changes dynamically as the user scrolls. In this case, we map the &lt;code&gt;scrollYProgress&lt;/code&gt; from &lt;code&gt;[0, 0.5, 1]&lt;/code&gt; to scale values &lt;code&gt;[1, 2, 1]&lt;/code&gt;, it means the text will grow from its original size, double in size at the middle of the scroll, and shrink back as you reach the bottom.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   const scaleValue = useTransform(scrollYProgress, [0, 0.5, 1], [1, 2, 1]);
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Animating the Text Element:&lt;/strong&gt; Inside the motion.h1 component, we will use the &lt;code&gt;scaleValue&lt;/code&gt; to the &lt;code&gt;style&lt;/code&gt; property to smoothly animate the text scaling effect based on the scroll position. The text will "&lt;em&gt;grow&lt;/em&gt;" and "&lt;em&gt;shrink&lt;/em&gt;" as the user scrolls.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   &amp;lt;motion.h1
     style={{ scale: scaleValue }} // Applying the dynamic scale 
      based on scroll
     className="text-6xl font-bold text-white drop-shadow-lg"
   &amp;gt;
     Scroll to Scale
   &amp;lt;/motion.h1&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Styling the Section:&lt;/strong&gt; The text is centered within a full-screen section that has a background gradient to add visual appeal. The background creates a smooth gradient from purple to indigo, making the dynamic text stand out more.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   &amp;lt;div className="min-h-screen flex justify-center items-center 
    bg-gradient-to-b from-purple-600 to-indigo-800"&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

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

&lt;p&gt;&lt;strong&gt;Concepts Used:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;useScroll&lt;/code&gt;: We use this hook again to track the page’s scroll progress, which serves as the trigger for the animation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;useTransform&lt;/code&gt;: Here, we map the scroll progress to scale values, making the text grow or shrink smoothly during the scroll event.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;motion&lt;/code&gt; Components: The &lt;code&gt;motion.h1&lt;/code&gt; the component is animated in real-time, with the scale dynamically adjusted as the scroll value changes.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We again used the same concepts. &lt;code&gt;useScroll&lt;/code&gt; &lt;code&gt;useTransform&lt;/code&gt; and &lt;code&gt;motion&lt;/code&gt;. So, this is the part of &lt;em&gt;scroll-linked&lt;/em&gt;. Now let’s create some &lt;em&gt;scroll-triggered&lt;/em&gt; animations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating Scroll-Triggered Animation
&lt;/h2&gt;

&lt;p&gt;Scroll-triggered animation doesn't stick to work with a few examples. You can link scroll animation to specific components of the page. For example, I have a landing page with two elements and a &lt;code&gt;div&lt;/code&gt; containing some text. Currently, there are no animations on the page, but I want to add animations that activate when the viewport comes into view.&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%2F35cjzfloc6mthvinrsbr.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%2F35cjzfloc6mthvinrsbr.png" alt="animation image" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For this, we will use scroll-triggered animations. As we learned earlier, scroll-triggered animations occur when a particular viewport comes into view. So, let’s add Framer Motion elements to this code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { motion, useScroll, useTransform } from 'framer-motion';

export default function ScrollOne() {
  return (
    &amp;lt;section className="min-h-screen flex flex-col justify-center items-center bg-gradient-to-br from-pink-500 to-yellow-500 text-center"&amp;gt;
    &amp;lt;motion.div
      initial={{ opacity: 0, y: 50 }}
      whileInView={{ opacity: 1, y: 0 }}
      transition={{ duration: 0.8, ease: 'easeOut' }}
      className="p-8 bg-white/90 shadow-2xl rounded-3xl max-w-lg"
    &amp;gt;
      &amp;lt;h1 className="text-5xl font-extrabold text-transparent bg-clip-text bg-gradient-to-r from-blue-400 to-green-400 mb-6"&amp;gt;
        Smooth Scroll Animation
      &amp;lt;/h1&amp;gt;
      &amp;lt;p className="text-lg text-gray-700"&amp;gt;
        This is a fade-in animation triggered by scrolling down. Enhance
        user experience by making content appear progressively as users
        scroll.
      &amp;lt;/p&amp;gt;
    &amp;lt;/motion.div&amp;gt;

    &amp;lt;motion.div
      initial={{ y: -100, opacity: 0 }}
      whileInView={{ y: 0, opacity: 1 }}
      transition={{ delay: 0.5, duration: 0.8 }}
      className="absolute top-[10%] text-2xl text-purple-200 font-semibold"
    &amp;gt;
      Top-to-Bottom Animated Text!
    &amp;lt;/motion.div&amp;gt;
  &amp;lt;/section&amp;gt;
  )
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;&lt;p&gt;We import &lt;code&gt;motion&lt;/code&gt; from &lt;code&gt;framer-motion&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The entire component is wrapped in a &lt;code&gt;section&lt;/code&gt; element, it has some basic styling.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The &lt;code&gt;&amp;lt;motion.div&amp;gt;&lt;/code&gt; which is the first motion div. It contains the main content (a heading and paragraph). It uses &lt;code&gt;initial&lt;/code&gt; and &lt;code&gt;whileInView&lt;/code&gt; to create a scroll-triggered animation:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;initial={{ opacity: 0, y: 50 }}:&lt;/code&gt; Initially, the box is invisible &lt;code&gt;(opacity: 0)&lt;/code&gt; and positioned 50 pixels down &lt;code&gt;(y: 50)&lt;/code&gt;.&lt;br&gt;
  &lt;code&gt;whileInView={{ opacity: 1, y: 0 }}:&lt;/code&gt; When the user scrolls it into view, it becomes fully visible &lt;code&gt;(opacity: 1)&lt;/code&gt; and moves to its normal position &lt;code&gt;(y: 0)&lt;/code&gt;.&lt;br&gt;
  &lt;code&gt;transition={{ duration: 0.8, ease: 'easeOut' }}:&lt;/code&gt; The transition takes &lt;code&gt;0.8 seconds&lt;/code&gt; and eases out smoothly.&lt;/p&gt;

&lt;p&gt;The content inside is styled with Tailwind classes, giving it padding, a background with 90% opacity, shadow, rounded corners, and a max width.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;After that, we have a second motion div (Top-to-Bottom Animated Text). This &lt;code&gt;&amp;lt;motion.div&amp;gt;&lt;/code&gt; animates some text that moves down from the top of the screen:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;initial={{ y: -100, opacity: 0 }}:&lt;/code&gt; The text starts 100 pixels above its normal position and is invisible.&lt;br&gt;
   &lt;code&gt;whileInView={{ y: 0, opacity: 1 }}:&lt;/code&gt; When it comes into view, it moves to its normal position and becomes fully visible.&lt;br&gt;
   &lt;code&gt;transition={{ delay: 0.5, duration: 0.8 }}:&lt;/code&gt; There's a 0.5-second delay before the animation starts, giving a staggered effect.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The final output will look like this.&lt;/p&gt;


  


&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;That’s it for today’s blog. We cover 2 types of animations with some methods used while creating these animations like: &lt;code&gt;useScroll&lt;/code&gt; &lt;code&gt;useTransition&lt;/code&gt; &lt;code&gt;whileInView&lt;/code&gt; &lt;code&gt;initial&lt;/code&gt; and &lt;code&gt;final states&lt;/code&gt; and things like that.&lt;/p&gt;

&lt;p&gt;All the code with more animation samples I have added in my StalkBliz project. You can check it out &lt;a href="https://stackblitz.com/edit/stackblitz-starters-ob9urs?file=componenets%2FBackgroundDynamicChange.tsx" rel="noopener noreferrer"&gt;here&lt;/a&gt; and view more sample scroll animations with code. If you want to explore more props, methods, and examples of scroll animations, you can check out &lt;a href="https://www.framer.com/motion/scroll-animations/" rel="noopener noreferrer"&gt;Framer Motion’s scroll docs here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks for reading this blog. If you learned something from it, please like and share it with your friends and community. I write blogs and share content on JavaScript, TypeScript, Open Source, and other web development-related topics. Feel free to follow me on my socials. I'll see you in the next one. Thank You :)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://x.com/Shivamkatare_27" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/shivam-katare/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Shivam-Katare" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>nextjs</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to Create PDFs using React JS</title>
      <dc:creator>Shivam Katare</dc:creator>
      <pubDate>Fri, 18 Oct 2024 06:13:27 +0000</pubDate>
      <link>https://dev.to/shivamkatare/how-to-create-pdfs-using-react-js-204k</link>
      <guid>https://dev.to/shivamkatare/how-to-create-pdfs-using-react-js-204k</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Sometimes, you need to save your app's tables or data as a PDF for easy sharing. With &lt;a href="https://www.npmjs.com/package/@react-pdf/renderer" rel="noopener noreferrer"&gt;React PDF Renderer&lt;/a&gt;, you can easily turn your React components into high-quality PDFs. In this blog, we will learn how to make it simple to change your web content into shareable PDFs.&lt;/p&gt;

&lt;p&gt;React PDF Renderer has its own set of components, which are a bit different from regular React components or HTML tags. But, its functionality is easy to understand. Once you learn the basics, you'll be able to use React PDF Renderer efficiently to create PDFs in your React apps. Before we get into the code, we'll first look at the main components provided by React PDF Renderer and see how they work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Basic knowledge of React&lt;/li&gt;
&lt;li&gt;Node.js and npm installed on your machine&lt;/li&gt;
&lt;li&gt;Familiarity with CSS&lt;/li&gt;
&lt;li&gt;An existing React project set up(this we will cover)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;With these basics, you are ready to start the process of converting your React components into PDFs.&lt;/p&gt;

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

&lt;p&gt;React PDF Renderer uses various components to help you convert React components into PDFs. Here are the key components and their uses:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Document&lt;/strong&gt;: The root element for creating a PDF document.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Page&lt;/strong&gt;: Represents a single page within the PDF.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;View&lt;/strong&gt;: A container element similar to a &lt;code&gt;div&lt;/code&gt; in HTML.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Text&lt;/strong&gt;: Used for rendering text within the PDF.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Image&lt;/strong&gt;: Allows you to include images in your PDF.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Link&lt;/strong&gt;: Enables clickable links within the PDF.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;View Component&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use&lt;/strong&gt;: Acts as a container for other components, similar to &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; in HTML.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Styling Format&lt;/strong&gt;: Supports styles such as width, height, margin, padding, background color, border, etc.
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;View style={{ width: 100, height: 50, backgroundColor: 'blue' }} &amp;gt;
/* pdf content */
&amp;lt;/View&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Text Component&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use&lt;/strong&gt;: Renders text content within the PDF document.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Styling Format&lt;/strong&gt;: Supports font size, font family, font weight, text alignment, color, and other text-related styles.
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Text style={{ fontSize: 14, fontWeight: 'bold', color: 'black' }}&amp;gt;
Hello, World!
&amp;lt;/Text&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Image Component&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use&lt;/strong&gt;: Embed images into the PDF document.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Styling Format&lt;/strong&gt;: Supports properties like width, height, and source URL for the image.
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Image src="example.jpg" style={{ width: 200, height: 100 }} /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Page Component&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use&lt;/strong&gt;: Defines individual pages within the PDF document.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Styling Format&lt;/strong&gt;: Supports properties like size, orientation, and margins for each page.
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Page size="A4" style={{ margin: 10 }}&amp;gt;Page Content&amp;lt;/Page&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Link Component&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use&lt;/strong&gt;: Creates hyperlinks within the PDF document.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Styling Format&lt;/strong&gt;: Supports defining the URL and styling options for the hyperlink.
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Link src="https://example.com" style={{ color: 'blue' }}&amp;gt;
  Click here
&amp;lt;/Link&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Document Component&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use&lt;/strong&gt;: Represents the entire PDF document.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Styling Format&lt;/strong&gt;: Supports global document settings such as page size, margins, and metadata.
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Document title="Example Document"&amp;gt;
&amp;lt;Page&amp;gt;
  &amp;lt;Text&amp;gt;
      Content
  &amp;lt;/Text&amp;gt;
&amp;lt;/Page&amp;gt;
&amp;lt;/Document&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

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

&lt;p&gt;These are the basic components that are used while working with React PDF. You can see a complete list of components with available valid props &lt;a href="https://react-pdf.org/components" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up the Environment
&lt;/h2&gt;

&lt;p&gt;You can start building your PDFs in your existing app, or you can use an &lt;a href="https://react-pdf-repl.vercel.app/" rel="noopener noreferrer"&gt;online REPL&lt;/a&gt; specifically for React PDF. The advantage of an online React PDF REPL is that we can instantly view the preview of our code. Without this preview system, we would need to download the PDF each time to view it.&lt;/p&gt;

&lt;p&gt;So, we will use the online REPL for React PDF because it allows us to preview our code changes instantly. This preview thing is great while creating a PDF, as it saves time and helps us catch errors early. Although, I will also cover how you can set up React PDF in your React application.&lt;/p&gt;

&lt;p&gt;Let's create a new React application, install React PDF Renderer, and write our first line of code with it.&lt;/p&gt;

&lt;p&gt;Open your terminal and run the following command to create a new React app using Create React App&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx create-react-app my-react-pdf-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will create a new directory named my-react-pdf-app with a basic React setup.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd my-react-pdf-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use npm to install the React PDF Renderer library.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm install @react-pdf/renderer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the newly created project (&lt;code&gt;my-react-pdf-app&lt;/code&gt;) in your favorite code editor (like VSCode). Create a new file named &lt;code&gt;MyDocument.js&lt;/code&gt; in the &lt;code&gt;src&lt;/code&gt; directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/MyDocument.js

import React from 'react';
import { Document, Page, Text, View, StyleSheet } from '@react-pdf/renderer';

// Create styles
const styles = StyleSheet.create({
  page: {
    flexDirection: 'column',
    backgroundColor: '#E4E4E4',
  },
  section: {
    margin: 10,
    padding: 10,
    flexGrow: 1,
  },
});

// Create Document Component
const MyDocument = () =&amp;gt; (
  &amp;lt;Document&amp;gt;
    &amp;lt;Page size="A4" style={styles.page}&amp;gt;
      &amp;lt;View style={styles.section}&amp;gt;
        &amp;lt;Text&amp;gt;Section #1&amp;lt;/Text&amp;gt;
      &amp;lt;/View&amp;gt;
      &amp;lt;View style={styles.section}&amp;gt;
        &amp;lt;Text&amp;gt;Section #2&amp;lt;/Text&amp;gt;
      &amp;lt;/View&amp;gt;
    &amp;lt;/Page&amp;gt;
  &amp;lt;/Document&amp;gt;
);

export default MyDocument;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open &lt;code&gt;App.js&lt;/code&gt; and modify it to render the PDF document.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/App.js

import React from 'react';
import { PDFDownloadLink } from '@react-pdf/renderer';
import MyDocument from './MyDocument';

const App = () =&amp;gt; (
  &amp;lt;div className="App"&amp;gt;
    &amp;lt;header className="App-header"&amp;gt;
      &amp;lt;PDFDownloadLink document={&amp;lt;MyDocument /&amp;gt;} fileName="mypdf.pdf"&amp;gt;
        {({ blob, url, loading, error }) =&amp;gt;
          loading ? 'Loading document...' : 'Download PDF now!'
        }
      &amp;lt;/PDFDownloadLink&amp;gt;
    &amp;lt;/header&amp;gt;
  &amp;lt;/div&amp;gt;
);

export default App;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open your terminal, make sure you are in the project directory, and start the development server.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Your default browser should automatically open and navigate to &lt;code&gt;http://localhost:3000&lt;/code&gt;, where you will see a link to download the PDF.&lt;/p&gt;

&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%2Fu62fwkm5fxw76mdrdzyl.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%2Fu62fwkm5fxw76mdrdzyl.png" alt="localhost preview" width="800" height="408"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But in this blog, we will use an &lt;a href="https://react-pdf-repl.vercel.app/" rel="noopener noreferrer"&gt;Online Code REPL&lt;/a&gt; so that we can see the output instantly. Then, we can use the same code in our React app to download it. Both methods will give the same result.&lt;/p&gt;

&lt;h2&gt;
  
  
  Code PDF
&lt;/h2&gt;

&lt;p&gt;So, we are going to code this PDF design. With this design, we will understand how all the components work. After that, you can code any PDF design.&lt;/p&gt;

&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%2Fp3e2rgpod8mf8wso7vgv.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%2Fp3e2rgpod8mf8wso7vgv.png" alt="PDF we are going to build" width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, till now, we understand that there are three major components for React PDF:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Document&lt;/li&gt;
&lt;li&gt;Page&lt;/li&gt;
&lt;li&gt;View&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This PDF design is also divided into these three main components.&lt;/p&gt;

&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%2F1kwvyir0cjejsrvts9rb.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%2F1kwvyir0cjejsrvts9rb.png" alt="understanding the pdf components" width="800" height="366"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, from the above diagram, I hope you understand what we need to build first. First, we will create a document using the &lt;code&gt;&amp;lt;Document&amp;gt;&lt;/code&gt; component. Then, we will declare a &lt;code&gt;&amp;lt;Page&amp;gt;&lt;/code&gt;, and after that, we will declare a &lt;code&gt;&amp;lt;View&amp;gt;&lt;/code&gt; and start defining our components there.&lt;/p&gt;

&lt;h2&gt;
  
  
  Steps:
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Basic Setup&lt;/strong&gt;&lt;br&gt;
Start with importing basic things and components we need to use for React PDF.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from 'react';
import { Page, Text, View, Document, StyleSheet, Image } from '@react-pdf/renderer';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Document Styling&lt;/strong&gt;&lt;br&gt;
Now we will style our document. Here, we will set how our entire document looks. We will use &lt;code&gt;StyleSheet.create&lt;/code&gt; to define the styles for our PDF components. This is similar to CSS but written in JavaScript objects:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const styles = StyleSheet.create({
  page: {
    padding: 20,
    backgroundColor: '#ffffff'
  },
  section: {
    marginBottom: 20
  }
});
// we will add more style later on.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we will use the page and section styles in our components 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;&amp;lt;Page style={styles.page}&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Define Data&lt;/strong&gt;&lt;br&gt;
Define the data you want to display in the PDF document. This data can be dynamic and fetched from an API or a database:&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 = [
  {
    title: 'Attack on Titan',
    studio: 'Wit Studio',
    genre: 'Action, Dark Fantasy',
    releaseDate: '04-07-2013',
    status: 'Completed',
    rating: '9.0',
    cost: '$120'
  }
];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Create Document Component&lt;/strong&gt;&lt;br&gt;
Define the &lt;code&gt;MyDocument&lt;/code&gt; component which will structure the PDF document. The name can be anything. It is our React component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const MyDocument = () =&amp;gt; {
 return (
   // Our pdf code will be here
     );
};
export default MyDocument;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The component returns JSX that describes the structure of the PDF document. So, in the return statement, we will start by using our first React PDF component, which is &lt;code&gt;&amp;lt;Document /&amp;gt;&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;const MyDocument = () =&amp;gt; {
 return (
     &amp;lt;Document&amp;gt;
       /* Here we will steup our page */
     &amp;lt;/Document&amp;gt;
     );
};
export default MyDocument;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This creates a Black PDF Document.&lt;/p&gt;

&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%2Fo6p51b5kq1gstg1nyz1q.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%2Fo6p51b5kq1gstg1nyz1q.png" alt="pdf preview" width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create PDF Pages&lt;/strong&gt;&lt;br&gt;
Now, let's start by creating pages for our PDF. Use the &lt;code&gt;&amp;lt;Page&amp;gt;&lt;/code&gt; component to define pages. The number of &lt;code&gt;&amp;lt;Page&amp;gt;&lt;/code&gt; components will determine the number of pages. For example, if you use two &lt;code&gt;&amp;lt;Page&amp;gt;&lt;/code&gt; tags, your PDF will have two pages. If you have too much data for a single page, React PDF will automatically create additional pages as needed.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;Page&amp;gt;&lt;/code&gt; component has several props, such as &lt;code&gt;size&lt;/code&gt;, which defines the page size like A4, A2, A3, etc., along with many other props. You can see all &lt;a href="https://react-pdf.org/components#page" rel="noopener noreferrer"&gt;page props here&lt;/a&gt;.&lt;/p&gt;

&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%2F1xo7iq0fp2k9pojkswpe.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%2F1xo7iq0fp2k9pojkswpe.png" alt="props page" width="800" height="419"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Create Document Component
const MyDocument = () =&amp;gt; {
  return (
    &amp;lt;Document&amp;gt;
      &amp;lt;Page size="A4" style={styles.page}&amp;gt;
        &amp;lt;View&amp;gt; 
          &amp;lt;Text&amp;gt;Hello&amp;lt;/Text&amp;gt;
        &amp;lt;/View&amp;gt;
      &amp;lt;/Page&amp;gt;
    &amp;lt;/Document&amp;gt;
  );
};
export default MyDocument;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we use the &lt;code&gt;&amp;lt;Page&amp;gt;&lt;/code&gt; component and add a &lt;code&gt;size&lt;/code&gt; prop, giving it a value. We also use the &lt;code&gt;style&lt;/code&gt; defined in our style object. Inside the &lt;code&gt;&amp;lt;Page&amp;gt;&lt;/code&gt; component, we are using the &lt;code&gt;&amp;lt;View&amp;gt;&lt;/code&gt; component, and within that, we are using the &lt;code&gt;&amp;lt;Text&amp;gt;&lt;/code&gt; component to display the text &lt;em&gt;"Hello."&lt;/em&gt; The output will look like this:&lt;/p&gt;

&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%2Ffqnlpec2gdugoevqx3b2.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%2Ffqnlpec2gdugoevqx3b2.png" alt="pdf first output" width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, the &lt;code&gt;View&lt;/code&gt; component works just like a &lt;code&gt;div&lt;/code&gt;. For example, if you want a big box in your PDF divided into specific columns, and you want to give each column a different color, you just need a few &lt;code&gt;View&lt;/code&gt; components and some styling. If you need to add text, use the &lt;code&gt;Text&lt;/code&gt; component. To add an image, use the &lt;code&gt;Image&lt;/code&gt; component. Check the code and output below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Create styles
const styles = StyleSheet.create({
  page: {
    padding: 20,
    backgroundColor: '#ffffff'
  },
  section: {
    marginBottom: 20
  },
  bigBox: {
    flexDirection: 'row',
    marginBottom: 20,
    borderWidth: 1,
    borderColor: '#000',
  },
  column: {
    flex: 1,
    padding: 10,
    borderWidth: 1,
    borderColor: '#000',
  },
  column1: {
    backgroundColor: '#ffcccc',
  },
  column2: {
    backgroundColor: '#ccffcc',
  },
  column3: {
    backgroundColor: '#ccccff',
  },
  text: {
    fontSize: 12,
  },
  image: {
    width: "auto",
    height: 100,
  },
  canvas: {
    width: '100%',
    height: 100,
    borderWidth: 1,
    borderColor: '#000',
    backgroundColor: '#e0e0e0',
  }
});

// Create Document Component
const MyDocument = () =&amp;gt; {
  return (
      &amp;lt;Document&amp;gt;
    &amp;lt;Page size="A4" style={styles.page}&amp;gt;
      &amp;lt;View style={[styles.bigBox]}&amp;gt;
        &amp;lt;View style={[styles.column, styles.column1]}&amp;gt;
          &amp;lt;Text style={styles.text}&amp;gt;This is a text column&amp;lt;/Text&amp;gt;
        &amp;lt;/View&amp;gt;
        &amp;lt;View style={[styles.column, styles.column2]}&amp;gt;
          &amp;lt;Image
            style={styles.image}
            src="https://t3.ftcdn.net/jpg/07/24/53/02/360_F_724530208_783brjeXb7pllU2HefNMxNc1TynemreM.jpg"
          /&amp;gt;
        &amp;lt;/View&amp;gt;
        &amp;lt;View style={[styles.column, styles.column3]}&amp;gt;
          &amp;lt;View style={styles.canvas}&amp;gt;
            &amp;lt;Text style={styles.text}&amp;gt;Canvas section (Placeholder)&amp;lt;/Text&amp;gt;
          &amp;lt;/View&amp;gt;
        &amp;lt;/View&amp;gt;
      &amp;lt;/View&amp;gt;
    &amp;lt;/Page&amp;gt;
  &amp;lt;/Document&amp;gt;
  );
};

export default MyDocument;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&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%2Fwv8hvpf9gpxub3h9kk36.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%2Fwv8hvpf9gpxub3h9kk36.png" alt="more colors in pdf" width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Explanation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;styles.bigBox&lt;/code&gt;: This style defines the main container that holds the three columns.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;styles.column&lt;/code&gt;: This style defines the base style for each column, including padding and borders.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;styles.column1&lt;/code&gt;, &lt;code&gt;styles.column2&lt;/code&gt;, &lt;code&gt;styles.column3&lt;/code&gt;: These styles define the background colors for each column.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;styles.text&lt;/code&gt;: This style is used for the text inside the first column.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;styles.image&lt;/code&gt;: This style is used for the image inside the second column.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;styles.canvas&lt;/code&gt;: This style defines the placeholder canvas section inside the third column.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here, each column is given a different background color for visual separation. The first column contains text, the second contains an image, and the third contains a placeholder for a canvas section.&lt;/p&gt;

&lt;p&gt;So, for this, we just used the &lt;code&gt;View&lt;/code&gt;, &lt;code&gt;Text&lt;/code&gt;, and &lt;code&gt;Image&lt;/code&gt; components. I hope you now understand that to create any component, we only need a few components to create a PDF in React. Now, let's return to our main design. We will use the same components and add some styling like flex, border styling, font styling, etc.&lt;/p&gt;

&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%2F9jhg7tefxjxhqccmio35.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%2F9jhg7tefxjxhqccmio35.png" alt="explaining each pdf parts" width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's create the header first. We need to use a &lt;code&gt;View&lt;/code&gt; component as the header, apply some styles using &lt;code&gt;flex&lt;/code&gt;, and add &lt;code&gt;Image&lt;/code&gt; and &lt;code&gt;Text&lt;/code&gt; components to it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// src/MyDocument.js
import React from 'react';
import { Page, Text, View, Document, StyleSheet, Image } from '@react-pdf/renderer';

// Create styles
const styles = StyleSheet.create({
  page: {
    padding: 20,
    backgroundColor: '#ffffff'
  },
  section: {
    marginBottom: 20
  },
  headerContainer: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    marginBottom: 20
  },
  headerText: {
    fontSize: 20,
    fontWeight: 'bold'
  },
  image: {
    width: 50,
    height: 50
  },
  date: {
    fontSize: 12,
    textAlign: 'right'
  },
});
// Create Document Component
const MyDocument = () =&amp;gt; {
  return (
        &amp;lt;Document&amp;gt;
      &amp;lt;Page size="A4" style={styles.page}&amp;gt;
        &amp;lt;View style={styles.section}&amp;gt;
          &amp;lt;View style={styles.headerContainer}&amp;gt;
            &amp;lt;Image style={styles.image} src="https://static.vecteezy.com/system/resources/thumbnails/013/993/061/small/mugiwara-the-illustration-vector.jpg" /&amp;gt;
            &amp;lt;Text style={styles.headerText}&amp;gt;Anime Report&amp;lt;/Text&amp;gt;
            &amp;lt;Text style={styles.date}&amp;gt;{new Date().toLocaleDateString()}&amp;lt;/Text&amp;gt;
            &amp;lt;/View&amp;gt;
          &amp;lt;/View&amp;gt;
      &amp;lt;/Page&amp;gt;
    &amp;lt;/Document&amp;gt;
  );
};

export default MyDocument;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&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%2F5t0u2ha9il88rhcqy5zi.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%2F5t0u2ha9il88rhcqy5zi.png" alt="starting building pdf report" width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You see, it's easy to grasp.&lt;/p&gt;

&lt;p&gt;Let's code the table. To create tables using React PDF Renderer, we just need to use &lt;code&gt;flex&lt;/code&gt; styling and the &lt;code&gt;View&lt;/code&gt; and &lt;code&gt;Text&lt;/code&gt; components. Each &lt;code&gt;View&lt;/code&gt; component will contain one &lt;code&gt;Text&lt;/code&gt; component, but you can add more &lt;code&gt;Text&lt;/code&gt; components if needed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Main Structure&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This code will create a table in a PDF document.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;View style={styles.table}&amp;gt;
  {/* Table Header */}
  &amp;lt;View style={styles.tableRow}&amp;gt;
    {/* Each Column Header */}
    &amp;lt;View style={styles.tableColHeader}&amp;gt;
      &amp;lt;Text style={styles.tableCellHeader}&amp;gt;Title&amp;lt;/Text&amp;gt;
    &amp;lt;/View&amp;gt;
    {/* More column headers... */}
  &amp;lt;/View&amp;gt;
  {/* Table Rows */}
  {data.map((item, index) =&amp;gt; (
    &amp;lt;View style={styles.tableRow} key={index}&amp;gt;
      {/* Each Column in a Row */}
      &amp;lt;View style={styles.tableCol}&amp;gt;
        &amp;lt;Text style={styles.tableCell}&amp;gt;{item.title}&amp;lt;/Text&amp;gt;
      &amp;lt;/View&amp;gt;
      {/* More columns... */}
    &amp;lt;/View&amp;gt;
  ))}
&amp;lt;/View&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Table Container&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;View style={styles.table}&amp;gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;This &lt;code&gt;View&lt;/code&gt; acts as the main container for the entire table. The &lt;code&gt;styles.table&lt;/code&gt; style will define how the table is displayed, like borders, padding, etc.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Table Header Row&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;View style={styles.tableRow}&amp;gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;This &lt;code&gt;View&lt;/code&gt; represents a row in the table. The &lt;code&gt;styles.tableRow&lt;/code&gt; style will apply to both the header row and each data row.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Column Headers&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;View style={styles.tableColHeader}&amp;gt;
&amp;lt;Text style={styles.tableCellHeader}&amp;gt;Title&amp;lt;/Text&amp;gt;
&amp;lt;/View&amp;gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Each &lt;code&gt;View&lt;/code&gt; inside the header row is a column header. The &lt;code&gt;styles.tableColHeader&lt;/code&gt; style will define how the header cells look, such as their background color, borders, and text alignment. The &lt;code&gt;Text&lt;/code&gt; component inside it contains the column's title and uses the &lt;code&gt;styles.tableCellHeader&lt;/code&gt; style for text styling. Repeat this for each column header (e.g., Title, Studio, Genre, Release Date, Status, Rating, Cost).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Data Rows&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{data.map((item, index) =&amp;gt; (
&amp;lt;View style={styles.tableRow} key={index}&amp;gt;
 {/* Columns for each row */}
&amp;lt;/View&amp;gt;
))}
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Here, we use the &lt;code&gt;map&lt;/code&gt; function to loop over an array called &lt;code&gt;data&lt;/code&gt;. For each item in the array, it creates a new row in the table. The &lt;code&gt;key&lt;/code&gt; attribute helps React manage the list of items efficiently.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Columns in Data Rows&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;View style={styles.tableCol}&amp;gt;
&amp;lt;Text style={styles.tableCell}&amp;gt;{item.title}&amp;lt;/Text&amp;gt;
&amp;lt;/View&amp;gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Each &lt;code&gt;View&lt;/code&gt; inside the data row is a column. The &lt;code&gt;styles.tableCol&lt;/code&gt; style will define the appearance of the cells in the data rows, and the &lt;code&gt;Text&lt;/code&gt; component inside displays the actual data. The &lt;code&gt;styles.tableCell&lt;/code&gt; style is applied to the text for consistent styling. Repeat this for each column in the data row (e.g., &lt;code&gt;item.title&lt;/code&gt;, &lt;code&gt;item.studio&lt;/code&gt;, &lt;code&gt;item.genre&lt;/code&gt;, &lt;code&gt;item.releaseDate&lt;/code&gt;, &lt;code&gt;item.status&lt;/code&gt;, &lt;code&gt;item.rating&lt;/code&gt;, &lt;code&gt;item.cost&lt;/code&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Table Code&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// React PDF Renderer Component
import React from 'react';
import { Page, Text, View, Document, StyleSheet } from '@react-pdf/renderer';
// Create styles
const styles = StyleSheet.create({
// after date styling....
table: {
  display: "table",
  width: "auto",
  borderStyle: "solid",
  borderWidth: 1,
  borderColor: '#bfbfbf'
},
tableRow: {
  flexDirection: "row"
},
tableColHeader: {
  width: "15%",
  borderStyle: "solid",
  borderWidth: 1,
  borderColor: '#bfbfbf',
  backgroundColor: '#f0f0f0'
},
tableCol: {
  width: "15%",
  borderStyle: "solid",
  borderWidth: 1,
  borderColor: '#bfbfbf'
},
tableCellHeader: {
  margin: 5,
  fontSize: 10,
  fontWeight: "bold"
},
tableCell: {
  margin: 5,
  fontSize: 10
},
footerContainer: {
  flexDirection: 'row',
  justifyContent: 'space-between',
  alignItems: 'center',
  marginTop: 20
},
footerText: {
  fontSize: 12
},
totalCost: {
  fontSize: 12,
  fontWeight: 'bold'
}
});
const data = [
{
  title: 'Attack on Titan',
  studio: 'Wit Studio',
  genre: 'Action, Dark Fantasy',
  releaseDate: '04-07-2013',
  status: 'Completed',
  rating: '9.0',
  cost: '$120'
},
// You can add more or fetch data from an API or database
];
// Create Document Component
const MyDocument = () =&amp;gt; (
&amp;lt;Document&amp;gt;
&amp;lt;Page size="A4"&amp;gt;
   /* After header code */
  &amp;lt;View style={styles.table}&amp;gt;
    &amp;lt;View style={styles.tableRow}&amp;gt;
      &amp;lt;View style={styles.tableColHeader}&amp;gt;
        &amp;lt;Text style={styles.tableCellHeader}&amp;gt;Title&amp;lt;/Text&amp;gt;
      &amp;lt;/View&amp;gt;
      &amp;lt;View style={styles.tableColHeader}&amp;gt;
        &amp;lt;Text style={styles.tableCellHeader}&amp;gt;Studio&amp;lt;/Text&amp;gt;
      &amp;lt;/View&amp;gt;
      &amp;lt;View style={styles.tableColHeader}&amp;gt;
        &amp;lt;Text style={styles.tableCellHeader}&amp;gt;Genre&amp;lt;/Text&amp;gt;
      &amp;lt;/View&amp;gt;
      &amp;lt;View style={styles.tableColHeader}&amp;gt;
        &amp;lt;Text style={styles.tableCellHeader}&amp;gt;Release Date&amp;lt;/Text&amp;gt;
      &amp;lt;/View&amp;gt;
      &amp;lt;View style={styles.tableColHeader}&amp;gt;
        &amp;lt;Text style={styles.tableCellHeader}&amp;gt;Status&amp;lt;/Text&amp;gt;
      &amp;lt;/View&amp;gt;
      &amp;lt;View style={styles.tableColHeader}&amp;gt;
        &amp;lt;Text style={styles.tableCellHeader}&amp;gt;Rating&amp;lt;/Text&amp;gt;
      &amp;lt;/View&amp;gt;
      &amp;lt;View style={styles.tableColHeader}&amp;gt;
        &amp;lt;Text style={styles.tableCellHeader}&amp;gt;Cost&amp;lt;/Text&amp;gt;
      &amp;lt;/View&amp;gt;
    &amp;lt;/View&amp;gt;
    {data.map((item, index) =&amp;gt; (
      &amp;lt;View style={styles.tableRow} key={index}&amp;gt;
        &amp;lt;View style={styles.tableCol}&amp;gt;
          &amp;lt;Text style={styles.tableCell}&amp;gt;{item.title}&amp;lt;/Text&amp;gt;
        &amp;lt;/View&amp;gt;
        &amp;lt;View style={styles.tableCol}&amp;gt;
          &amp;lt;Text style={styles.tableCell}&amp;gt;{item.studio}&amp;lt;/Text&amp;gt;
        &amp;lt;/View&amp;gt;
        &amp;lt;View style={styles.tableCol}&amp;gt;
          &amp;lt;Text style={styles.tableCell}&amp;gt;{item.genre}&amp;lt;/Text&amp;gt;
        &amp;lt;/View&amp;gt;
        &amp;lt;View style={styles.tableCol}&amp;gt;
          &amp;lt;Text style={styles.tableCell}&amp;gt;{item.releaseDate}&amp;lt;/Text&amp;gt;
        &amp;lt;/View&amp;gt;
        &amp;lt;View style={styles.tableCol}&amp;gt;
          &amp;lt;Text style={styles.tableCell}&amp;gt;{item.status}&amp;lt;/Text&amp;gt;
        &amp;lt;/View&amp;gt;
        &amp;lt;View style={styles.tableCol}&amp;gt;
          &amp;lt;Text style={styles.tableCell}&amp;gt;{item.rating}&amp;lt;/Text&amp;gt;
        &amp;lt;/View&amp;gt;
        &amp;lt;View style={styles.tableCol}&amp;gt;
          &amp;lt;Text style={styles.tableCell}&amp;gt;{item.cost}&amp;lt;/Text&amp;gt;
        &amp;lt;/View&amp;gt;
      &amp;lt;/View&amp;gt;
    ))}
  &amp;lt;/View&amp;gt;
&amp;lt;/Page&amp;gt;
&amp;lt;/Document&amp;gt;
);
export default MyDocument;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Here, we've created a simple table with headers and data rows. Each item in the &lt;code&gt;data&lt;/code&gt; array becomes a row in the table, and each property of the item becomes a cell in that row. The styling makes sure the table looks neat and professional in the PDF document.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&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%2F5s7mj1j3cfjqpvcym000.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%2F5s7mj1j3cfjqpvcym000.png" alt="Table in pdf" width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, at the end, we can code a footer. The code below creates a footer with an image and a text displaying the total cost.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// style code after table styles...

  footerContainer: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center',
    marginTop: 20
  },
  footerText: {
    fontSize: 12
  },
  totalCost: {
    fontSize: 12,
    fontWeight: 'bold'
  }

//... After table code add footer

&amp;lt;View style={styles.footerContainer}&amp;gt;
  &amp;lt;Image style={styles.image} src="https://static.vecteezy.com/system/resources/thumbnails/013/993/061/small/mugiwara-the-illustration-vector.jpg" /&amp;gt;
  &amp;lt;Text style={styles.totalCost}&amp;gt;Total Cost: ${calculateTotalCost()}&amp;lt;/Text&amp;gt;
&amp;lt;/View&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;code&gt;View&lt;/code&gt; acts as the main container for the footer. The &lt;code&gt;styles.footerContainer&lt;/code&gt; style defines how the footer is displayed, including its layout, padding, margin, and alignment. The &lt;code&gt;Image&lt;/code&gt; component displays an image, while the &lt;code&gt;Text&lt;/code&gt; component shows the total cost.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this blog, we covered how to use React PDF Renderer to convert React components into high-quality PDFs. We covered the key components, including Document, Page, View, Text, Image, and Link, and explained their uses and styling. We covered, creating a basic PDF document, adding pages, styling, and building complex structures like tables and footers. By following this, you can easily transform your web content into shareable PDFs using React.&lt;/p&gt;

&lt;p&gt;Thanks for reading this blog. If you learned something from it, please like and share it with your friends and community. I write blogs and share content on JavaScript, TypeScript, Open Source, and other web development-related topics. Feel free to follow me on my socials. I'll see you in the next one. Thank You :)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://x.com/Shivamkatare_27" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/shivam-katare/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Shivam-Katare" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://react-pdf.org/" rel="noopener noreferrer"&gt;Official React PDF Renderer Docs&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.npmjs.com/package/@react-pdf/renderer" rel="noopener noreferrer"&gt;React PDF Renderer NPM&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://accessible-mango-68a.notion.site/React-PDF-Renderer-Code-7847872741da4f089546b844f09e3846" rel="noopener noreferrer"&gt;Complete Code&lt;/a&gt;&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>beginners</category>
      <category>react</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Speaking at a Tech Event for the First Time: My Experience</title>
      <dc:creator>Shivam Katare</dc:creator>
      <pubDate>Wed, 14 Aug 2024 15:16:24 +0000</pubDate>
      <link>https://dev.to/shivamkatare/speaking-at-a-tech-event-for-the-first-time-my-experience-418n</link>
      <guid>https://dev.to/shivamkatare/speaking-at-a-tech-event-for-the-first-time-my-experience-418n</guid>
      <description>&lt;p&gt;The first time for anything always feels special. It could be the first interview, the first job offer, the first salary, or anything else. The first time feels special because it gives us new learnings, new perspectives, and new ways to think about our next steps. In this blog, I am going to share my first-time experience as a speaker at a tech event and my first in-person meetup. The best part is, I attended my first tech event as a speaker.&lt;/p&gt;

&lt;p&gt;So, without further delay, let's get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;I am Shivam, a recently graduated computer science student, currently working as a frontend developer at an American-based startup. I love contributing to open-source projects, writing technical blogs, and being part of a community. With this passion, I always wanted to share my learnings at a tech event. Finally, I got the chance to speak at a tech event on June 29, 2024 (yeah😅, I know I am publishing this a bit late, but better late than never). This date will always be memorable. I learned a lot and shared a lot. Let's explore all of that in the next sections.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Conference
&lt;/h2&gt;

&lt;p&gt;The conference that allowed me to speak at an in-person event was &lt;a href="https://jslovers.com/" rel="noopener noreferrer"&gt;JSLovers&lt;/a&gt;. For those who don't know, JSLovers is a well-known tech meetup community for JavaScript developers. It's a place where JavaScript enthusiasts gather to share knowledge, learn about the latest trends and network with like-minded individuals.&lt;/p&gt;

&lt;p&gt;I had the chance to speak at their Delhi meetup on the topic of &lt;strong&gt;React 19&lt;/strong&gt;. My presentation was titled "&lt;em&gt;React 19 and the Chamber of Secrets&lt;/em&gt;," and I covered topics of React 19 features, including server components, asset loading, actions, and enhanced hooks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deciding to apply
&lt;/h2&gt;

&lt;p&gt;In my third year of college (2023), I got to know about tech conferences. The first one I discovered was &lt;a href="https://www.reactindia.io/" rel="noopener noreferrer"&gt;React India&lt;/a&gt;. At that time, I was looking for a job and landed on the React India website because they were hiring volunteers. I applied but unfortunately didn't get selected. After applying, I started exploring their website and realized that these events not only feature experienced speakers but also welcome knowledgeable first-time speakers.&lt;/p&gt;

&lt;p&gt;I applied with a very poor CFP (Call for Proposals) because I didn't know how to write a good one, so it was rejected. However, from that experience, I set a goal. I realized that these events are a great opportunity to meet industry professionals, gain knowledge, and build confidence by speaking at events. From that point on, I applied to many conferences. Finally, JSLovers CFP was selected, and I got a chance to speak at a JSLovers Meetup. On May 30th, I saw their post mentioning that they had opened a CFP form, and a month earlier, my CFP had been rejected by &lt;a href="https://reactnexus.com/" rel="noopener noreferrer"&gt;ReactNexus&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%2Fsg7kz8pqld39f89b7oa7.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%2Fsg7kz8pqld39f89b7oa7.png" alt="cfp rejection mail from react nexus" width="800" height="326"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So, I was fully motivated, thinking, "&lt;em&gt;Now I know how to avoid those mistakes and write a better CFP&lt;/em&gt;." I followed the tweet and filled out the form.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1796164721638334933-984" src="https://platform.twitter.com/embed/Tweet.html?id=1796164721638334933"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1796164721638334933-984');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1796164721638334933&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;And yay🥳!!! It got selected. But how did this one get selected and not the others? What did I write in this CFP? and how was the experience? Let's explore all of this in the next section.&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%2Ftfvwtcf2qtk8sf38mnsj.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%2Ftfvwtcf2qtk8sf38mnsj.png" alt="acceptance mail" width="800" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Experience
&lt;/h2&gt;

&lt;p&gt;I had an online demo session with the JSLovers team when I received the email. During this session, I received feedback and suggestions to improve my presentation. After adding their input, I was very excited about the talk. The event was initially scheduled for June 22nd but was later moved to June 29th.&lt;/p&gt;

&lt;p&gt;On June 28th, I was nervous. I was worried about what would happen at the event, how I would start conversations with people I didn't know, where I would find a place to sit, and what I should say when I got there. I had never been to an in-person event before, and because I'm a bit shy and introverted, these thoughts made me feel anxious. &lt;em&gt;I would appreciate any tips on handling these kinds of situations, so please share your advice in the comments below.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I woke up around 4 AM, got ready, and took the metro at 6:30 AM. Despite my nervousness, I was ready to hit the stage.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1806853503194955952-178" src="https://platform.twitter.com/embed/Tweet.html?id=1806853503194955952"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1806853503194955952-178');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1806853503194955952&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;I reached the destination. There were only a few people there (I arrived too early 😅), so I thought about recording some videos, but my shyness stopped me.&lt;/p&gt;

&lt;p&gt;I sat down without talking to anyone, just thinking about how I would talk to everyone. After 15 minutes, people started arriving, including the organizers. Meeting them made me feel a bit better. The event began about 20 minutes later. I was set to speak second, and I felt pretty good about what I was going to talk about. I was excited for my turn to speak.&lt;/p&gt;

&lt;p&gt;The first talk finished, and my name was announced. As I walked up to the stage, I felt a bit nervous again, but once I grabbed the mic, all my nervousness disappeared.&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%2Fvnhud5med3re8nbsgpxb.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%2Fvnhud5med3re8nbsgpxb.png" alt="me speaking on stage" width="800" height="599"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It was just me, my talk, and the audience with their questions and appreciation. My talk lasted about 40-45 minutes and went great. When I finished, everyone applauded and appreciated my talk. It felt like a hero moment, standing in front of everyone and receiving their appreciation for the effort that we made.&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%2F877olumgqt5heryncqo4.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%2F877olumgqt5heryncqo4.png" alt="me speaking on stage" width="800" height="715"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After that, a 30-minute break for networking began, and I connected with many people. We talked about jobs, tech, and personal views on various topics. It felt like we had known each other for a long time. It was a great experience.&lt;/p&gt;

&lt;p&gt;Following the break, there were two more talks about &lt;strong&gt;Polyfills&lt;/strong&gt; and &lt;strong&gt;TypeScript&lt;/strong&gt;. After those talks, the event ended. We took a group photo, clicked pictures with everyone, and then headed back home.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1808386771484340430-535" src="https://platform.twitter.com/embed/Tweet.html?id=1808386771484340430"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1808386771484340430-535');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1808386771484340430&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h2&gt;
  
  
  Learnings
&lt;/h2&gt;

&lt;p&gt;My first time speaking at a tech event was a really valuable experience. I learned a lot and gained some important lessons.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Preparation is key:&lt;/strong&gt; The online demo session with the JSLovers team was crucial. Their feedback helped me refine my presentation and boosted my confidence. Always be open to constructive criticism and use it to improve your content.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Embrace nervousness:&lt;/strong&gt; It's normal to feel anxious before your first speaking engagement. Instead of trying to get rid of these feelings, use them as positive energy. Remember, a bit of nervousness can keep you alert and focused.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Confidence grows on stage:&lt;/strong&gt; Even though I was nervous at first, I felt more confident once I started speaking. Trust in your preparation and your understanding of the topic.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Networking is priceless:&lt;/strong&gt; The connections made during the networking break were a highlight of the event. These interactions can lead to future opportunities, collaborations, or simply enriching professional relationships.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Community support:&lt;/strong&gt; The tech community, especially at events like these, is supportive and encouraging to newcomers. Don't hesitate to put yourself out there.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Follow-up matters:&lt;/strong&gt; After the event, I realized the importance of following up with the connections I made. Whether through LinkedIn or email, maintaining these relationships can be beneficial for future collaborations or opportunities.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Speaking at this tech event was about more than just giving a talk; it was about personal growth, engaging with the community, and professional development. These experiences have boosted my confidence for future speaking engagements and improved my approach to professional interactions in the tech world.&lt;/p&gt;

&lt;p&gt;For future events, I am very excited and am now learning more to prepare myself for big tech events. I have submitted my CFP for React India 2024 and am 🤞hoping for the best.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing an Effective CFP: How I Got Selected
&lt;/h2&gt;

&lt;p&gt;I am not experienced in writing CFPs, but below are a few things that I learned by writing many CFPs and learning from their rejections:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Focus on the "How" and "Why," not just the "What":&lt;/em&gt;&lt;/strong&gt; When selecting topics for your CFP, don't just choose to tell your audience "WHAT." Try to include "HOW" and "WHY" too. For example, if you choose React as your topic, don't just focus on "What is React?" Instead, mention and focus on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Why React?&lt;/li&gt;
&lt;li&gt;Why do we need it?&lt;/li&gt;
&lt;li&gt;Is it really better or necessary? If yes, how?&lt;/li&gt;
&lt;li&gt;What problems does it solve?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Organizers want to choose talks that make sense and provide value to their audience. Put yourself in the attendees' shoes - they expect great talks that offer valuable insights.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Be specific and unique:&lt;/strong&gt;&lt;/em&gt; Highlight what makes your talk different from others on similar topics. Share personal experiences or unique perspectives that can add value to the audience.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Clear and concise title:&lt;/strong&gt;&lt;/em&gt; Create a title that accurately represents your talk and catches attention. Avoid clickbait but make it intriguing.&lt;/p&gt;

&lt;p&gt;Let's say you're giving a talk about optimizing React applications for better performance. Here are a few examples of how you could craft the title:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Bad (too vague): "Making React Better"&lt;/li&gt;
&lt;li&gt;Bad (clickbait): "The ONE WEIRD TRICK to Make Your React App Lightning Fast!"&lt;/li&gt;
&lt;li&gt;Good: "React Performance Optimization: Strategies for Faster Apps"&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A good example is easy to understand, gets to the point without using too many words, and makes you interested in what it has to offer. It should be informative but not try to trick you into clicking on it, and it should be something that would appeal to people who work with React.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Well-structured description:&lt;/strong&gt;&lt;/em&gt; Provide a clear outline of what you'll cover. Use bullet points to break down the main topics you'll discuss.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Highlight takeaways:&lt;/strong&gt;&lt;/em&gt; Clearly state what the audience will learn or gain from your talk. This helps organizers understand the value you're bringing.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Proofread and edit:&lt;/strong&gt;&lt;/em&gt; Ensure your proposal is free of grammatical errors and typos. A well-written CFP demonstrates professionalism and attention to detail.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Submit early:&lt;/strong&gt;&lt;/em&gt; Don't wait until the last minute. Early submissions often get more attention and consideration.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;&lt;strong&gt;Learn from rejections:&lt;/strong&gt;&lt;/em&gt; If your CFP doesn't get selected, don't be discouraged. Ask for feedback when possible and use it to improve future submissions.&lt;/p&gt;

&lt;p&gt;For this particular CFP that got selected, I made sure to apply these principles. I focused not just on explaining React 19, but on why it's important, how it differs from previous versions, and what problems it solves for developers. I structured my proposal to show that I would provide practical insights and not just theoretical knowledge.&lt;/p&gt;

&lt;p&gt;Remember, when writing a CFP, approach it from an attendee's point of view. Submit a talk proposal that you would be excited to attend yourself. The goal isn't just to get selected; it's to share valuable learning with others and contribute meaningfully to the tech community.&lt;/p&gt;

&lt;p&gt;By following these things and continuously improving my CFP writing skills, I was able to create a proposal that resonated with the event organizers and ultimately led to my selection as a speaker.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;That's it for this blog, I hope you enjoy reading it. I write blogs and share content on JavaScript, TypeScript, Open Source, and other web development-related topics. Feel free to follow me on my socials. I'll see you in the next one. Thank You :)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://twitter.com/Shivamkatare_27" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/in/shivam-katare/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Shivam-Katare" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>learning</category>
      <category>career</category>
      <category>techtalks</category>
      <category>beginners</category>
    </item>
    <item>
      <title>How to Create Dynamic Email Contact Form in Next.js Using Resend and Zod</title>
      <dc:creator>Shivam Katare</dc:creator>
      <pubDate>Sat, 10 Aug 2024 09:53:53 +0000</pubDate>
      <link>https://dev.to/shivamkatare/how-to-create-dynamic-email-contact-form-in-nextjs-using-resend-and-zod-50d</link>
      <guid>https://dev.to/shivamkatare/how-to-create-dynamic-email-contact-form-in-nextjs-using-resend-and-zod-50d</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Next.js is a powerful full-stack framework that allows us to build applications with both frontend and backend features. It's very flexible and can be used for everything from simple static websites to complex web apps. Today, we will use Next.js to build an email contact form.&lt;/p&gt;

&lt;p&gt;Forms are a key part of any website, letting users interact with the application. Whether it's for signing up, logging in, giving feedback, or collecting data, forms are vital for user experience. Without forms, a full-stack application wouldn't be able to gather and process user input properly.&lt;/p&gt;

&lt;p&gt;In this blog, I will show you how to create an email contact form using Next.js, Resend, and Zod (for form validation). We will cover setting up the project, designing the form, handling form submissions, and creating a separate API route. By the end, you will know how to build and add forms to your Next.js apps, ensuring your web app works well and is easy to use.&lt;/p&gt;

&lt;p&gt;So, without further delay, let's get started.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Resend?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://resend.com/" rel="noopener noreferrer"&gt;Resend&lt;/a&gt; is a modern email API for developers. It's designed to make sending emails from your applications simple, reliable, and scalable. Unlike traditional email services, Resend is built with developers in mind, offering a straightforward API that integrates seamlessly with various programming languages and frameworks, including Next.js.&lt;/p&gt;

&lt;p&gt;In our Next.js form project, we'll use Resend to send emails. When a user submits the form, we'll use Resend's API to send a confirmation email or process the form data as needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Zod?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://zod.dev/" rel="noopener noreferrer"&gt;Zod&lt;/a&gt; is a powerful tool for your data. It's a TypeScript-first library that helps you define and check the shape of your data. Think of it as setting rules for your data and then making sure the data matches those rules before you use it.&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%2Fgdma6uidg832qr8fqy8j.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%2Fgdma6uidg832qr8fqy8j.png" alt="Zod" width="800" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you're using TypeScript (and if you're not, you should consider it!), Zod plays nicely with it. It can automatically infer TypeScript types from your schemas, which is a huge time-saver. While TypeScript checks types at compile-time, Zod does it at runtime. This means you can catch data issues that might slip through static type checking. You can use Zod for all sorts of data validation scenarios, from simple form inputs to complex API responses.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project Setup
&lt;/h2&gt;

&lt;p&gt;Let's start by setting up our Next.js project with all the necessary dependencies. We'll use TypeScript for type safety, Tailwind CSS for styling, &lt;a href="https://ant.design/" rel="noopener noreferrer"&gt;Ant Design&lt;/a&gt; for UI components, Zod for form validation, and Resend for email functionality.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a new Next.js project with TypeScript:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx create-next-app@latest my-contact-form --typescript
cd my-contact-form
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Install additional dependencies:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add antd zod resend react-icons
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Setup Environment variables
&lt;/h2&gt;

&lt;p&gt;For sending an email, we will use Resend, so we need the Resend API key. Before starting our server, let's go to Resend and get our API keys. &lt;a href="https://resend.com" rel="noopener noreferrer"&gt;Click here to go to the Resend site&lt;/a&gt;, and click the sign-in button.&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%2F0gcseggow03nwd2nez3u.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%2F0gcseggow03nwd2nez3u.png" alt="Resend Homepage" width="800" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After signing in, you'll be redirected to this page. Here, you'll see all the emails you received from your form.&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%2Fqqrge676svdta6ulx4a4.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%2Fqqrge676svdta6ulx4a4.png" alt="Resend  dashboard page after sign in" width="800" height="353"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here, click on the API Keys section&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%2Fa1907gj9f2g5ycq1atzw.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%2Fa1907gj9f2g5ycq1atzw.png" alt="Resend  dashboard page after sign in" width="800" height="347"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And, generate an API key by clicking on this 👇 button&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%2Fclvzz819sfxooi4pfo8s.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%2Fclvzz819sfxooi4pfo8s.png" alt="Resend API keys Page" width="800" height="365"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, copy that API key and keep it safe. Next, open VSCode and create a new file named .env in your root folder. Add an environment variable there.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Now you can also run your server using this command.&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Email Template Component
&lt;/h2&gt;

&lt;p&gt;Let's start by creating an email template. This will be the template you receive when someone sends you an email via the contact form.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import * as React from 'react';

interface EmailTemplateProps {
  firstName: string;
  message: string;
}

export const EmailTemplate: React.FC&amp;lt;EmailTemplateProps&amp;gt; = ({
  firstName,
  message,
}) =&amp;gt; (
  &amp;lt;div&amp;gt;
    &amp;lt;h1&amp;gt;Hello, I am {firstName}!&amp;lt;/h1&amp;gt;
    &amp;lt;p&amp;gt;You have received a new message from your Portfolio:&amp;lt;/p&amp;gt;
    &amp;lt;p&amp;gt;{message}&amp;lt;/p&amp;gt;
  &amp;lt;/div&amp;gt;
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This simple React component defines the structure of the email that will be sent when someone submits the contact form. It takes two props: &lt;code&gt;firstName&lt;/code&gt; and &lt;code&gt;message&lt;/code&gt;. The component creates a personalized greeting using the first name and displays the submitted message.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementing Email Sending with Resend API
&lt;/h2&gt;

&lt;p&gt;Here. we'll see how to implement email-sending functionality using the Resend API. &lt;/p&gt;

&lt;h3&gt;
  
  
  The Code Structure
&lt;/h3&gt;

&lt;p&gt;First, let's look at where this code lives in our Next.js project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;app/
  ├── api/
  │   └── v1/
  │       └── send/
  │           └── route.ts
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This structure follows Next.js 13's App Router convention, where API routes are defined as route handlers within the app directory.&lt;/p&gt;

&lt;p&gt;This is our complete API route code 👇&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { EmailTemplate } from 'app/components/email-template';
import { NextResponse } from 'next/server';
import { Resend } from 'resend';
import { v4 as uuid } from 'uuid';

const resend = new Resend(process.env.RESEND_API_KEY);

export async function POST(req: Request) {
  try {
    const { name, email, subject, message } = await req.json();

    const { data, error } = await resend.emails.send({
      from: 'Contact Form &amp;lt;onboarding@resend.dev&amp;gt;',
      to: 'katare27451@gmail.com',
      subject: subject || 'New Contact Form Submission',
      reply_to: email,
      headers: {
        'X-Entity-Ref-ID': uuid(),
      },
      react: EmailTemplate({ firstName: name, message }) as React.ReactElement,
    });

    if (error) {
      return NextResponse.json({ error }, { status: 500 });
    }

    return NextResponse.json({ data, message: 'success' }, { status: 200 });
  } catch (error) {
    console.error('Error processing request:', error);
    return NextResponse.json({ error: 'Failed to process request' }, { status: 500 });
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Breaking Down the Code
&lt;/h3&gt;

&lt;p&gt;Now, let's examine each part of 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;import { EmailTemplate } from 'app/components/email-template';
import { NextResponse } from 'next/server';
import { Resend } from 'resend';
import { v4 as uuid } from 'uuid';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These import statements bring in the necessary dependencies:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;EmailTemplate&lt;/strong&gt;: A custom React component for our email content(That we already built above.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NextResponse&lt;/strong&gt;: Next.js utility for creating API responses.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resend&lt;/strong&gt;: The Resend API client.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;uuid&lt;/strong&gt;: For generating unique identifiers.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const resend = new Resend(process.env.RESEND_API_KEY);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we initialize the Resend client with our API key. It's crucial to keep this key secret, so we store it in an environment variable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export async function POST(req: Request) {
  // ... (code inside this function)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This exports an async function named POST, which Next.js will automatically use to handle POST requests to this route.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const { name, email, subject, message } = await req.json();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We extract the form data from the request body. This assumes the client is sending a JSON payload with these fields.&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, error } = await resend.emails.send({
  from: 'Contact Form &amp;lt;onboarding@resend.dev&amp;gt;',
  to: 'katare27451@gmail.com',
  subject: subject || 'New Contact Form Submission',
  reply_to: email,
  headers: {
    'X-Entity-Ref-ID': uuid(),
  },
  react: EmailTemplate({ firstName: name, message }) as React.ReactElement,
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is where we'll get our emails! We use Resend's &lt;code&gt;send&lt;/code&gt; method to dispatch the email:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;from&lt;/code&gt;: The sender's email address.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;to&lt;/code&gt;: The recipient's email address.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;subject&lt;/code&gt;: The email subject, using the provided subject or a default.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;reply_to&lt;/code&gt;: Sets the reply-to address to the form submitter's email.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;headers&lt;/code&gt;: Includes a unique ID for tracking.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;react&lt;/code&gt;: Uses our custom EmailTemplate component to generate the email content.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (error) {
  return NextResponse.json({ error }, { status: 500 });
}

return NextResponse.json({ data, message: 'success' }, { status: 200 });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, we handle the response from Resend. If there's an error, we return a &lt;code&gt;500 status&lt;/code&gt; with the error details. Otherwise, we send a &lt;code&gt;success&lt;/code&gt; response.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;catch (error) {
  console.error('Error processing request:', error);
  return NextResponse.json({ error: 'Failed to process request' }, { status: 500 });
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This catch block handles any unexpected errors, logs them, and returns a generic error response.&lt;/p&gt;

&lt;p&gt;And that's it! We've set up our API route. The only thing left is to set up our logic and UI. Let's do that too 💪&lt;/p&gt;

&lt;h2&gt;
  
  
  Contact Page Component
&lt;/h2&gt;

&lt;p&gt;In your app directory, create a new folder named &lt;code&gt;contact-form&lt;/code&gt; and inside this folder, create a file named &lt;code&gt;page.tsx&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;app/
  ├── contact-form/
  │   └── page.tsx
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Imports and Dependencies
&lt;/h3&gt;

&lt;p&gt;First, import all necessary components from Ant Design, Next.js, and React Icons. We also import Zod for form validation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from 'react';
import { Form, Input, Button, message, Space, Divider, Typography } from 'antd';
import Head from 'next/head';
import { FaUser } from 'react-icons/fa';
import { MdMail } from 'react-icons/md';
import { FaMessage } from 'react-icons/fa6';
import { z } from 'zod';
import Paragraph from 'antd/es/typography/Paragraph';
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  UI Layout and Design
&lt;/h3&gt;

&lt;p&gt;Now, let's create our UI, and then we'll move on to the logic. Our form will look something like this.👇&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%2Feieiz0mjdo1xb8kias3o.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%2Feieiz0mjdo1xb8kias3o.png" alt="contact form ui" width="800" height="617"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In your &lt;code&gt;page.tsx&lt;/code&gt;, after all the import statements, define a component and add a return statement first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const ContactPage: React.FC = () =&amp;gt; {
  return (
      &amp;lt;div className="max-w-3xl w-full space-y-8 bg-white p-10 rounded-xl shadow-2xl"&amp;gt;
       /* our code will be here */
      &amp;lt;/div&amp;gt;
  );
};

export default ContactPage;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Currently, we have just a simple div with a few tailwind styling now, we'll first add our heading part.&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%2Fabpk3ctxmdo44cu0l8jh.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%2Fabpk3ctxmdo44cu0l8jh.png" alt="contact form ui" width="800" height="360"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

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

        &amp;lt;div&amp;gt;
          &amp;lt;h2 className="mt-1 text-center text-3xl font-extrabold text-gray-900"&amp;gt;Get in Touch&amp;lt;/h2&amp;gt;
          &amp;lt;p className="mt-1 mb-4 text-center text-sm text-gray-600"&amp;gt;
            I'd love to hear from you! Fill out the form below to get in touch.
          &amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;

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

&lt;/div&gt;



&lt;p&gt;Now, let's add our input fields&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%2Fbqnyhatsj0kg0mei3fqj.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%2Fbqnyhatsj0kg0mei3fqj.png" alt="contact form ui" width="800" height="596"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; ...     
        &amp;lt;Form
          form={form}
          name="contact"
          onFinish={onFinish}
          layout="vertical"
          className="mt-8 space-y-6"
        &amp;gt;
          &amp;lt;Form.Item
            name="name"
            rules={[{ required: true, message: 'Please input your name!' }]}
          &amp;gt;
            &amp;lt;Input prefix={&amp;lt;FaUser className="site-form-item-icon" /&amp;gt;} placeholder="Your Name" size="large" /&amp;gt;
          &amp;lt;/Form.Item&amp;gt;
          &amp;lt;Form.Item
            name="email"
            rules={[
              { required: true, message: 'Please input your email!' },
              { type: 'email', message: 'Please enter a valid email!' }
            ]}
          &amp;gt;
            &amp;lt;Input prefix={&amp;lt;MdMail className="site-form-item-icon" /&amp;gt;} placeholder="Your Email" size="large" /&amp;gt;
          &amp;lt;/Form.Item&amp;gt;
          &amp;lt;Form.Item
            name="subject"
            rules={[{ required: true, message: 'Please input a subject!' }]}
          &amp;gt;
            &amp;lt;Input prefix={&amp;lt;FaMessage className="site-form-item-icon" /&amp;gt;} placeholder="Subject" size="large" /&amp;gt;
          &amp;lt;/Form.Item&amp;gt;
          &amp;lt;Form.Item
            name="message"
            rules={[{ required: true, message: 'Please input your message!' }]}
          &amp;gt;
            &amp;lt;TextArea
              placeholder="Your Message"
              autoSize={{ minRows: 4, maxRows: 8 }}
            /&amp;gt;
          &amp;lt;/Form.Item&amp;gt;
          &amp;lt;Form.Item&amp;gt;
            &amp;lt;Button
              type="primary"
              htmlType="submit"
              className="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
              disabled={isSubmitting}
            &amp;gt;
              {isSubmitting ? 'Sending...' : 'Send Message'}
            &amp;lt;/Button&amp;gt;
          &amp;lt;/Form.Item&amp;gt;
        &amp;lt;/Form&amp;gt;
...

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

&lt;/div&gt;



&lt;p&gt;Here, in the above code firstly we added a Form Component. This is the main Form component from Ant Design. It has the following props:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Form
  form={form}
  name="contact"
  onFinish={onFinish}
  layout="vertical"
  className="mt-8 space-y-6"
&amp;gt;
  {/* Form items */}
&amp;lt;/Form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;form&lt;/code&gt;: Links the form to the form object created using Form.useForm().&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;name&lt;/code&gt;: Gives the form a name, in this case, "contact".&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;onFinish&lt;/code&gt;(we'll declare this function in our next section): Specifies the function to be called when the form is submitted successfully.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;layout&lt;/code&gt;: Sets the form layout to "vertical".&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;className&lt;/code&gt;: Applies CSS classes for styling.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then, we added a &lt;strong&gt;Form Items&lt;/strong&gt;. Each &lt;code&gt;Form.Item&lt;/code&gt; represents a field in the form. Let's look at the "&lt;code&gt;name&lt;/code&gt;" field as an example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Form.Item
  name="name"
  rules={[{ required: true, message: 'Please input your name!' }]}
&amp;gt;
  &amp;lt;Input prefix={&amp;lt;FaUser className="site-form-item-icon" /&amp;gt;} placeholder="Your Name" size="large" /&amp;gt;
&amp;lt;/Form.Item&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;name&lt;/code&gt;: Specifies the field name.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;rules&lt;/code&gt;: An array of validation rules. Here, it's set as required.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;Input&lt;/code&gt; component is used for text input, with a user icon prefix and a placeholder.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Similarly, we have Email and other fields.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Form.Item
  name="email"
  rules={[
    { required: true, message: 'Please input your email!' },
    { type: 'email', message: 'Please enter a valid email!' }
  ]}
&amp;gt;
  &amp;lt;Input prefix={&amp;lt;MdMail className="site-form-item-icon" /&amp;gt;} placeholder="Your Email" size="large" /&amp;gt;
&amp;lt;/Form.Item&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This field has an additional rule to ensure the input is a valid email address.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Subject and Message Fields&lt;/strong&gt;: These are similar to the name field, with the message field using a &lt;code&gt;TextArea&lt;/code&gt; component for multi-line input.&lt;/p&gt;

&lt;p&gt;Then, we have a Submit Button to submit our form&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;Form.Item&amp;gt;
  &amp;lt;Button
    type="primary"
    htmlType="submit"
    className="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
    disabled={isSubmitting}
  &amp;gt;
    {isSubmitting ? 'Sending...' : 'Send Message'}
  &amp;lt;/Button&amp;gt;
&amp;lt;/Form.Item&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is the submit button for the form. It's disabled when &lt;code&gt;isSubmitting&lt;/code&gt; (we'll add this state in our next section) is true, and its text changes to "&lt;code&gt;Sending...&lt;/code&gt;" during submission.&lt;/p&gt;

&lt;h2&gt;
  
  
  Form Submission Logic
&lt;/h2&gt;

&lt;p&gt;So, in the logic part, we have a few things to do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Setting up Zod schema for form validation&lt;/li&gt;
&lt;li&gt;Adding new states&lt;/li&gt;
&lt;li&gt;and, implementing a &lt;code&gt;onFinish&lt;/code&gt; function&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We'll start with setting up our schema first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Zod schema for form validation
const contactSchema = z.object({
  name: z.string().min(4, 'Name must be at least 4 characters').max(50, 'Name must not exceed 50 characters'),
  email: z.string().email('Invalid email address').regex(/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/, "Email must be a valid format"),
  subject: z.string().min(5, 'Subject must be at least 5 characters').max(100, 'Subject must not exceed 100 characters'),
  message: z.string().min(20, 'Message must be at least 20 characters').max(1000, 'Message must not exceed 1000 characters'),
});

type ContactFormData = z.infer&amp;lt;typeof contactSchema&amp;gt;;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This part defines a Zod schema for form validation. As we already learned, Zod is a TypeScript-first schema declaration and validation library. The &lt;code&gt;contactSchema&lt;/code&gt; object defines the structure and validation rules for the contact form:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;name&lt;/code&gt;: Must be a string between 4 and 50 characters.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;email&lt;/code&gt;: Must be a valid email address and match the specified regex pattern.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;subject&lt;/code&gt;: Must be a string between 5 and 100 characters.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;message&lt;/code&gt;: Must be a string between 20 and 1000 characters.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;ContactFormData&lt;/code&gt; type is inferred from the Zod schema, providing type safety for the form data.&lt;/p&gt;

&lt;p&gt;Now, let's add 2 new states and implement our &lt;code&gt;onFinish&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const ContactPage: React.FC = () =&amp;gt; {
  const [form] = Form.useForm&amp;lt;ContactFormData&amp;gt;();
  const [isSubmitting, setIsSubmitting] = React.useState(false);

  const onFinish = async (values: ContactFormData) =&amp;gt; {
    setIsSubmitting(true);
    try {
      contactSchema.parse(values);

      const response = await fetch('/api/v1/send', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(values),
      });

      if (!response.ok) {
        message.error('Failed to send message. Please try again.');
        setIsSubmitting(false); 
      }

      const data = await response.json();

      if (data.message === 'success') {
        message.success('Message sent successfully!');
        setIsSubmitting(false);
        form.resetFields();
      } else {
        throw new Error('Failed to send message');
      }
    } catch (error) {
      if (error instanceof z.ZodError) {
        error.errors.forEach((err) =&amp;gt; {
          message.error(err.message);
          setIsSubmitting(false);
        });
      } else {
        message.error('Failed to send message. Please try again.');
        setIsSubmitting(false);
      }
    } finally {
      setIsSubmitting(false);
    }
  };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This part defines the &lt;code&gt;ContactPage&lt;/code&gt; functional component:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It uses the Form.useForm hook to create a form instance.&lt;/li&gt;
&lt;li&gt;It manages a &lt;code&gt;isSubmitting&lt;/code&gt; state to track form submission status.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;onFinish&lt;/code&gt; function is called when the form is submitted: &lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;It sets isSubmitting to true.&lt;/li&gt;
&lt;li&gt;It uses contactSchema.parse(values) to validate the form data against the Zod schema.&lt;/li&gt;
&lt;li&gt;If validation passes, it sends a POST request to /api/v1/send with the form data.&lt;/li&gt;
&lt;li&gt;It handles the response, showing success or error messages accordingly.&lt;/li&gt;
&lt;li&gt;If there's a Zod validation error, it displays the error message.&lt;/li&gt;
&lt;li&gt;Finally, it sets isSubmitting back to false.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This setup ensures that the form data is validated on both the client-side (using Antd's form validation) and the server-side (using Zod schema validation) before sending the data to the server. It also manages the submission state and provides user feedback through success or error messages.&lt;/p&gt;

&lt;p&gt;And, this is the complete code of our contact-form file 👇&lt;br&gt;
&lt;/p&gt;

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

import React from 'react';
import { Form, Input, Button, message, Space, Divider, Typography } from 'antd';
import Head from 'next/head';
import { FaUser } from 'react-icons/fa';
import { MdMail } from 'react-icons/md';
import { FaMessage } from 'react-icons/fa6';
import { z } from 'zod';
import { Container } from 'app/components/container';
import Paragraph from 'antd/es/typography/Paragraph';

const { TextArea } = Input;
const { Text } = Typography;

// Zod schema for form validation
const contactSchema = z.object({
  name: z.string().min(4, 'Name must be at least 4 characters').max(50, 'Name must not exceed 50 characters'),
  email: z.string().email('Invalid email address').regex(/^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/, "Email must be a valid format"),
  subject: z.string().min(5, 'Subject must be at least 5 characters').max(100, 'Subject must not exceed 100 characters'),
  message: z.string().min(20, 'Message must be at least 20 characters').max(1000, 'Message must not exceed 1000 characters'),
});

type ContactFormData = z.infer&amp;lt;typeof contactSchema&amp;gt;;

const ContactPage: React.FC = () =&amp;gt; {
  const [form] = Form.useForm&amp;lt;ContactFormData&amp;gt;();
  const [isSubmitting, setIsSubmitting] = React.useState(false);

  const onFinish = async (values: ContactFormData) =&amp;gt; {
    setIsSubmitting(true);
    try {
      contactSchema.parse(values);

      const response = await fetch('/api/v1/send', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(values),
      });

      if (!response.ok) {
        message.error('Failed to send message. Please try again.');
        setIsSubmitting(false); 
      }

      const data = await response.json();

      if (data.message === 'success') {
        message.success('Message sent successfully!');
        setIsSubmitting(false);
        form.resetFields();
      } else {
        throw new Error('Failed to send message');
      }
    } catch (error) {
      if (error instanceof z.ZodError) {
        error.errors.forEach((err) =&amp;gt; {
          message.error(err.message);
          setIsSubmitting(false);
        });
      } else {
        message.error('Failed to send message. Please try again.');
        setIsSubmitting(false);
      }
    } finally {
      setIsSubmitting(false);
    }
  };

  return (
      &amp;lt;div className="max-w-3xl w-full space-y-8 bg-white p-10 rounded-xl shadow-2xl"&amp;gt;
        &amp;lt;div&amp;gt;
          &amp;lt;h2 className="mt-1 text-center text-3xl font-extrabold text-gray-900"&amp;gt;Get in Touch&amp;lt;/h2&amp;gt;
          &amp;lt;p className="mt-1 mb-4 text-center text-sm text-gray-600"&amp;gt;
            I'd love to hear from you! Fill out the form below to get in touch.
          &amp;lt;/p&amp;gt;
        &amp;lt;/div&amp;gt;
        &amp;lt;Form
          form={form}
          name="contact"
          onFinish={onFinish}
          layout="vertical"
          className="mt-8 space-y-6"
        &amp;gt;
          &amp;lt;Form.Item
            name="name"
            rules={[{ required: true, message: 'Please input your name!' }]}
          &amp;gt;
            &amp;lt;Input prefix={&amp;lt;FaUser className="site-form-item-icon" /&amp;gt;} placeholder="Your Name" size="large" /&amp;gt;
          &amp;lt;/Form.Item&amp;gt;
          &amp;lt;Form.Item
            name="email"
            rules={[
              { required: true, message: 'Please input your email!' },
              { type: 'email', message: 'Please enter a valid email!' }
            ]}
          &amp;gt;
            &amp;lt;Input prefix={&amp;lt;MdMail className="site-form-item-icon" /&amp;gt;} placeholder="Your Email" size="large" /&amp;gt;
          &amp;lt;/Form.Item&amp;gt;
          &amp;lt;Form.Item
            name="subject"
            rules={[{ required: true, message: 'Please input a subject!' }]}
          &amp;gt;
            &amp;lt;Input prefix={&amp;lt;FaMessage className="site-form-item-icon" /&amp;gt;} placeholder="Subject" size="large" /&amp;gt;
          &amp;lt;/Form.Item&amp;gt;
          &amp;lt;Form.Item
            name="message"
            rules={[{ required: true, message: 'Please input your message!' }]}
          &amp;gt;
            &amp;lt;TextArea
              placeholder="Your Message"
              autoSize={{ minRows: 4, maxRows: 8 }}
            /&amp;gt;
          &amp;lt;/Form.Item&amp;gt;
          &amp;lt;Form.Item&amp;gt;
            &amp;lt;Button
              type="primary"
              htmlType="submit"
              className="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
              disabled={isSubmitting}
            &amp;gt;
              {isSubmitting ? 'Sending...' : 'Send Message'}
            &amp;lt;/Button&amp;gt;
          &amp;lt;/Form.Item&amp;gt;
        &amp;lt;/Form&amp;gt;
      &amp;lt;/div&amp;gt;
 );
};

export default ContactPage;

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;Till now, we're all set, now it's time to run and test our application.&lt;/p&gt;

&lt;p&gt;Start your server:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;First, let's try to hit the endpoint without filling out the form. As expected, the API doesn't get called, and we receive error messages.&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%2Ftn2timf8grhq5av8c0um.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%2Ftn2timf8grhq5av8c0um.png" alt="error check in contact form" width="800" height="622"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, let's fill out the form&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%2F7h5zyl72fw8io72x7r4g.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%2F7h5zyl72fw8io72x7r4g.png" alt="filled contact form" width="800" height="598"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;and hit the send button. It's in process.&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%2Fcflpk2smdf21ebq74cjm.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%2Fcflpk2smdf21ebq74cjm.png" alt="form data sending" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here we go, the message is sent. The sender receives a notification saying "Message sent," and the form is also refreshed.&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%2Fjo24oad70kpbb4fvb48b.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%2Fjo24oad70kpbb4fvb48b.png" alt="message successfully sent" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The receiver also gets the message🥳&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%2Fix1yn01cr1m680d0fcnu.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%2Fix1yn01cr1m680d0fcnu.png" alt="message recived" width="800" height="1063"&gt;&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%2Fce61en5cph0rmzevel32.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%2Fce61en5cph0rmzevel32.png" alt="message recived" width="800" height="1063"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And that's it. We have successfully built an email contact form in Next.js using Resend and Zod.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, we built a contact form using Next.js and implemented features like form validation with Zod and email functionality with Resend. We start by setting up the Next.js project, configuring necessary dependencies, and managing environment variables for secure API access. &lt;/p&gt;

&lt;p&gt;Then, we designed the email template, set up an API route for handling email submissions, and implemented the frontend form with Ant Design components.&lt;/p&gt;

&lt;p&gt;If you want to see a live preview of it, you can check it out &lt;a href="https://shivamkatare.vercel.app/contact-me" rel="noopener noreferrer"&gt;here&lt;/a&gt;. I have implemented the same form in my portfolio.&lt;/p&gt;

&lt;p&gt;Thanks for reading this blog. If you learned something from it, please like and share it with your friends and community. I write blogs and share content on JavaScript, TypeScript, Open Source, and other web development-related topics. Feel free to follow me on my socials. I'll see you in the next one. Thank You :)&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/Shivamkatare_27" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/in/shivam-katare/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/Shivam-Katare" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>nextjs</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
