{"id":4348,"date":"2022-07-05T20:25:29","date_gmt":"2022-07-05T20:25:29","guid":{"rendered":"https:\/\/codevoweb.com\/?p=4348"},"modified":"2023-05-23T12:14:01","modified_gmt":"2023-05-23T12:14:01","slug":"fullstack-app-trpc-reactjs-nodejs-jwt-authentication","status":"publish","type":"post","link":"https:\/\/codevoweb.com\/fullstack-app-trpc-reactjs-nodejs-jwt-authentication\/","title":{"rendered":"Full-Stack App tRPC, React.js, &#038; Node.js: JWT Authentication"},"content":{"rendered":"\n<p><a href=\"https:\/\/trpc.io\/\" target=\"_blank\" rel=\"noreferrer noopener\">tRPC<\/a> is a toolkit that allows developers to statically type their API endpoints and share those types between the client and server, without installing extra libraries for code generation.<\/p>\n\n\n\n<p>When we build full-stack applications with TypeScript, it becomes difficult to share types between the frontend and the backend.<\/p>\n\n\n\n<p>Currently, GraphQL is the dominant library used to build and consume fully typesafe APIs in TypeScript. It offers a radically new approach to building type-safety APIs with more flexibility and control.<\/p>\n\n\n\n<p>However, since GraphQL is a query language, it doesn&#8217;t leverage the full power of TypeScript and depends on code generation and already-defined schemas to implement typesafe APIs.<\/p>\n\n\n\n<p>This is where <a href=\"https:\/\/trpc.io\/\" target=\"_blank\" rel=\"noreferrer noopener\">tRPC<\/a> was introduced to solve the complexities and flaws of GraphQL. tRPC allows us to build full-stack TypeScript applications and share the types directly between the client and server.<\/p>\n\n\n\n<p>In this article, you&#8217;ll learn how to add JWT Authentication to your React.js, TypeScript, <a href=\"https:\/\/trpc.io\/\" target=\"_blank\" rel=\"noreferrer noopener\">tRPC<\/a> Client, tailwindCss and React Query projects.<\/p>\n\n\n\n<p>tRPC Client and Server with React.js, Express, and Node.js Series:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li><a href=\"\/trpc-api-reactjs-nodejs-mongodb-project-setup\">Build tRPC API with React.js, Node.js &amp; MongoDB: Project Setup<\/a><\/li>\n\n\n\n<li><a href=\"\/trpc-api-with-reactjs-nodejs-access-and-refresh-tokens\">Build tRPC API with React.js &amp; Node.js: Access and Refresh Tokens<\/a><\/li>\n\n\n\n<li><a href=\"\/fullstack-app-trpc-reactjs-nodejs-jwt-authentication\">Full-Stack App tRPC, React.js, &amp; Node.js: JWT Authentication<\/a><\/li>\n<\/ol>\n\n\n\n<p>More practice:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"\/react-material-ui-and-react-hook-form-html-forms\">React, Material UI and React Hook Form: Login and Signup Forms<\/a><\/li>\n\n\n\n<li><a href=\"\/react-rtk-query-react-hook-form-and-material-ui-multipart-formdata\">React, RTK Query, React Hook Form and Material UI &#8211; Image Upload<\/a><\/li>\n\n\n\n<li><a href=\"\/react-redux-toolkit-jwt-authentication-and-authorization\">React + Redux Toolkit: JWT Authentication and Authorization<\/a><\/li>\n\n\n\n<li><a href=\"\/react-redux-toolkit-refresh-token-authentication\">React.js + Redux Toolkit: Refresh Tokens Authentication<\/a><\/li>\n\n\n\n<li><a href=\"\/vue-query-and-axios-crud-app\">Build Vue.js, Vue Query, and Axios CRUD App with RESTful API<\/a><\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"850\" height=\"478\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/Full-Stack-App-tRPC-React.js-Node.js-JWT-Authentication.webp\" alt=\"Full-Stack App tRPC, React.js, &amp; Node.js JWT Authentication\" class=\"wp-image-4401\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/Full-Stack-App-tRPC-React.js-Node.js-JWT-Authentication.webp 850w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/Full-Stack-App-tRPC-React.js-Node.js-JWT-Authentication-300x169.webp 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/Full-Stack-App-tRPC-React.js-Node.js-JWT-Authentication-768x432.webp 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/Full-Stack-App-tRPC-React.js-Node.js-JWT-Authentication-100x56.webp 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/Full-Stack-App-tRPC-React.js-Node.js-JWT-Authentication-700x394.webp 700w\" sizes=\"auto, (max-width: 850px) 100vw, 850px\" \/><\/figure>\n\n\n<style>.kb-table-of-content-nav.kb-table-of-content-id_93fe97-cd .kb-table-of-content-wrap{padding-top:10px;padding-right:10px;padding-bottom:10px;padding-left:10px;border-top:1px solid #abb8c3;border-right:1px solid #abb8c3;border-bottom:1px solid #abb8c3;border-left:1px solid #abb8c3;}.kb-table-of-content-nav.kb-table-of-content-id_93fe97-cd .kb-table-of-contents-title-wrap{padding-top:0px;padding-right:0px;padding-bottom:0px;padding-left:0px;}.kb-table-of-content-nav.kb-table-of-content-id_93fe97-cd .kb-table-of-contents-title-wrap{color:#ffffff;}.kb-table-of-content-nav.kb-table-of-content-id_93fe97-cd .kb-table-of-contents-title{color:#ffffff;font-weight:regular;font-style:normal;}.kb-table-of-content-nav.kb-table-of-content-id_93fe97-cd .kb-table-of-content-wrap .kb-table-of-content-list{color:#ffffff;font-weight:regular;font-style:normal;margin-top:10px;margin-right:0px;margin-bottom:0px;margin-left:-5px;}@media all and (max-width: 1024px){.kb-table-of-content-nav.kb-table-of-content-id_93fe97-cd .kb-table-of-content-wrap{border-top:1px solid #abb8c3;border-right:1px solid #abb8c3;border-bottom:1px solid #abb8c3;border-left:1px solid #abb8c3;}}@media all and (max-width: 767px){.kb-table-of-content-nav.kb-table-of-content-id_93fe97-cd .kb-table-of-content-wrap{border-top:1px solid #abb8c3;border-right:1px solid #abb8c3;border-bottom:1px solid #abb8c3;border-left:1px solid #abb8c3;}}<\/style>\n\n\n<h2 class=\"wp-block-heading\">Prerequisites<\/h2>\n\n\n\n<p>Before we begin, you should:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Have basic knowledge of JavaScript, TypeScript, and React.js<\/li>\n\n\n\n<li>Have basic knowledge of CSS and tailwindCss<\/li>\n<\/ul>\n\n\n\n<span id=\"ezoic-pub-video-placeholder-107\"><\/span>\n\n\n\n<h2 class=\"wp-block-heading\">React Query, tRPC Client, &amp; React.js Overview<\/h2>\n\n\n\n<p>The user clicks on the &#8220;<strong>SignUp<\/strong>&#8221; button on the Homepage to create an account.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"945\" height=\"1024\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/trpc-client-and-server-react-home-page-945x1024.png\" alt=\"trpc client and server react home page\" class=\"wp-image-4368\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/trpc-client-and-server-react-home-page-945x1024.png 945w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/trpc-client-and-server-react-home-page-277x300.png 277w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/trpc-client-and-server-react-home-page-768x832.png 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/trpc-client-and-server-react-home-page-92x100.png 92w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/trpc-client-and-server-react-home-page-415x450.png 415w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/trpc-client-and-server-react-home-page.png 997w\" sizes=\"auto, (max-width: 945px) 100vw, 945px\" \/><\/figure>\n\n\n\n<p>The user is taken to the signup page where he is required to provide his credentials.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"949\" height=\"1024\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/trpc-client-and-server-react-register-new-user-with-react-query-949x1024.png\" alt=\"trpc client and server react register new user with react query\" class=\"wp-image-4371\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/trpc-client-and-server-react-register-new-user-with-react-query-949x1024.png 949w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/trpc-client-and-server-react-register-new-user-with-react-query-278x300.png 278w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/trpc-client-and-server-react-register-new-user-with-react-query-768x829.png 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/trpc-client-and-server-react-register-new-user-with-react-query-93x100.png 93w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/trpc-client-and-server-react-register-new-user-with-react-query-417x450.png 417w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/trpc-client-and-server-react-register-new-user-with-react-query.png 1000w\" sizes=\"auto, (max-width: 949px) 100vw, 949px\" \/><\/figure>\n\n\n\n<p>Next, the user is redirected to the login page where he needs to provide his credentials to log in.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"951\" height=\"1024\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/trpc-client-and-server-react-login-user-with-react-query-951x1024.png\" alt=\"trpc client and server react login user with react query\" class=\"wp-image-4369\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/trpc-client-and-server-react-login-user-with-react-query-951x1024.png 951w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/trpc-client-and-server-react-login-user-with-react-query-279x300.png 279w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/trpc-client-and-server-react-login-user-with-react-query-768x827.png 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/trpc-client-and-server-react-login-user-with-react-query-93x100.png 93w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/trpc-client-and-server-react-login-user-with-react-query-418x450.png 418w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/trpc-client-and-server-react-login-user-with-react-query.png 1003w\" sizes=\"auto, (max-width: 951px) 100vw, 951px\" \/><\/figure>\n\n\n\n<p>After the tRPC Express server has successfully authenticated the user, React redirects the user to the profile page.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"947\" height=\"1024\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/trpc-client-and-server-react-profile-page-947x1024.png\" alt=\"trpc client and server react profile page\" class=\"wp-image-4370\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/trpc-client-and-server-react-profile-page-947x1024.png 947w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/trpc-client-and-server-react-profile-page-278x300.png 278w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/trpc-client-and-server-react-profile-page-768x830.png 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/trpc-client-and-server-react-profile-page-93x100.png 93w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/trpc-client-and-server-react-profile-page-416x450.png 416w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/trpc-client-and-server-react-profile-page.png 999w\" sizes=\"auto, (max-width: 947px) 100vw, 947px\" \/><\/figure>\n\n\n\n<p>You can inspect the &#8220;<strong>Application<\/strong>&#8221; tab in the dev tools to see the cookies returned by the <strong>tRPC<\/strong> Express server.<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"975\" height=\"1013\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/jwt-refresh-access-token-cookies-in-the-browser.png\" alt=\"jwt refresh access token cookies in the browser\" class=\"wp-image-1472\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/jwt-refresh-access-token-cookies-in-the-browser.png 975w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/jwt-refresh-access-token-cookies-in-the-browser-289x300.png 289w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/jwt-refresh-access-token-cookies-in-the-browser-768x798.png 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/jwt-refresh-access-token-cookies-in-the-browser-96x100.png 96w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/jwt-refresh-access-token-cookies-in-the-browser-433x450.png 433w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/05\/jwt-refresh-access-token-cookies-in-the-browser-600x623.png 600w\" sizes=\"auto, (max-width: 975px) 100vw, 975px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Setup React Query, tRPC Client, and React.js<\/h2>\n\n\n\n<p>We are going to use React.js, tRPC Client, and React Query along with the React binding for tRPC.<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p>Follow the <a href=\"\/trpc-api-reactjs-nodejs-mongodb-project-setup\">Project Setup<\/a> article to set up the <strong>tRPC<\/strong> client and server with React, Node.js, Express, and tailwindCss before continuing with this article.<\/p>\n<\/blockquote>\n\n\n\n<p>Now change the directory into the client folder <code>cd packages\/client<\/code> and run this command to install the <strong>AppRouter<\/strong> type we exported from the tRPC server.<\/p>\n\n\n\n<pre class=\"line-numbers language-shell\"><code>\nyarn add server@1.0.0\n<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>server<\/code> &#8211; is the name used in the tRPC server <strong>package.json<\/strong> file.<\/li>\n\n\n\n<li><code>@1.0.0<\/code> &#8211; is the version used in the tRPC server <strong>package.json<\/strong> file.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Managing State with Zustand<\/h2>\n\n\n\n<p>Redux is probably the most popular React state management library but it requires some amount of code to get it up and running with React. To update a single state, you will need to create actions, dispatch functions, and reducers for Redux to work properly.<\/p>\n\n\n\n<p>Similar to React Context API, Redux needs to be wrapped with a provider component to make the Redux&nbsp;<code>store<\/code>&nbsp;available to any nested components that need to access the Redux store.<\/p>\n\n\n\n<p>With <a href=\"https:\/\/github.com\/pmndrs\/zustand\" target=\"_blank\" rel=\"noreferrer noopener\">Zustand<\/a>, you can create a globally available state with few lines of code and inject it into any component, without the need to wrap a provider around your root app.<\/p>\n\n\n\n<p>Another exciting feature of <a href=\"https:\/\/github.com\/pmndrs\/zustand\" target=\"_blank\" rel=\"noreferrer noopener\">Zustand<\/a> is that you can retrieve data from your state in functions that are not React components.<\/p>\n\n\n\n<p>Before we start creating the React state with Zustand, create a <code>packages\/client\/src\/libs\/types.ts<\/code> file and add the following TypeScript types.<\/p>\n\n\n\n<p><strong>packages\/client\/src\/libs\/types.ts<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-ts\"><code>\nexport interface IUser {\n  name: string;\n  email: string;\n  role: string;\n  photo: string;\n  _id: string;\n  id: string;\n  createdAt: string;\n  updatedAt: string;\n  __v: number;\n}\n\n<\/code>\n<\/pre>\n\n\n\n<p>Open your terminal and change the directory to the client folder <code>cd packages\/client<\/code> and run this command to install the Zustand state management library:<\/p>\n\n\n\n<pre class=\"line-numbers language-shell\"><code>\nyarn add zustand\n<\/code>\n<\/pre>\n\n\n\n<p>Next, create a <code>packages\/client\/src\/store\/index.ts<\/code> file and add the code below to set up the Zustand store for our application.<\/p>\n\n\n\n<p><strong>packages\/client\/src\/store\/index.ts<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-ts\"><code>\nimport create from 'zustand';\nimport { IUser } from '..\/libs\/types';\n\ntype Store = {\n  authUser: IUser | null;\n  uploadingImage: boolean;\n  pageLoading: boolean;\n  setAuthUser: (user: IUser) =&gt; void;\n  setUploadingImage: (isUploading: boolean) =&gt; void;\n  setPageLoading: (isLoading: boolean) =&gt; void;\n};\n\nconst useStore = create&lt;Store&gt;((set) =&gt; ({\n  authUser: null,\n  uploadingImage: false,\n  pageLoading: false,\n  setAuthUser: (user) =&gt; set((state) =&gt; ({ ...state, authUser: user })),\n  setUploadingImage: (isUploading) =&gt;\n    set((state) =&gt; ({ ...state, uploadingImage: isUploading })),\n  setPageLoading: (isLoading) =&gt;\n    set((state) =&gt; ({ ...state, pageLoading: isLoading })),\n}));\n\nexport default useStore;\n<\/code>\n<\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>authUser<\/code> &#8211; This state will store the authenticated user&#8217;s credentials. Defaults to <strong>null<\/strong>.<\/li>\n\n\n\n<li><code>uploadingImage<\/code> &#8211; This state will indicate whether an image is being uploaded to Cloudinary.<\/li>\n\n\n\n<li><code>pageLoading<\/code> &#8211; This state will indicate whether a request is being processed by the tRPC server. When a request is in flight, it will be set to <strong>true<\/strong> and if the app gets a response from the tRPC API, it will be set to <strong>false<\/strong>.<\/li>\n\n\n\n<li><code>setAuthUser<\/code> &#8211; This function will add the authenticated user&#8217;s credentials to the &#8220;<strong>authUser<\/strong>&#8221; state.<\/li>\n\n\n\n<li><code>setUploadingImage<\/code> &#8211; This function will change the status of the &#8220;<strong>uploadingImage<\/strong>&#8221; state.<\/li>\n\n\n\n<li><code>setPageLoading<\/code> &#8211; This function will change the status of the &#8220;<strong>pageLoading<\/strong>&#8221; state.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Create React Components with tailwindCss<\/h2>\n\n\n\n<p>In React projects, it&#8217;s a good practice to make components reusable to follow the <strong>DRY<\/strong> (Don&#8217;t Repeat Yourself) principle. So to achieve this, we&#8217;ll create a couple of reusable React components and style them with tailwind CSS. Before that, change the directory to the client folder <code>cd packages\/client<\/code> and run this command to install these dependencies.<\/p>\n\n\n\n<pre class=\"line-numbers language-shell\"><code>\nyarn add react-router-dom tailwind-merge react-cookie\n<\/code>\n<\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code><a href=\"https:\/\/www.npmjs.com\/package\/react-router-dom\" target=\"_blank\" rel=\"noreferrer noopener\">react-router-dom<\/a><\/code> &#8211; A library that allows us to implement dynamic routing in React web apps.<\/li>\n\n\n\n<li><code><a href=\"https:\/\/www.npmjs.com\/package\/tailwind-merge\" target=\"_blank\" rel=\"noreferrer noopener\">tailwind-merge<\/a><\/code> &#8211; A library for merging&nbsp;<a href=\"https:\/\/tailwindcss.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Tailwind CSS<\/a>&nbsp;classes in JS without style conflicts.<\/li>\n\n\n\n<li><code><a href=\"https:\/\/www.npmjs.com\/package\/react-cookie\" target=\"_blank\" rel=\"noreferrer noopener\">react-cookie<\/a><\/code> &#8211; A library that uses React hooks to access and modify cookies.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Create a Header Component with tailwindCss<\/h3>\n\n\n\n<p>The first component will be a <strong>Header<\/strong>. This component will display a handful of navigation menus and it will also have logic for logging out a user. To do that, we&#8217;ll create a mutation hook to evoke the <code>logoutUser<\/code> procedure on the tRPC server.<\/p>\n\n\n\n<p>When the logout menu is clicked, the <code>handleLogout()<\/code> function will be called which will also evoke the <code>logoutUser()<\/code> method to trigger the mutation. The mutation hook will then evoke the &#8220;<strong>logoutUser<\/strong>&#8221; procedure to log the user out of the tRPC API.<\/p>\n\n\n\n<p><strong>packages\/client\/src\/components\/Header.tsx<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-tsx\"><code>\nimport { useQueryClient } from \"@tanstack\/react-query\";\nimport { Link } from \"react-router-dom\";\nimport useStore from \"..\/store\";\nimport { trpc } from \"..\/trpc\";\n\nconst Header = () =&gt; {\n  const store = useStore();\n  const user = store.authUser;\n\n  const queryClient = useQueryClient();\n  const { mutate: logoutUser } = trpc.logoutUser.useMutation({\n    onSuccess(data) {\n      queryClient.clear();\n      document.location.href = \"\/login\";\n    },\n    onError(error) {\n      queryClient.clear();\n      document.location.href = \"\/login\";\n    },\n  });\n\n  const handleLogout = () =&gt; {\n    logoutUser();\n  };\n\n  return (\n    &lt;header className=\"bg-white h-20\"&gt;\n      &lt;nav className=\"h-full flex justify-between container items-center\"&gt;\n        &lt;div&gt;\n          &lt;Link to=\"\/\" className=\"text-ct-dark-600 text-2xl font-semibold\"&gt;\n            CodevoWeb\n          &lt;\/Link&gt;\n        &lt;\/div&gt;\n        &lt;ul className=\"flex items-center gap-4\"&gt;\n          &lt;li&gt;\n            &lt;Link to=\"\/\" className=\"text-ct-dark-600\"&gt;\n              Home\n            &lt;\/Link&gt;\n          &lt;\/li&gt;\n          {!user &amp;&amp; (\n            &lt;&gt;\n              &lt;li&gt;\n                &lt;Link to=\"\/register\" className=\"text-ct-dark-600\"&gt;\n                  SignUp\n                &lt;\/Link&gt;\n              &lt;\/li&gt;\n              &lt;li&gt;\n                &lt;Link to=\"\/login\" className=\"text-ct-dark-600\"&gt;\n                  Login\n                &lt;\/Link&gt;\n              &lt;\/li&gt;\n            &lt;\/&gt;\n          )}\n          {user &amp;&amp; (\n            &lt;&gt;\n              &lt;li&gt;\n                &lt;Link to=\"\/profile\" className=\"text-ct-dark-600\"&gt;\n                  Profile\n                &lt;\/Link&gt;\n              &lt;\/li&gt;\n              &lt;li className=\"cursor-pointer\"&gt;Create Post&lt;\/li&gt;\n              &lt;li className=\"cursor-pointer\" onClick={handleLogout}&gt;\n                Logout\n              &lt;\/li&gt;\n            &lt;\/&gt;\n          )}\n        &lt;\/ul&gt;\n      &lt;\/nav&gt;\n    &lt;\/header&gt;\n  );\n};\n\nexport default Header;\n<\/code>\n<\/pre>\n\n\n\n<p>The tRPC API will then send expired cookies to delete the existing ones in the browser. If the mutation resolves in success or error, React Query will evoke the<code>queryClient.clear()<\/code> method to clear the server state before React will redirect the user to the login page.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Create a Loading Spinner with tailwindCss<\/h3>\n\n\n\n<p>The next reusable component will be a <strong>Spinner<\/strong>. This component will accept a couple of props to make it customizable. By default, the Spinner will be hidden but it will be made visible when a background process is running or the API is processing the request.<\/p>\n\n\n\n<p>To create the Spinner component, go into the <code>packages\/client\/src\/components<\/code> folder and create a <code>Spinner.tsx<\/code> file. Then, open the <strong>Spinner.tsx<\/strong> file and add this TSX code.<\/p>\n\n\n\n<p><strong>packages\/client\/src\/components\/Spinner.tsx<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-tsx\"><code>\nimport React from 'react';\nimport { twMerge } from 'tailwind-merge';\ntype SpinnerProps = {\n  width?: number;\n  height?: number;\n  color?: string;\n  bgColor?: string;\n};\nconst Spinner: React.FC&lt;SpinnerProps&gt; = ({\n  width = 5,\n  height = 5,\n  color,\n  bgColor,\n}) =&gt; {\n  return (\n    &lt;svg\n      role='status'\n      className={twMerge(\n        'w-5 h-5 mr-2 text-gray-200 animate-spin dark:text-gray-600 fill-blue-600',\n        `w-${width} h-${height} ${color} ${bgColor}`\n      )}\n      viewBox='0 0 100 101'\n      fill='none'\n      xmlns='http:\/\/www.w3.org\/2000\/svg'\n    &gt;\n      &lt;path\n        d='M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z'\n        fill='currentColor'\n      \/&gt;\n      &lt;path\n        d='M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z'\n        fill='currentFill'\n      \/&gt;\n    &lt;\/svg&gt;\n  );\n};\n\nexport default Spinner;\n<\/code>\n<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Create a Screen Loader with React and tailwindCss<\/h3>\n\n\n\n<p>Here, you&#8217;ll create a component that will cover the full screen and display the Spinner component when a request is fired to the tRPC API.  Navigate to the <code>packages\/client\/src\/components<\/code> directory and create a <code>FullScreenLoader.tsx<\/code> file. Next, add the code below to the <strong>FullScreenLoader.tsx<\/strong> file.<\/p>\n\n\n\n<p><strong>packages\/client\/src\/components\/FullScreenLoader.tsx<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-tsx\"><code>\nimport Spinner from '.\/Spinner';\n\nconst FullScreenLoader = () =&gt; {\n  return (\n    &lt;div className='w-screen h-screen fixed'&gt;\n      &lt;div className='absolute top-64 left-1\/2 -translate-x-1\/2'&gt;\n        &lt;Spinner width={8} height={8} \/&gt;\n      &lt;\/div&gt;\n    &lt;\/div&gt;\n  );\n};\n\nexport default FullScreenLoader;\n<\/code>\n<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Create a Loading Button with React and tailwindCss<\/h3>\n\n\n\n<p>Now let&#8217;s create a custom button that will have the <strong>Spinner<\/strong> component and a text element. The Spinner component will be hidden by default but when the form is submitted and the form data is posted to the tRPC API, it will be made visible to give the user visual feedback that the request is being processed by the API.<\/p>\n\n\n\n<p><strong>packages\/client\/src\/components\/LoadingButton.tsx<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-tsx\"><code>\nimport React from 'react';\nimport Spinner from '.\/Spinner';\n\ntype LoadingButtonProps = {\n  loading: boolean;\n  btnColor?: string;\n  textColor?: string;\n  children: React.ReactNode;\n};\n\nexport const LoadingButton: React.FC&lt;LoadingButtonProps&gt; = ({\n  textColor = 'text-white',\n  btnColor = 'bg-ct-yellow-600',\n  children,\n  loading = false,\n}) =&gt; {\n  return (\n    &lt;button\n      type='submit'\n      className={`w-full py-3 font-semibold ${btnColor} rounded-lg outline-none border-none flex justify-center ${\n        loading ? 'bg-[#ccc]' : ''\n      }`}\n    &gt;\n      {loading ? (\n        &lt;div className='flex items-center gap-3'&gt;\n          &lt;Spinner \/&gt;\n          &lt;span className='text-slate-500'&gt;Loading...&lt;\/span&gt;\n        &lt;\/div&gt;\n      ) : (\n        &lt;span className={`${textColor}`}&gt;{children}&lt;\/span&gt;\n      )}\n    &lt;\/button&gt;\n  );\n};\n<\/code>\n<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">React Layout Component with React-Router-Dom<\/h3>\n\n\n\n<p>To avoid repeating the <strong>Header<\/strong> component in all the pages that need it, let&#8217;s create a <strong>Layout<\/strong> component with the help of React-Router-DOM&#8217;s <code>Outlet<\/code> component to render the pages as child routes.<\/p>\n\n\n\n<p>So, create a <code>Layout.tsx<\/code> file in the <code>packages\/client\/src\/components<\/code> folder and add the code snippets below.<\/p>\n\n\n\n<p><strong>packages\/client\/src\/components\/Layout.tsx<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-tsx\"><code>\nimport { Outlet } from 'react-router-dom';\nimport Header from '.\/Header';\n\nconst Layout = () =&gt; {\n  return (\n    &lt;&gt;\n      &lt;Header \/&gt;\n      &lt;Outlet \/&gt;\n    &lt;\/&gt;\n  );\n};\n\nexport default Layout;\n<\/code>\n<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Create an InputField with React-Hook-Form<\/h3>\n\n\n\n<p>Since there would be similar input elements in the login and registration forms, let&#8217;s create a reusable input component to prevent us from repeating the same block of code in all the forms that need it. To begin, open your terminal and install React-Hook-Form, Zod, and React-Hook-Form&#8217;s validation resolver.<\/p>\n\n\n\n<pre class=\"line-numbers language-shell\"><code>\nyarn add react-hook-form zod @hookform\/resolvers\n<\/code>\n<\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code><a href=\"https:\/\/www.npmjs.com\/package\/react-hook-form\" target=\"_blank\" rel=\"noreferrer noopener\">react-hook-form<\/a><\/code> &#8211; A library for validating forms in React.js<\/li>\n\n\n\n<li><code><a href=\"https:\/\/github.com\/colinhacks\/zod\" target=\"_blank\" rel=\"noreferrer noopener\">zod<\/a><\/code> &#8211; A TypeScript-first schema declaration and validation library<\/li>\n<\/ul>\n\n\n\n<p>After the installations, create a <code>FormInput.tsx<\/code> file in the <code>client\/src\/components<\/code>  directory and add the following code.<\/p>\n\n\n\n<p><strong>packages\/client\/src\/components\/FormInput.tsx<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-tsx\"><code>\nimport React from 'react';\nimport { useFormContext } from 'react-hook-form';\n\ntype FormInputProps = {\n  label: string;\n  name: string;\n  type?: string;\n};\n\nconst FormInput: React.FC&lt;FormInputProps&gt; = ({\n  label,\n  name,\n  type = 'text',\n}) =&gt; {\n  const {\n    register,\n    formState: { errors },\n  } = useFormContext();\n  return (\n    &lt;div className=''&gt;\n      &lt;label htmlFor={name} className='block text-ct-blue-600 mb-3'&gt;\n        {label}\n      &lt;\/label&gt;\n      &lt;input\n        type={type}\n        placeholder=' '\n        className='block w-full rounded-2xl appearance-none focus:outline-none py-2 px-4'\n        {...register(name)}\n      \/&gt;\n      {errors[name] &amp;&amp; (\n        &lt;span className='text-red-500 text-xs pt-1 block'&gt;\n          {errors[name]?.message as string}\n        &lt;\/span&gt;\n      )}\n    &lt;\/div&gt;\n  );\n};\n\nexport default FormInput;\n<\/code>\n<\/pre>\n\n\n\n<p>Instead of passing the form context as a prop to the Input component, we used the <code><a href=\"https:\/\/react-hook-form.com\/docs\/useformcontext\" target=\"_blank\" rel=\"noreferrer noopener\">useFormContext<\/a><\/code> hook to access the form context. Later, we&#8217;ll wrap the <a href=\"https:\/\/react-hook-form.com\/docs\/formprovider\" target=\"_blank\" rel=\"noreferrer noopener\">FormProvider<\/a>\u00a0component around the form for the <strong>useFormContext<\/strong> hook to work properly.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Create an Image Uploader with React and Cloudinary<\/h3>\n\n\n\n<p>To begin, register for an account on <a href=\"https:\/\/cloudinary.com\/\" target=\"_blank\" rel=\"noreferrer noopener\">Cloudinary<\/a> and follow the following steps to configure the upload preset on your account.<\/p>\n\n\n\n<p><strong>Step 1: <\/strong> Click on the Settings icon on the top navigation bar.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"974\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-1024x974.png\" alt=\"cloudinary dashboard\" class=\"wp-image-4418\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-1024x974.png 1024w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-300x285.png 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-768x731.png 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-100x95.png 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-473x450.png 473w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard.png 1135w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><strong>Step 2:<\/strong> Click the &#8220;<strong>Upload<\/strong>&#8221; tab on the settings page<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"971\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-upload-tab-1024x971.png\" alt=\"cloudinary dashboard upload tab\" class=\"wp-image-4417\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-upload-tab-1024x971.png 1024w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-upload-tab-300x284.png 300w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-upload-tab-768x728.png 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-upload-tab-100x95.png 100w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-upload-tab-475x450.png 475w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-upload-tab.png 1139w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><strong>Step 3:<\/strong> Scroll down to the &#8220;<strong>Upload presets<\/strong>&#8221; section and click on the &#8220;<strong>Add upload preset<\/strong>&#8221; link<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"983\" height=\"1024\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-upload-settings-983x1024.png\" alt=\"cloudinary dashboard upload settings\" class=\"wp-image-4416\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-upload-settings-983x1024.png 983w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-upload-settings-288x300.png 288w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-upload-settings-768x800.png 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-upload-settings-96x100.png 96w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-upload-settings-432x450.png 432w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-upload-settings.png 1037w\" sizes=\"auto, (max-width: 983px) 100vw, 983px\" \/><\/figure>\n\n\n\n<p><strong>Step 4:<\/strong> Enter the &#8220;<strong>Upload preset name<\/strong>&#8221; and change the &#8220;<strong>Signing Mode<\/strong>&#8221; to &#8220;<strong>Unsigned<\/strong>&#8220;. Input the preset name as the folder name.<\/p>\n\n\n\n<p>Once you are done, click on the &#8220;<strong>Upload Manipulations<\/strong>&#8221; tab on the left sidebar.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"984\" height=\"1024\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-upload-settings-preset-984x1024.png\" alt=\"cloudinary dashboard upload settings preset\" class=\"wp-image-4415\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-upload-settings-preset-984x1024.png 984w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-upload-settings-preset-288x300.png 288w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-upload-settings-preset-768x799.png 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-upload-settings-preset-96x100.png 96w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-upload-settings-preset-433x450.png 433w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-upload-settings-preset.png 1038w\" sizes=\"auto, (max-width: 984px) 100vw, 984px\" \/><\/figure>\n\n\n\n<p><strong>Step 5:<\/strong> Click on the &#8220;<strong>Edit<\/strong>&#8221; link under the &#8220;<strong>Incoming Transformation<\/strong>&#8221; section.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"985\" height=\"1024\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-add-upload-preset-985x1024.png\" alt=\"cloudinary dashboard add upload preset\" class=\"wp-image-4412\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-add-upload-preset-985x1024.png 985w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-add-upload-preset-289x300.png 289w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-add-upload-preset-768x798.png 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-add-upload-preset-96x100.png 96w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-add-upload-preset-433x450.png 433w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-add-upload-preset.png 1039w\" sizes=\"auto, (max-width: 985px) 100vw, 985px\" \/><\/figure>\n\n\n\n<p><strong>Step 6:<\/strong> Change the width and height to your preferred size and change the quality type from &#8220;<strong>Manual<\/strong>&#8221; to &#8220;<strong>Automatic &#8211; best quality<\/strong>&#8220;.<\/p>\n\n\n\n<p>Once you are done, click the &#8220;<strong>ok<\/strong>&#8221; button.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"985\" height=\"1024\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-traform-image-985x1024.png\" alt=\"cloudinary dashboard traform image\" class=\"wp-image-4414\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-traform-image-985x1024.png 985w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-traform-image-289x300.png 289w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-traform-image-768x798.png 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-traform-image-96x100.png 96w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-traform-image-433x450.png 433w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-traform-image.png 1039w\" sizes=\"auto, (max-width: 985px) 100vw, 985px\" \/><\/figure>\n\n\n\n<p><strong>Step 7:<\/strong> Click on the &#8220;<strong>Save<\/strong>&#8221; button to save the changes.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"983\" height=\"1024\" src=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-click-on-the-save-button-983x1024.png\" alt=\"cloudinary dashboard click on the save button\" class=\"wp-image-4413\" srcset=\"https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-click-on-the-save-button-983x1024.png 983w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-click-on-the-save-button-288x300.png 288w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-click-on-the-save-button-768x800.png 768w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-click-on-the-save-button-96x100.png 96w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-click-on-the-save-button-432x450.png 432w, https:\/\/codevoweb.com\/wp-content\/uploads\/2022\/07\/cloudinary-dashboard-click-on-the-save-button.png 1037w\" sizes=\"auto, (max-width: 983px) 100vw, 983px\" \/><\/figure>\n\n\n\n<p>For security reasons don&#8217;t expose your &#8220;<strong>upload preset name<\/strong>&#8221; to the public to avoid unsolicited upload of images by users.<\/p>\n\n\n\n<p>I will definitely change my account details at the end of this tutorial.<\/p>\n\n\n\n<p>Now create <code>packages\/client\/src\/components\/FileUpload.tsx<\/code> file and add the following code to process the upload request.<\/p>\n\n\n\n<p><strong>packages\/client\/src\/components\/FileUpload.tsx<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-tsx\"><code>\nimport React, { useCallback } from 'react';\nimport { Controller, useController, useFormContext } from 'react-hook-form';\nimport useStore from '..\/store';\nimport Spinner from '.\/Spinner';\n\nconst CLOUDINARY_UPLOAD_PRESET = 'trpc-api';\nconst CLOUDINARY_URL = 'https:\/\/api.cloudinary.com\/v1_1\/Codevo\/image\/upload';\n\ntype FileUpLoaderProps = {\n  name: string;\n};\nconst FileUpLoader: React.FC&lt;FileUpLoaderProps&gt; = ({ name }) =&gt; {\n  const {\n    control,\n    formState: { errors },\n  } = useFormContext();\n  const { field } = useController({ name, control });\n  const store = useStore();\n\n  const onFileDrop = useCallback(\n    async (e: React.SyntheticEvent&lt;EventTarget&gt;) =&gt; {\n      const target = e.target as HTMLInputElement;\n      if (!target.files) return;\n      const newFile = Object.values(target.files).map((file: File) =&gt; file);\n      const formData = new FormData();\n      formData.append('file', newFile[0]);\n      formData.append('upload_preset', CLOUDINARY_UPLOAD_PRESET);\n\n      store.setUploadingImage(true);\n      const data = await fetch(CLOUDINARY_URL, {\n        method: 'POST',\n        body: formData,\n      })\n        .then((res) =&gt; {\n          store.setUploadingImage(false);\n\n          return res.json();\n        })\n        .catch((err) =&gt; {\n          store.setUploadingImage(false);\n          console.log(err);\n        });\n\n      if (data.secure_url) {\n        field.onChange(data.secure_url);\n      }\n    },\n\n    [field, store]\n  );\n\n  return (\n    &lt;Controller\n      name={name}\n      defaultValue=''\n      control={control}\n      render={({ field: { name, onBlur, ref } }) =&gt; (\n        &lt;&gt;\n          &lt;div className='mb-2 flex justify-between items-center'&gt;\n            &lt;div&gt;\n              &lt;span className='block mb-2'&gt;Choose profile photo&lt;\/span&gt;\n              &lt;input\n                className='block text-sm mb-2 text-slate-500 file:mr-4 file:py-2 file:px-4 file:rounded-full file:border-0 file:text-sm file:font-semibold file:bg-violet-50 file:text-violet-700 hover:file:bg-violet-100'\n                type='file'\n                name={name}\n                onBlur={onBlur}\n                ref={ref}\n                onChange={onFileDrop}\n                multiple={false}\n                accept='image\/jpg, image\/png, image\/jpeg'\n              \/&gt;\n            &lt;\/div&gt;\n            &lt;div&gt;\n              {store.uploadingImage &amp;&amp; &lt;Spinner color='text-yellow-400' \/&gt;}\n            &lt;\/div&gt;\n          &lt;\/div&gt;\n          &lt;p\n            className={`text-red-500 text-xs italic mb-2 ${\n              errors[name] ? 'visible' : 'invisible'\n            }`}\n          &gt;\n            {errors[name] &amp;&amp; (errors[name]?.message as string)}\n          &lt;\/p&gt;\n        &lt;\/&gt;\n      )}\n    \/&gt;\n  );\n};\n\nexport default FileUpLoader;\n<\/code>\n<\/pre>\n\n\n\n<p>From the above code, you will need two pieces of information, the <a href=\"https:\/\/cloudinary.com\/documentation\/image_upload_api_reference\" target=\"_blank\" rel=\"noreferrer noopener\">Cloudinary RESTful image upload<\/a> URL and your preset name.<\/p>\n\n\n\n<p>For example, to upload an image to your account, you would have to make a <strong>POST<\/strong> request with the formData to:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>POST https:\/\/api.cloudinary.com\/v1_1\/:cloud_name\/image\/upload<\/code><\/pre>\n\n\n\n<p>Where <code>:cloud_name<\/code> is your Cloudinary Cloud name. Also, you need to append your upload preset name to the <code>formData<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">React Query and tRPC Client: Register User<\/h2>\n\n\n\n<p>At this point, we are ready to implement the authentication logic of the tRPC client. The first component will add new users to the tRPC API. This component will contain a form that has name, email, photo, password, and password confirm input elements. The validation of the input fields will be handled by React-Hook-Form and the validation rules will be defined with Zod.<\/p>\n\n\n\n<p>To begin, change the directory to the client folder <code>cd packages\/client<\/code> and install the <code><a href=\"https:\/\/www.npmjs.com\/package\/react-toastify\" target=\"_blank\" rel=\"noreferrer noopener\">react-toastify<\/a><\/code> package. This library will allow us to show alert notifications.<\/p>\n\n\n\n<pre class=\"line-numbers language-shell\"><code>\nyarn add react-toastify\n<\/code><\/pre>\n\n\n\n<p>With that out of the way, create a <code>register.page.tsx<\/code> file in the <code>packages\/client\/src\/pages<\/code> directory and add the code snippets below.<\/p>\n\n\n\n<p><strong>packages\/client\/src\/pages\/register.page.tsx<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-tsx\"><code>\nimport { FormProvider, SubmitHandler, useForm } from \"react-hook-form\";\nimport { object, string, TypeOf } from \"zod\";\nimport { zodResolver } from \"@hookform\/resolvers\/zod\";\nimport { useEffect } from \"react\";\nimport { Link, useNavigate } from \"react-router-dom\";\nimport FormInput from \"..\/components\/FormInput\";\nimport { LoadingButton } from \"..\/components\/LoadingButton\";\nimport { toast } from \"react-toastify\";\nimport { trpc } from \"..\/trpc\";\nimport FileUpLoader from \"..\/components\/FileUpload\";\n\nconst registerSchema = object({\n  name: string().min(1, \"Full name is required\").max(100),\n  email: string()\n    .min(1, \"Email address is required\")\n    .email(\"Email Address is invalid\"),\n  photo: string().min(1, \"Photo is required\").url(\"Photo URL is invalid\"),\n  password: string()\n    .min(1, \"Password is required\")\n    .min(8, \"Password must be more than 8 characters\")\n    .max(32, \"Password must be less than 32 characters\"),\n  passwordConfirm: string().min(1, \"Please confirm your password\"),\n}).refine((data) =&gt; data.password === data.passwordConfirm, {\n  path: [\"passwordConfirm\"],\n  message: \"Passwords do not match\",\n});\n\nexport type RegisterInput = TypeOf&lt;typeof registerSchema&gt;;\n\nconst RegisterPage = () =&gt; {\n  const navigate = useNavigate();\n\n  const { isLoading, mutate: SignUpUser } = trpc.registerUser.useMutation({\n    onSuccess: (data) =&gt; {\n      toast.success(\"Registration successful\");\n      navigate(\"\/login\");\n    },\n    onError: (error) =&gt; {\n      toast.error(error.message, {\n        type: \"error\",\n        position: \"top-right\",\n      });\n    },\n  });\n\n  const methods = useForm&lt;RegisterInput&gt;({\n    resolver: zodResolver(registerSchema),\n  });\n\n  const {\n    reset,\n    handleSubmit,\n    formState: { isSubmitSuccessful },\n  } = methods;\n\n  useEffect(() =&gt; {\n    if (isSubmitSuccessful) {\n      reset();\n    }\n    \/\/ eslint-disable-next-line react-hooks\/exhaustive-deps\n  }, [isSubmitSuccessful]);\n\n  const onSubmitHandler: SubmitHandler&lt;RegisterInput&gt; = (values) =&gt; {\n    \/\/ \ud83d\udc47 Execute the Mutation\n    SignUpUser(values);\n  };\n\n  return (\n    &lt;section className=\"py-8 bg-ct-blue-600 min-h-screen grid place-items-center\"&gt;\n      &lt;div className=\"w-full\"&gt;\n        &lt;h1 className=\"text-4xl xl:text-6xl text-center font-[600] text-ct-yellow-600 mb-4\"&gt;\n          Welcome to CodevoWeb!\n        &lt;\/h1&gt;\n        &lt;h2 className=\"text-lg text-center mb-4 text-ct-dark-200\"&gt;\n          Sign Up To Get Started!\n        &lt;\/h2&gt;\n        &lt;FormProvider {...methods}&gt;\n          &lt;form\n            onSubmit={handleSubmit(onSubmitHandler)}\n            className=\"max-w-md w-full mx-auto overflow-hidden shadow-lg bg-ct-dark-200 rounded-2xl p-8 space-y-5\"\n          &gt;\n            &lt;FormInput label=\"Full Name\" name=\"name\" \/&gt;\n            &lt;FormInput label=\"Email\" name=\"email\" type=\"email\" \/&gt;\n            &lt;FormInput label=\"Password\" name=\"password\" type=\"password\" \/&gt;\n            &lt;FormInput\n              label=\"Confirm Password\"\n              name=\"passwordConfirm\"\n              type=\"password\"\n            \/&gt;\n            &lt;FileUpLoader name=\"photo\" \/&gt;\n            &lt;span className=\"block\"&gt;\n              Already have an account?{\" \"}\n              &lt;Link to=\"\/login\" className=\"text-ct-blue-600\"&gt;\n                Login Here\n              &lt;\/Link&gt;\n            &lt;\/span&gt;\n            &lt;LoadingButton loading={isLoading} textColor=\"text-ct-blue-600\"&gt;\n              Sign Up\n            &lt;\/LoadingButton&gt;\n          &lt;\/form&gt;\n        &lt;\/FormProvider&gt;\n      &lt;\/div&gt;\n    &lt;\/section&gt;\n  );\n};\n\nexport default RegisterPage;\n<\/code>\n<\/pre>\n\n\n\n<p>We created a validation schema with Zod and assigned it the <code>registerSchema<\/code> variable. Then, we inferred a TypeScript type from the schema and assigned it to <code>RegisterInput<\/code> .<\/p>\n\n\n\n<p>Next, we created a <code>registerUser<\/code> mutation hook to submit the registration form data to the tRPC API. When the form is submitted and is valid, React-Hook-Form will evoke the <code>onSubmitHandler()<\/code> function we passed to the <code>handleSubmit()<\/code> method.<\/p>\n\n\n\n<p>The <strong>SignUpUser<\/strong> method will then be evoked with the form data which will trigger the <code>registerUser<\/code> hook to submit the form data to the tRPC server. The mutation hook will evoke the  <code>registerUser<\/code> procedure on the tRPC API to add the new user to the database. If the tRPC API adds the new user to the database, React will redirect the user to the login page. Otherwise, an error message will be displayed.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">React Query and tRPC Client: Login User<\/h2>\n\n\n\n<p>Now that we are able to register a user, let&#8217;s create a component to sign the user into the tRPC app. Similar to the user registration component, we&#8217;ll define a schema with Zod and provide it to React-Hook-Form&#8217;s <code>useForm()<\/code> hook for the form validation.<\/p>\n\n\n\n<p>After that, we&#8217;ll create a mutation hook with React Query to submit the form data to the tRPC backend. So, go into the <code>packages\/client\/src\/pages<\/code> folder and create a <code>login.page.tsx<\/code> file. Next, open the newly-created <strong>login.page.tsx<\/strong> file and add the code below.<\/p>\n\n\n\n<p><strong>packages\/client\/src\/pages\/login.page.tsx<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-tsx\"><code>\nimport { FormProvider, SubmitHandler, useForm } from \"react-hook-form\";\nimport { object, string, TypeOf } from \"zod\";\nimport { zodResolver } from \"@hookform\/resolvers\/zod\";\nimport { useEffect } from \"react\";\nimport { Link, useLocation, useNavigate } from \"react-router-dom\";\nimport FormInput from \"..\/components\/FormInput\";\nimport { LoadingButton } from \"..\/components\/LoadingButton\";\nimport { toast } from \"react-toastify\";\nimport { trpc } from \"..\/trpc\";\n\nconst loginSchema = object({\n  email: string()\n    .min(1, \"Email address is required\")\n    .email(\"Email Address is invalid\"),\n  password: string()\n    .min(1, \"Password is required\")\n    .min(8, \"Password must be more than 8 characters\")\n    .max(32, \"Password must be less than 32 characters\"),\n});\n\nexport type LoginInput = TypeOf&lt;typeof loginSchema&gt;;\n\nconst LoginPage = () =&gt; {\n  const navigate = useNavigate();\n  const location = useLocation();\n  const from = ((location.state as any)?.from.pathname as string) || \"\/profile\";\n\n  const { isLoading, mutate: loginUser } = trpc.loginUser.useMutation({\n    onSuccess(data) {\n      toast(\"Logged in successfully\", {\n        type: \"success\",\n        position: \"top-right\",\n      });\n      navigate(from);\n    },\n    onError(error) {\n      toast(error.message, {\n        type: \"error\",\n        position: \"top-right\",\n      });\n    },\n  });\n\n  const methods = useForm&lt;LoginInput&gt;({\n    resolver: zodResolver(loginSchema),\n  });\n\n  const {\n    reset,\n    handleSubmit,\n    formState: { isSubmitSuccessful },\n  } = methods;\n\n  useEffect(() =&gt; {\n    if (isSubmitSuccessful) {\n      reset();\n    }\n    \/\/ eslint-disable-next-line react-hooks\/exhaustive-deps\n  }, [isSubmitSuccessful]);\n\n  const onSubmitHandler: SubmitHandler&lt;LoginInput&gt; = (values) =&gt; {\n    \/\/ \ud83d\udc47 Executing the loginUser Mutation\n    loginUser(values);\n  };\n\n  return (\n    &lt;section className=\"bg-ct-blue-600 min-h-screen grid place-items-center\"&gt;\n      &lt;div className=\"w-full\"&gt;\n        &lt;h1 className=\"text-4xl xl:text-6xl text-center font-[600] text-ct-yellow-600 mb-4\"&gt;\n          Welcome Back\n        &lt;\/h1&gt;\n        &lt;h2 className=\"text-lg text-center mb-4 text-ct-dark-200\"&gt;\n          Login to have access\n        &lt;\/h2&gt;\n        &lt;FormProvider {...methods}&gt;\n          &lt;form\n            onSubmit={handleSubmit(onSubmitHandler)}\n            className=\"max-w-md w-full mx-auto overflow-hidden shadow-lg bg-ct-dark-200 rounded-2xl p-8 space-y-5\"\n          &gt;\n            &lt;FormInput label=\"Email\" name=\"email\" type=\"email\" \/&gt;\n            &lt;FormInput label=\"Password\" name=\"password\" type=\"password\" \/&gt;\n\n            &lt;div className=\"text-right\"&gt;\n              &lt;Link to=\"#\" className=\"\"&gt;\n                Forgot Password?\n              &lt;\/Link&gt;\n            &lt;\/div&gt;\n            &lt;LoadingButton loading={isLoading} textColor=\"text-ct-blue-600\"&gt;\n              Login\n            &lt;\/LoadingButton&gt;\n            &lt;span className=\"block\"&gt;\n              Need an account?{\" \"}\n              &lt;Link to=\"\/register\" className=\"text-ct-blue-600\"&gt;\n                Sign Up Here\n              &lt;\/Link&gt;\n            &lt;\/span&gt;\n          &lt;\/form&gt;\n        &lt;\/FormProvider&gt;\n      &lt;\/div&gt;\n    &lt;\/section&gt;\n  );\n};\n\nexport default LoginPage;\n<\/code>\n<\/pre>\n\n\n\n<p>We created a validation schema to validate the email and password input fields and assigned the schema to the <code>loginSchema<\/code> variable. Then, we inferred a TypeScript type from the schema and assigned it to the <code>LoginInput<\/code> type.<\/p>\n\n\n\n<p>Next, we created a React Query mutation hook to submit the form data to the tRPC backend API. when the form is submitted and there are no validation errors, React will evoke the <code>onSubmitHandler<\/code> function we passed to the <code>handleSubmit()<\/code> method.<\/p>\n\n\n\n<p>This will in turn evoke the <code>loginUser()<\/code> mutation function and pass the form data as an argument to it. After the mutation has been triggered, the <code>loginUser<\/code> procedure will be evoked to sign the user into the API. If the user&#8217;s credentials are valid and the authentication succeeds, React will display a success alert message and redirect the user to the protected route or the profile page.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">React Query and tRPC Client: Authentication Guard<\/h2>\n\n\n\n<p>On the tRPC server, we returned a <code>logged_in<\/code> cookie that is not HTTPOnly to the client or browser. This will enable us to access it from the React application.<\/p>\n\n\n\n<p>Now let&#8217;s create an authentication guard to protect the private routes on the React application.<\/p>\n\n\n\n<p><strong>packages\/client\/src\/components\/requireUser.tsx<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-tsx\"><code>\nimport { useCookies } from \"react-cookie\";\nimport { Navigate, Outlet, useLocation } from \"react-router-dom\";\nimport { IUser } from \"..\/libs\/types\";\nimport useStore from \"..\/store\";\nimport { trpc } from \"..\/trpc\";\nimport FullScreenLoader from \".\/FullScreenLoader\";\n\nconst RequireUser = ({ allowedRoles }: { allowedRoles: string[] }) =&gt; {\n  const [cookies] = useCookies([\"logged_in\"]);\n  const location = useLocation();\n  const store = useStore();\n\n  const {\n    isLoading,\n    isFetching,\n    data: user,\n  } = trpc.getMe.useQuery(undefined, {\n    retry: 1,\n    select: (data) =&gt; data.data.user,\n    onSuccess: (data) =&gt; {\n      store.setAuthUser(data as IUser);\n    },\n    onError: (error) =&gt; {\n      console.log(error);\n      if (error.message.includes(\"Could not refresh access token\")) {\n        document.location.href = \"\/login\";\n      }\n    },\n  });\n\n  const loading = isLoading || isFetching;\n\n  if (loading) {\n    return &lt;FullScreenLoader \/&gt;;\n  }\n\n  return (cookies.logged_in || user) &amp;&amp;\n    allowedRoles.includes(user?.role as string) ? (\n    &lt;Outlet \/&gt;\n  ) : cookies.logged_in &amp;&amp; user ? (\n    &lt;Navigate to=\"\/unauthorized\" state={{ from: location }} replace \/&gt;\n  ) : (\n    &lt;Navigate to=\"\/login\" state={{ from: location }} replace \/&gt;\n  );\n};\n\nexport default RequireUser;\n<\/code>\n<\/pre>\n\n\n\n<p>The <code>requireUser<\/code> auth guard will make a tRPC query request to retrieve the authenticated user&#8217;s information. This request will only be successful if the user is logged in. When the request results in an error, it means the user doesn&#8217;t have a valid access token and React will redirect the user to the login page.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Refresh Access Token Middleware Guard<\/h2>\n\n\n\n<p>Let&#8217;s create a component to refresh the user&#8217;s access token when it expires. To do this, we&#8217;ll wrap the middleware guard around the root component. When a user signs into the app, the tRPC API will send access, refresh, and logged_in cookies to the user&#8217;s browser. Both the access and refresh tokens are <strong>HTTPOnly<\/strong> cookies but the logged_in cookie is not. That means, we can access the value of the logged_in cookie in the React app.<\/p>\n\n\n\n<p>Also, the logged_in token has the same expiration time as the access token. So, when the access token expires, the logged_in token will also expire and the browser will delete both of them automatically.<\/p>\n\n\n\n<p><strong>packages\/client\/src\/middleware\/AuthMiddleware.tsx<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-tsx\"><code>\nimport { useCookies } from \"react-cookie\";\nimport FullScreenLoader from \"..\/components\/FullScreenLoader\";\nimport React from \"react\";\nimport { trpc } from \"..\/trpc\";\nimport { IUser } from \"..\/libs\/types\";\nimport { useQueryClient } from \"@tanstack\/react-query\";\nimport useStore from \"..\/store\";\n\ntype AuthMiddlewareProps = {\n  children: React.ReactElement;\n};\n\nconst AuthMiddleware: React.FC&lt;AuthMiddlewareProps&gt; = ({ children }) =&gt; {\n  const [cookies] = useCookies([\"logged_in\"]);\n  const store = useStore();\n\n  const queryClient = useQueryClient();\n  const { refetch } = trpc.refreshToken.useQuery(undefined, {\n    retry: 1,\n    enabled: false,\n    onSuccess: (data) =&gt; {\n      queryClient.invalidateQueries([[\"getMe\"]]);\n    },\n  });\n\n  const query = trpc.getMe.useQuery(undefined, {\n    enabled: !!cookies.logged_in,\n    retry: 1,\n    select: (data) =&gt; data.data.user,\n    onSuccess: (data) =&gt; {\n      store.setAuthUser(data as IUser);\n    },\n    onError: (error) =&gt; {\n      let retryRequest = true;\n      if (error.message.includes(\"must be logged in\") &amp;&amp; retryRequest) {\n        retryRequest = false;\n        try {\n          refetch({ throwOnError: true });\n        } catch (err: any) {\n          if (err.message.includes(\"Could not refresh access token\")) {\n            document.location.href = \"\/login\";\n          }\n        }\n      }\n    },\n  });\n\n  if (query.isLoading &amp;&amp; cookies.logged_in) {\n    return &lt;FullScreenLoader \/&gt;;\n  }\n\n  return children;\n};\n\nexport default AuthMiddleware;\n<\/code>\n<\/pre>\n\n\n\n<p>If the logged_in cookie is available then it means the user still has an access token. However, if React attempts to retrieve the logged_in cookie and an undefined value is returned then that means the access token has expired.<\/p>\n\n\n\n<p>When the logged_in cookie is available, the <strong>getMe<\/strong> query will be evoked to retrieve the authenticated user&#8217;s profile information from the tRPC API. When the tRPC API returns a &#8220;<strong>must be logged in<\/strong>&#8221; error, React Query will call the <code>refreshToken<\/code> query to obtain a new access token.<\/p>\n\n\n\n<p>After the access token has been refreshed, the <code>.invalidateQueries([[\"getMe\"]])<\/code> method will be evoked to invalidate the <strong>getMe<\/strong> query. This will force React Query to re-fetch the user&#8217;s credentials with the new access token.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Creating the Home and Profile Pages<\/h2>\n\n\n\n<p>Now let&#8217;s create the remaining React components. Here, you&#8217;ll create the Home and Profile pages. The Profile page will be private and only authenticated user&#8217;s can see it. The Home page on the other hand will be public and anyone can visit it.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Home Page<\/h3>\n\n\n\n<p>To create the Home page, go into the <code>packages\/client\/src\/pages<\/code> folder and create a <code>home.page.tsx<\/code> file. After that, open the <strong>home.page.tsx<\/strong> file and add the code below.<\/p>\n\n\n\n<p><strong>packages\/client\/src\/pages\/home.page.tsx<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-tsx\"><code>\nconst HomePage = () =&gt; {\n  return (\n    &lt;section className='bg-ct-blue-600 min-h-screen pt-20'&gt;\n      &lt;div className='max-w-4xl mx-auto bg-ct-dark-100 rounded-md h-[20rem] flex justify-center items-center'&gt;\n        &lt;p className='text-5xl font-semibold'&gt;Home Page&lt;\/p&gt;\n      &lt;\/div&gt;\n    &lt;\/section&gt;\n  );\n};\n\nexport default HomePage;\n<\/code>\n<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Profile Page<\/h3>\n\n\n\n<p>Let&#8217;s create the Profile page to display the user&#8217;s profile information. When this component mounts, the user&#8217;s credentials will be retrieved from the Zustand store and React will display them in the UI. So, create a <code>profile.page.tsx<\/code> file in the <strong>pages<\/strong> directory and add the code snippets below.<\/p>\n\n\n\n<p><strong>packages\/client\/src\/pages\/profile.page.tsx<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-tsx\"><code>\nimport useStore from '..\/store';\n\nconst ProfilePage = () =&gt; {\n  const store = useStore();\n\n  const user = store.authUser;\n\n  return (\n    &lt;section className='bg-ct-blue-600 min-h-screen pt-20'&gt;\n      &lt;div className='max-w-4xl mx-auto bg-ct-dark-100 rounded-md h-[20rem] flex justify-center items-center'&gt;\n        &lt;div&gt;\n          &lt;p className='text-5xl font-semibold'&gt;Profile Page&lt;\/p&gt;\n          &lt;div className='mt-8'&gt;\n            &lt;p className='mb-4'&gt;ID: {user?.id}&lt;\/p&gt;\n            &lt;p className='mb-4'&gt;Name: {user?.name}&lt;\/p&gt;\n            &lt;p className='mb-4'&gt;Email: {user?.email}&lt;\/p&gt;\n            &lt;p className='mb-4'&gt;Role: {user?.role}&lt;\/p&gt;\n          &lt;\/div&gt;\n        &lt;\/div&gt;\n      &lt;\/div&gt;\n    &lt;\/section&gt;\n  );\n};\n\nexport default ProfilePage;\n<\/code>\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Setup Routing with React-Router-Dom<\/h2>\n\n\n\n<p><strong>React Router DOM v6<\/strong> ships with <code>&lt;Routes&gt;<\/code>,&nbsp;<code>&lt;Route&gt;<\/code>, and&nbsp;<code>&lt;Outlet&gt;<\/code>&nbsp;APIs for creating routes with React elements. But it also provides another API that can be used to declare routes with plain JavaScript objects.<\/p>\n\n\n\n<p>Each JavaScript Object has a <code>path<\/code>,&nbsp;<code>element<\/code>, and optional&nbsp;<code>children<\/code> properties in the array of routes.<\/p>\n\n\n\n<p>To define the routes, create a <strong>router<\/strong> folder in the <strong>packages\/client\/src<\/strong> directory. Then, create an <code>index.tsx<\/code> file in the <strong>router<\/strong> folder and add the following route definitions.<\/p>\n\n\n\n<p><strong>packages\/client\/src\/router\/index.tsx<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-tsx\"><code>\nimport { Suspense, lazy } from 'react';\nimport type { RouteObject } from 'react-router-dom';\nimport FullScreenLoader from '..\/components\/FullScreenLoader';\nimport Layout from '..\/components\/Layout';\nimport RequireUser from '..\/components\/requireUser';\nimport HomePage from '..\/pages\/home.page';\nimport LoginPage from '..\/pages\/login.page';\nimport ProfilePage from '..\/pages\/profile.page';\n\nconst Loadable =\n  (Component: React.ComponentType&lt;any&gt;) =&gt; (props: JSX.IntrinsicAttributes) =&gt;\n    (\n      &lt;Suspense fallback={&lt;FullScreenLoader \/&gt;}&gt;\n        &lt;Component {...props} \/&gt;\n      &lt;\/Suspense&gt;\n    );\n\nconst RegisterPage = Loadable(lazy(() =&gt; import('..\/pages\/register.page')));\n\nconst authRoutes: RouteObject = {\n  path: '*',\n  children: [\n    {\n      path: 'login',\n      element: &lt;LoginPage \/&gt;,\n    },\n    {\n      path: 'register',\n      element: &lt;RegisterPage \/&gt;,\n    },\n  ],\n};\n\nconst normalRoutes: RouteObject = {\n  path: '*',\n  element: &lt;Layout \/&gt;,\n  children: [\n    {\n      index: true,\n      element: &lt;HomePage \/&gt;,\n    },\n    {\n      path: 'profile',\n      element: &lt;RequireUser allowedRoles={['user']} \/&gt;,\n      children: [{ path: '', element: &lt;ProfilePage \/&gt; }],\n    },\n  ],\n};\n\nconst routes: RouteObject[] = [authRoutes, normalRoutes];\n\nexport default routes;\n<\/code>\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Update the Index and App Files<\/h2>\n\n\n\n<p>Now that we&#8217;ve created the routes, we need a way to render them as React elements. Luckily, <strong>React Router DOM v6<\/strong> provides a first-class API hook called <code>useRoutes<\/code> that can be used to compose routes from JavaScript objects.<\/p>\n\n\n\n<p>The <code>useRoutes<\/code> hook takes an array of JavaScript Objects as an argument and returns Route elements based on the matched location.<\/p>\n\n\n\n<p><strong>packages\/client\/src\/App.tsx<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-tsx\"><code>\nimport { useState } from \"react\";\nimport { QueryClientProvider, QueryClient } from \"@tanstack\/react-query\";\nimport { ReactQueryDevtools } from \"@tanstack\/react-query-devtools\";\nimport { useRoutes } from \"react-router-dom\";\nimport { getFetch, httpBatchLink, loggerLink } from \"@trpc\/client\";\nimport routes from \".\/router\";\nimport { trpc } from \".\/trpc\";\nimport AuthMiddleware from \".\/middleware\/AuthMiddleware\";\n\nfunction AppContent() {\n  const content = useRoutes(routes);\n  return content;\n}\n\nfunction App() {\n  const [queryClient] = useState(\n    () =&gt;\n      new QueryClient({\n        defaultOptions: {\n          queries: {\n            staleTime: 5 * 1000,\n          },\n        },\n      })\n  );\n\n  const [trpcClient] = useState(() =&gt;\n    trpc.createClient({\n      links: [\n        loggerLink(),\n        httpBatchLink({\n          url: \"http:\/\/localhost:8000\/api\/trpc\",\n          fetch: async (input, init?) =&gt; {\n            const fetch = getFetch();\n            return fetch(input, {\n              ...init,\n              credentials: \"include\",\n            });\n          },\n        }),\n      ],\n    })\n  );\n  return (\n    &lt;trpc.Provider client={trpcClient} queryClient={queryClient}&gt;\n      &lt;QueryClientProvider client={queryClient}&gt;\n        &lt;AuthMiddleware&gt;\n          &lt;AppContent \/&gt;\n        &lt;\/AuthMiddleware&gt;\n        &lt;ReactQueryDevtools initialIsOpen={false} \/&gt;\n      &lt;\/QueryClientProvider&gt;\n    &lt;\/trpc.Provider&gt;\n  );\n}\n\nexport default App;\n<\/code>\n<\/pre>\n\n\n\n<p>Above, we created the React Query and tRPC clients and wrapped them around the <strong>AppContent<\/strong> component.<\/p>\n\n\n\n<p>To make the routing work, wrap React Router Dom&#8217;s <code>&lt;BrowserRouter&gt;<\/code> element around the root App component. Also, include the <strong>ToastContainer<\/strong> component so that the alert notifications can work.<\/p>\n\n\n\n<p><strong>packages\/client\/src\/index.tsx<\/strong><\/p>\n\n\n\n<pre class=\"line-numbers language-tsx\"><code>\nimport React from 'react';\nimport ReactDOM from 'react-dom\/client';\nimport { BrowserRouter as Router } from 'react-router-dom';\nimport { ToastContainer } from 'react-toastify';\nimport App from '.\/App';\n\nimport '.\/global.css';\nimport 'react-toastify\/dist\/ReactToastify.css';\n\nconst root = ReactDOM.createRoot(\n  document.getElementById('root') as HTMLElement\n);\nroot.render(\n  &lt;React.StrictMode&gt;\n    &lt;Router&gt;\n      &lt;App \/&gt;\n      &lt;ToastContainer \/&gt;\n    &lt;\/Router&gt;\n  &lt;\/React.StrictMode&gt;\n);\n<\/code>\n<\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p>With this React Query, tRPC Client, tailwindCss, and React-Hook-Form example in Typescript, you&#8217;ve learned how to add access and refresh token functionality to your React.js applications.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">React Query and tRPC Client Source Code<\/h2>\n\n\n\n<p>Check out the complete source code for:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><a href=\"https:\/\/github.com\/wpcodevo\/trpc-react-node-mongodb\/tree\/trpc-node-auth\" target=\"_blank\" rel=\"noreferrer noopener\">tRPC Server with Node.js, Express and TypeScript<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/github.com\/wpcodevo\/trpc-react-node-mongodb\/tree\/trpc-node-react-auth\" target=\"_blank\" rel=\"noreferrer noopener\">tRPC Client and Server with React.js and Node.js<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>tRPC is a toolkit that allows developers to statically type their API endpoints and share those types between the client and server, without installing extra&#8230;<\/p>\n","protected":false},"author":1,"featured_media":4401,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[54,47],"tags":[42,56,55],"class_list":["post-4348","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-react","category-nodejs","tag-nodejs-api","tag-react","tag-reactjs"],"acf":[],"_links":{"self":[{"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/posts\/4348","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/comments?post=4348"}],"version-history":[{"count":3,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/posts\/4348\/revisions"}],"predecessor-version":[{"id":11528,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/posts\/4348\/revisions\/11528"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/media\/4401"}],"wp:attachment":[{"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/media?parent=4348"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/categories?post=4348"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/codevoweb.com\/wp-json\/wp\/v2\/tags?post=4348"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}