<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Andrew Baisden - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Andrew Baisden - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Sun, 24 May 2026 16:29:44 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/andrewbaisden/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Build a CRUD App with TanStack Start and TanStackDB (with RxDB Integration) ]]>
                </title>
                <description>
                    <![CDATA[ TanStack Start is a new full-stack framework for React. It’s been growing in popularity ever since it reached the Release Candidate stage of its development in September, 2025. The Release Candidate stage is basically a version of software which is c... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-crud-app-with-tanstack-start-and-tanstackdb-with-rxdb-integration/</link>
                <guid isPermaLink="false">68ffdf4bd48b77fcbe0114ce</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ TypeScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ tanstack ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Andrew Baisden ]]>
                </dc:creator>
                <pubDate>Mon, 27 Oct 2025 21:08:27 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1761589878275/a36fe1d2-edf5-4339-b594-b1e55066485c.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>TanStack Start is a new full-stack framework for React. It’s been growing in popularity ever since it reached the Release Candidate stage of its development in September, 2025. The Release Candidate stage is basically a version of software which is considered to be almost complete, in a stable state, and ready for final public testing before its official launch.</p>
<p>TanStack Start has already started to emerge as a good alternative to other popular React frameworks like Next.js and Remix. The TanStack ecosystem is already quite popular with developers, and other well-known tools include:</p>
<ul>
<li><p><a target="_blank" href="https://tanstack.com/router/latest">TanStack Router</a>: Type-safe Routing for React and Solid applications</p>
</li>
<li><p><a target="_blank" href="https://tanstack.com/query/latest">TanStack Query</a>: Powerful asynchronous state management, server-state utilities and data fetching</p>
</li>
<li><p><a target="_blank" href="https://tanstack.com/form/latest">TanStack Form</a>: Headless UI for building performant and type-safe forms</p>
</li>
<li><p><a target="_blank" href="https://tanstack.com/db/latest">TanStackDB</a>: A reactive client store for building super-fast apps on sync</p>
</li>
</ul>
<p>In this tutorial, we’ll build a simple but powerful to-do list CRUD application using <a target="_blank" href="https://tanstack.com/start">TanStack Start</a>, <a target="_blank" href="https://tanstack.com/db">TanStackDB</a>, and <a target="_blank" href="https://rxdb.info/">RxDB</a>. You can see what the app looks like below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761220967371/3bd1f03c-e844-42bc-ac0f-b637e7cd6e61.png" alt="TanStack to do list CRUD App" class="image--center mx-auto" width="2526" height="1010" loading="lazy"></p>
<p>The tutorial will teach you how to:</p>
<ul>
<li><p>Create and persist data locally using RxDB</p>
</li>
<li><p>Create a TanStack Start project that uses TanStackDB for data storage</p>
</li>
<li><p>Build a full CRUD (Create, Read, Update, Delete) app</p>
</li>
</ul>
<p>At the end of this guide, we are also going to look at what makes TanStack Start different from other React frameworks like Next.js and Remix and how TanStackDB fits into this ever-growing ecosystem.</p>
<p>Let's get started.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-is-tanstack-start">What is TanStack Start?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-what-is-tanstack-db-with-rxdb-integration">What is TanStack DB (with RxDB Integration)?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-setting-up-our-project">Setting Up Our Project</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-creating-the-database-client">Creating the Database Client</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-understanding-local-persistence-with-rxdb">Understanding Local Persistence with RxDB</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-creating-a-todo-collection">Creating a Todo Collection</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-creating-our-crud-actions">Creating Our CRUD Actions</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-creating-the-frontend-page">Creating the Frontend Page</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-does-tanstack-start-compare-to-nextjs-and-remix">How does TanStack Start Compare to Next.js and Remix?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-when-should-you-use-tanstack-start-nextjs-or-remix">When Should You Use TanStack Start, Next.js, or Remix?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Fortunately, not much is required – just the following:</p>
<ul>
<li><p>Node and npm installed</p>
</li>
<li><p>Code editor/IDE</p>
</li>
</ul>
<h2 id="heading-what-is-tanstack-start">What is TanStack Start?</h2>
<p>TanStack Start is a modern React-based meta-framework built by the developer Tanner Linsley (who’s famous for building the TanStack ecosystem).</p>
<p>TanStack Start is designed to be:</p>
<ul>
<li><p>Blazing fast, as it is powered by Vite, Bun, or other modern bundlers</p>
</li>
<li><p>Type-safe, because it is deeply integrated with TypeScript as well as TanStack Router</p>
</li>
<li><p>Lightweight, since there is no server-side rendering unless you want it</p>
</li>
<li><p>Full-stack ready, as it works with loaders, actions, and data mutations just like Remix</p>
</li>
</ul>
<p>If you’re already familiar with Next.js and Remix, you can think of TanStack Start as a more modular, transparent, and flexible way of building full-stack React apps.</p>
<h2 id="heading-what-is-tanstack-db-with-rxdb-integration">What is TanStack DB (with RxDB Integration)?</h2>
<p>TanStackDB is a reactive data management layer which sits between your user interface and data source. It’s not like a typical ORM (Object Relational Mapper). Instead, it gives you a unified abstraction layer for working with local-first data collections which are reactive.</p>
<p>So when you combine TanStackDB with RxDB, you get local database persistence which works by using IndexDB or SQLite and real-time reactivity. This gives you the ability to sync data to remote backends later like PostgreSQL, for example.</p>
<p>In this project, we’re going to use RxDB for local-first-storage that makes it behave like SQLite when it’s inside a browser.</p>
<h2 id="heading-setting-up-our-project">Setting Up Our Project</h2>
<p>Let’s start fresh. Find a location on your computer for creating this project and run these commands in your terminal to set it up:</p>
<pre><code class="lang-bash">npm create @tanstack/start@latest my-app
<span class="hljs-built_in">cd</span> my-app
npm install rxdb @tanstack/react-db @tanstack/rxdb-db-collection
mkdir -p src/db
touch src/db/actions.ts src/db/client.ts src/db/todoCollection.ts
</code></pre>
<p>This run script creates a TanStack Start project, installs dependencies for RxDB and TanStackDB, and creates the folders and files we need for our app.</p>
<p>At the end, we’re also going to replace the existing <code>index.tsx</code> page with our own CRUD app codebase, while also keeping the demo routes in navigation so you can still explore them.</p>
<h2 id="heading-creating-the-database-client">Creating the Database Client</h2>
<p>Up first is our <code>src/db/client.ts</code> file, so copy and paste the below code into the file:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { createRxDatabase, removeRxDatabase } <span class="hljs-keyword">from</span> <span class="hljs-string">"rxdb"</span>;
<span class="hljs-keyword">import</span> { getRxStorageDexie } <span class="hljs-keyword">from</span> <span class="hljs-string">"rxdb/plugins/storage-dexie"</span>;

<span class="hljs-keyword">let</span> dbInstance: <span class="hljs-built_in">any</span> = <span class="hljs-literal">null</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">initDB</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// Only initialize in browser environment</span>
  <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> <span class="hljs-built_in">window</span> === <span class="hljs-string">"undefined"</span>) {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"initDB: Not in browser, skipping"</span>);
    <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
  }

  <span class="hljs-keyword">if</span> (dbInstance) {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"initDB: Returning existing instance"</span>);
    <span class="hljs-keyword">return</span> dbInstance;
  }

  <span class="hljs-keyword">try</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"initDB: Creating new database instance"</span>);
    <span class="hljs-keyword">const</span> storage = getRxStorageDexie();

    <span class="hljs-comment">// Always remove existing database in development</span>
    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">import</span>.meta.env.DEV) {
      <span class="hljs-keyword">try</span> {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"initDB: Removing existing database (dev mode)"</span>);
        <span class="hljs-keyword">await</span> removeRxDatabase(<span class="hljs-string">"appdb"</span>, storage);
      } <span class="hljs-keyword">catch</span> (e) {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"initDB: No existing database to remove"</span>);
      }
    }

    dbInstance = <span class="hljs-keyword">await</span> createRxDatabase({
      name: <span class="hljs-string">"appdb"</span>,
      storage,
      multiInstance: <span class="hljs-literal">false</span>,
      eventReduce: <span class="hljs-literal">true</span>,
    });

    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"initDB: Database created successfully"</span>);
    <span class="hljs-keyword">return</span> dbInstance;
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"initDB: Failed to create database"</span>, error);
    <span class="hljs-keyword">throw</span> error;
  }
}

<span class="hljs-comment">// Cleanup for HMR</span>
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> <span class="hljs-built_in">window</span> !== <span class="hljs-string">"undefined"</span> &amp;&amp; <span class="hljs-keyword">import</span>.meta.hot) {
  <span class="hljs-keyword">import</span>.meta.hot.dispose(<span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"HMR: Disposing database"</span>);
    <span class="hljs-keyword">if</span> (dbInstance) {
      <span class="hljs-keyword">await</span> dbInstance.destroy();
      dbInstance = <span class="hljs-literal">null</span>;
    }
  });
}
</code></pre>
<p>This code uses RxDB to create a client-side database which is <code>appdb</code>. We use the function <code>getRxStorageDexie()</code> to provide IndexDB storage when used in browsers.</p>
<p>In dev mode, we can clear the DB on each reload, giving us a clean state. Server-side execution is protected by using the <code>window</code> check. HMR cleanup guarantees that the DB is reset properly when hot reloading.</p>
<h2 id="heading-understanding-local-persistence-with-rxdb">Understanding Local Persistence with RxDB</h2>
<p>Before we move to the next section, lets go over the concept of local persistence with RxDB. Our data is likely to disappear when the page is reloaded during development because RxDB used a browser-based database engine for persisting data locally. So we’ll be using the Dexie storage adapter which stores all of our apps data inside a browser’s IndexedDB.</p>
<p>So basically, this means that our todos actually don’t persist in the browser, even if we close and reopen the app – but there’s a way to get this working in our app.</p>
<p>In the <code>src/db/client.ts</code> file, there just happens to be a section of code that looks like this:</p>
<pre><code class="lang-typescript">    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">import</span>.meta.env.DEV) {
      <span class="hljs-keyword">try</span> {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"initDB: Removing existing database (dev mode)"</span>);
        <span class="hljs-keyword">await</span> removeRxDatabase(<span class="hljs-string">"appdb"</span>, storage);
      } <span class="hljs-keyword">catch</span> (e) {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"initDB: No existing database to remove"</span>);
      }
    }
</code></pre>
<p>This code is making sure that when we’re in development mode, our database is removed and then recreated every time the app is reloaded. This is quite useful, because when we’re actively developing and changing schemas, it can guarantee that old data is not going to conflict with new database structures.</p>
<p>The downside, though, is that todos will disappear every time the page is refreshed. This behaviour is expected while running our app locally in dev mode. If you want todos to persist between reloads, then all you have to do is comment out this code block:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Comment or remove this code block to persist data across reloads</span>
   <span class="hljs-keyword">if</span> (<span class="hljs-keyword">import</span>.meta.env.DEV) {
      <span class="hljs-keyword">try</span> {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"initDB: Removing existing database (dev mode)"</span>);
        <span class="hljs-keyword">await</span> removeRxDatabase(<span class="hljs-string">"appdb"</span>, storage);
      } <span class="hljs-keyword">catch</span> (e) {
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"initDB: No existing database to remove"</span>);
      }
    }
</code></pre>
<p>After making this update, RxDB will now store your todos in IndexedDB and they will be automatically loaded whenever you revisit the app. You can even see this for yourself by opening your browser while the app is running and navigating to DevTools -&gt; Application -&gt; IndexedDB -&gt; appdb.</p>
<p>See the examples shown here:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761221020837/2a7fd37f-e638-4c6b-997f-ecde0aa789bb.png" alt="TanStack to do list app" class="image--center mx-auto" width="1114" height="746" loading="lazy"></p>
<p>Here you can see an example of what our app looks like with some tasks:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761221062751/431c4ac5-39d3-4adb-b197-bd4cc01a538e.png" alt="IndexedDB in the browser with tasks" class="image--center mx-auto" width="1814" height="1158" loading="lazy"></p>
<p>Here, you can see that our data stored inside IndexedDB in our browser.</p>
<p>The tasks should remain there until you have manually cleared the browser data.</p>
<h2 id="heading-creating-a-todo-collection">Creating a Todo Collection</h2>
<p>Now, let's work on our <code>src/db/todoCollection.ts</code> file. Copy and paste this code into the empty file in the codebase:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { initDB } <span class="hljs-keyword">from</span> <span class="hljs-string">"./client"</span>;

<span class="hljs-keyword">let</span> todoCollectionInstance: <span class="hljs-built_in">any</span> = <span class="hljs-literal">null</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createTodoCollection</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// Protect against server-side execution</span>
  <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> <span class="hljs-built_in">window</span> === <span class="hljs-string">"undefined"</span>) {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"createTodoCollection: Not in browser, skipping"</span>);
    <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
  }

  <span class="hljs-keyword">if</span> (todoCollectionInstance) {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"createTodoCollection: Returning existing collection"</span>);
    <span class="hljs-keyword">return</span> todoCollectionInstance;
  }

  <span class="hljs-keyword">try</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"createTodoCollection: Initializing database"</span>);
    <span class="hljs-keyword">const</span> db = <span class="hljs-keyword">await</span> initDB();

    <span class="hljs-keyword">if</span> (!db) {
      <span class="hljs-built_in">console</span>.error(
        <span class="hljs-string">"createTodoCollection: Database initialization returned null"</span>,
      );
      <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
    }

    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"createTodoCollection: Adding collections"</span>);
    <span class="hljs-keyword">if</span> (!db.todos) {
      <span class="hljs-keyword">await</span> db.addCollections({
        todos: {
          schema: {
            version: <span class="hljs-number">0</span>,
            primaryKey: <span class="hljs-string">"id"</span>,
            <span class="hljs-keyword">type</span>: <span class="hljs-string">"object"</span>,
            properties: {
              id: {
                <span class="hljs-keyword">type</span>: <span class="hljs-string">"string"</span>,
                maxLength: <span class="hljs-number">100</span>,
              },
              title: {
                <span class="hljs-keyword">type</span>: <span class="hljs-string">"string"</span>,
              },
              completed: {
                <span class="hljs-keyword">type</span>: <span class="hljs-string">"boolean"</span>,
              },
            },
            required: [<span class="hljs-string">"id"</span>, <span class="hljs-string">"title"</span>, <span class="hljs-string">"completed"</span>],
          },
        },
      });
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"createTodoCollection: Collections added successfully"</span>);
    }

    <span class="hljs-comment">// Return the RxDB collection directly</span>
    todoCollectionInstance = db.todos;

    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"createTodoCollection: Collection created successfully"</span>);
    <span class="hljs-keyword">return</span> todoCollectionInstance;
  } <span class="hljs-keyword">catch</span> (error) {
    <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"createTodoCollection: Failed to create collection"</span>, error);
    <span class="hljs-keyword">throw</span> error;
  }
}
</code></pre>
<p>With this file, we define a <code>todos</code> collection schema which has an <code>id</code>, <code>title</code>, and <code>completed</code> fields. This schema is able to ensure that the structure and validation is correct, and we memoize the collection instance which prevents multiple DB connections from occurring. The code then returns a live RxDB collection which is ready for querying and mutation.</p>
<h2 id="heading-creating-our-crud-actions">Creating Our CRUD Actions</h2>
<p>It’s now time to work on our CRUD actions. These allow us to perform the usual updates/changes to the data in our todo list.</p>
<p>Open the <code>src/db/actions.ts</code> file and copy and paste this code into it:</p>
<pre><code class="lang-ts"><span class="hljs-keyword">import</span> { createTodoCollection } <span class="hljs-keyword">from</span> <span class="hljs-string">"./todoCollection"</span>;

<span class="hljs-keyword">let</span> collectionPromise: <span class="hljs-built_in">Promise</span>&lt;<span class="hljs-built_in">any</span>&gt; | <span class="hljs-literal">null</span> = <span class="hljs-literal">null</span>;

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getCollection</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> <span class="hljs-built_in">window</span> === <span class="hljs-string">"undefined"</span>) {
    <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
  }

  <span class="hljs-keyword">if</span> (!collectionPromise) {
    collectionPromise = createTodoCollection();
  }
  <span class="hljs-keyword">return</span> collectionPromise;
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> TodoActions = {
  <span class="hljs-keyword">async</span> getAll() {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">const</span> collection = <span class="hljs-keyword">await</span> getCollection();
      <span class="hljs-keyword">if</span> (!collection) <span class="hljs-keyword">return</span> [];

      <span class="hljs-keyword">const</span> docs = <span class="hljs-keyword">await</span> collection.find().exec();
      <span class="hljs-keyword">return</span> docs.map(<span class="hljs-function">(<span class="hljs-params">doc: <span class="hljs-built_in">any</span></span>) =&gt;</span> ({
        id: doc.id,
        title: doc.title,
        completed: doc.completed,
      }));
    } <span class="hljs-keyword">catch</span> (error) {
      <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"TodoActions.getAll error:"</span>, error);
      <span class="hljs-keyword">throw</span> error;
    }
  },

  <span class="hljs-keyword">async</span> add(title: <span class="hljs-built_in">string</span>) {
    <span class="hljs-keyword">const</span> collection = <span class="hljs-keyword">await</span> getCollection();
    <span class="hljs-keyword">if</span> (!collection) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Collection not initialized"</span>);

    <span class="hljs-keyword">await</span> collection.insert({
      id: crypto.randomUUID(),
      title,
      completed: <span class="hljs-literal">false</span>,
    });
  },

  <span class="hljs-keyword">async</span> update(
    id: <span class="hljs-built_in">string</span>,
    changes: Partial&lt;{ title: <span class="hljs-built_in">string</span>; completed: <span class="hljs-built_in">boolean</span> }&gt;
  ) {
    <span class="hljs-keyword">const</span> collection = <span class="hljs-keyword">await</span> getCollection();
    <span class="hljs-keyword">if</span> (!collection) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Collection not initialized"</span>);

    <span class="hljs-keyword">const</span> doc = <span class="hljs-keyword">await</span> collection.findOne(id).exec();
    <span class="hljs-keyword">if</span> (doc) {
      <span class="hljs-keyword">const</span> patch: <span class="hljs-built_in">any</span> = {};
      <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> changes.title !== <span class="hljs-string">"undefined"</span>) patch.title = changes.title;
      <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> changes.completed !== <span class="hljs-string">"undefined"</span>)
        patch.completed = changes.completed;
      <span class="hljs-keyword">if</span> (<span class="hljs-built_in">Object</span>.keys(patch).length &gt; <span class="hljs-number">0</span>) {
        <span class="hljs-keyword">await</span> doc.patch(patch);
      }
    }
  },

  <span class="hljs-keyword">async</span> toggle(id: <span class="hljs-built_in">string</span>) {
    <span class="hljs-keyword">const</span> collection = <span class="hljs-keyword">await</span> getCollection();
    <span class="hljs-keyword">if</span> (!collection) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Collection not initialized"</span>);

    <span class="hljs-keyword">const</span> doc = <span class="hljs-keyword">await</span> collection.findOne(id).exec();
    <span class="hljs-keyword">if</span> (doc) {
      <span class="hljs-keyword">await</span> doc.patch({ completed: !doc.completed });
    }
  },

  <span class="hljs-keyword">async</span> remove(id: <span class="hljs-built_in">string</span>) {
    <span class="hljs-keyword">const</span> collection = <span class="hljs-keyword">await</span> getCollection();
    <span class="hljs-keyword">if</span> (!collection) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"Collection not initialized"</span>);

    <span class="hljs-keyword">const</span> doc = <span class="hljs-keyword">await</span> collection.findOne(id).exec();
    <span class="hljs-keyword">if</span> (doc) {
      <span class="hljs-keyword">await</span> doc.remove();
    }
  },
};
</code></pre>
<p>With this code, we use the <code>getCollection()</code> function to ensure that we only initialise the collection once. Each CRUD method (getAll, add, toggle, remove) interacts directly with RxDB and the methods use the native browser <code>crypto.randomUUID()</code> to generate a unique ID. We can now safely handle server-side rendering, as we skip DB access on the server using this strategy.</p>
<h2 id="heading-creating-the-frontend-page">Creating the Frontend Page</h2>
<p>All that remains is the frontend user interface, as we’ve written the bulk of the logic already. We’re going to replace the default <code>src/routes/index.tsx</code> file with our own CRUD UI, so just replace all of the code in that file with this code here:</p>
<pre><code class="lang-tsx">import * as React from "react";
import { createFileRoute } from "@tanstack/react-router";
import { TodoActions } from "../db/actions";

function Index() {
  const [todos, setTodos] = React.useState&lt;
    Array&lt;{ id: string; title: string; completed: boolean }&gt;
  &gt;([]);
  const [title, setTitle] = React.useState("");
  const [isLoading, setIsLoading] = React.useState(true);
  const [error, setError] = React.useState&lt;Error | null&gt;(null);
  const [editingId, setEditingId] = React.useState&lt;string | null&gt;(null);
  const [editingTitle, setEditingTitle] = React.useState("");

  React.useEffect(() =&gt; {
    let active = true;

    (async () =&gt; {
      try {
        console.log("Index: Loading todos");
        const data = await TodoActions.getAll();
        console.log("Index: Todos loaded", data);
        if (active) {
          setTodos(data);
          setIsLoading(false);
        }
      } catch (err) {
        console.error("Index: Failed to load todos:", err);
        if (active) {
          setError(err as Error);
          setIsLoading(false);
        }
      }
    })();

    return () =&gt; {
      active = false;
    };
  }, []);

  const handleAdd = async (e: React.FormEvent) =&gt; {
    e.preventDefault();
    if (title.trim()) {
      try {
        await TodoActions.add(title);
        setTodos(await TodoActions.getAll());
        setTitle("");
      } catch (err) {
        console.error("Failed to add todo:", err);
        setError(err as Error);
      }
    }
  };

  const handleToggle = async (id: string) =&gt; {
    try {
      await TodoActions.toggle(id);
      setTodos(await TodoActions.getAll());
    } catch (err) {
      console.error("Failed to toggle todo:", err);
    }
  };

  const handleRemove = async (id: string) =&gt; {
    try {
      await TodoActions.remove(id);
      setTodos(await TodoActions.getAll());
    } catch (err) {
      console.error("Failed to remove todo:", err);
    }
  };

  const startEdit = (todo: { id: string; title: string }) =&gt; {
    setEditingId(todo.id);
    setEditingTitle(todo.title);
  };

  const cancelEdit = () =&gt; {
    setEditingId(null);
    setEditingTitle("");
  };

  const saveEdit = async () =&gt; {
    if (!editingId) return;
    const newTitle = editingTitle.trim();
    if (!newTitle) return;
    try {
      await TodoActions.update(editingId, { title: newTitle });
      setTodos(await TodoActions.getAll());
      setEditingId(null);
      setEditingTitle("");
    } catch (err) {
      console.error("Failed to update todo:", err);
    }
  };

  if (isLoading) {
    return (
      &lt;main className="p-6 max-w-lg mx-auto"&gt;
        &lt;div className="text-center"&gt;Loading database...&lt;/div&gt;
      &lt;/main&gt;
    );
  }

  if (error) {
    return (
      &lt;main className="p-6 max-w-lg mx-auto"&gt;
        &lt;div className="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded"&gt;
          &lt;strong className="font-bold"&gt;Error: &lt;/strong&gt;
          &lt;span className="block sm:inline"&gt;{error.message}&lt;/span&gt;
          &lt;details className="mt-2"&gt;
            &lt;summary className="cursor-pointer"&gt;Show details&lt;/summary&gt;
            &lt;pre className="mt-2 text-xs overflow-auto"&gt;{error.stack}&lt;/pre&gt;
          &lt;/details&gt;
        &lt;/div&gt;
      &lt;/main&gt;
    );
  }

  return (
    &lt;main className="p-6 max-w-lg mx-auto"&gt;
      &lt;h1 className="text-2xl font-bold mb-4"&gt;TanStack CRUD (RxDB)&lt;/h1&gt;

      &lt;form onSubmit={handleAdd} className="flex gap-2 mb-4"&gt;
        &lt;input
          value={title}
          onChange={(e) =&gt; setTitle(e.target.value)}
          placeholder="Add a new task"
          className="border rounded px-3 py-2 flex-1"
        /&gt;
        &lt;button
          type="submit"
          className="bg-blue-500 text-white px-4 py-2 rounded hover:bg-blue-600"
        &gt;
          Add
        &lt;/button&gt;
      &lt;/form&gt;

      &lt;ul&gt;
        {todos.length === 0 ? (
          &lt;li className="text-gray-500 text-center py-4"&gt;No todos yet&lt;/li&gt;
        ) : (
          todos.map((todo) =&gt; (
            &lt;li
              key={todo.id}
              className="flex justify-between items-center py-2 border-b"
            &gt;
              {editingId === todo.id ? (
                &lt;div className="flex w-full items-center gap-2"&gt;
                  &lt;input
                    value={editingTitle}
                    onChange={(e) =&gt; setEditingTitle(e.target.value)}
                    className="border rounded px-2 py-1 flex-1"
                  /&gt;
                  &lt;button
                    onClick={saveEdit}
                    className="bg-green-500 text-white px-3 py-1 rounded hover:bg-green-600"
                  &gt;
                    Save
                  &lt;/button&gt;
                  &lt;button
                    onClick={cancelEdit}
                    className="px-3 py-1 rounded border"
                  &gt;
                    Cancel
                  &lt;/button&gt;
                &lt;/div&gt;
              ) : (
                &lt;&gt;
                  &lt;span
                    onClick={() =&gt; handleToggle(todo.id)}
                    className={
                      todo.completed
                        ? "line-through cursor-pointer"
                        : "cursor-pointer"
                    }
                  &gt;
                    {todo.title}
                  &lt;/span&gt;
                  &lt;div className="flex items-center gap-3"&gt;
                    &lt;button
                      onClick={() =&gt; startEdit(todo)}
                      className="text-blue-500 hover:text-blue-700"
                    &gt;
                      Edit
                    &lt;/button&gt;
                    &lt;button
                      onClick={() =&gt; handleRemove(todo.id)}
                      className="text-red-500 hover:text-red-700"
                    &gt;
                      ✕
                    &lt;/button&gt;
                  &lt;/div&gt;
                &lt;/&gt;
              )}
            &lt;/li&gt;
          ))
        )}
      &lt;/ul&gt;
    &lt;/main&gt;
  );
}

export const Route = createFileRoute("/")({
  component: Index,
});
</code></pre>
<p>Our updated <code>index.tsx</code> file uses TanStack Router to define our root page, and we have React hooks to handle state, error handling, and the CRUD updates.</p>
<p>Our frontend is set up to show loading/error states for a much smoother UX, and each button triggers a corresponding <code>TodoActions</code> method. The result is that we have a fully reactive, local CRUD app.</p>
<p>That's all there is to it. To run the app, use the usual run command for a Vite application:</p>
<pre><code class="lang-bash">npm run dev
</code></pre>
<h2 id="heading-how-does-tanstack-start-compare-to-nextjs-and-remix">How Does TanStack Start Compare to Next.js and Remix?</h2>
<p>TanStack Start seems pretty impressive, right? But lets see how it compares to the other two big established frameworks, Next.js and Remix.</p>
<p>Next.js recently released <a target="_blank" href="https://nextjs.org/blog/next-16">version 16</a>, which brought some new improvements and features which you can read about. It’s without a doubt the most well known and used React framework available right now.</p>
<p>Remix also has a lot going for it, recently held its <a target="_blank" href="https://remix.run/blog/remix-jam-2025-recap">Remix Jam 2025 recap event</a> which you can also read and learn about.</p>
<p>TanStack Start, on the other hand, is built using the popular <a target="_blank" href="https://vite.dev/">Vite</a> build tool which it uses for its development, workflow, and production builds alongside TanStack Router and other libraries.</p>
<p>This is how all three compare when we put them side by side in a table:</p>
<div class="hn-table">
<table>
<thead>
<tr>
<td>Feature</td><td><strong>TanStack Start</strong></td><td><strong>Next.js</strong></td><td><strong>Remix</strong></td></tr>
</thead>
<tbody>
<tr>
<td>Routing</td><td>TanStack Router</td><td>File-based</td><td>Nested routes</td></tr>
<tr>
<td>Type Safety</td><td>Deep TS integration</td><td>Partial</td><td>Full</td></tr>
<tr>
<td>Data Loading</td><td>Loaders/Actions</td><td>Server Components</td><td>Loaders/Actions</td></tr>
<tr>
<td>SSR Support</td><td>Optional</td><td>Built-in</td><td>Built-in</td></tr>
<tr>
<td>Bundler</td><td>Vite / Bun</td><td>Webpack / Turbopack</td><td>Remix Compiler</td></tr>
<tr>
<td>DX</td><td>Simple, minimal</td><td>Full-stack ecosystem</td><td>Full-stack with conventions</td></tr>
</tbody>
</table>
</div><p>As you can see, TanStack Start offers quite a lot of flexibility. It doesn’t force conventions like Next.js or Remix because of its design and it has just the right amount of structure for developers who want control and transparency in their projects. All three are great options, though.</p>
<h2 id="heading-when-should-you-use-tanstack-start-nextjs-or-remix">When Should You Use TanStack Start, Next.js, or Remix?</h2>
<p>Each of these frameworks has its advantages and disadvantages depending on your projects setup and priorities. We have to take into account the performance, flexibility, ecosystem as well as the developer experience.</p>
<p>With all of this in mind, it paints a clearer picture on when its best to use them.</p>
<h3 id="heading-when-to-use-tanstack-start">When to use TanStack Start</h3>
<p>If you want full control of your architecture without having to be locked into conventions, then TanStack Start is a great choice. It’s ideal if you value having transparency alongside the flexibility that comes from it.</p>
<p>You’ll find that its pretty useful especially in projects which need fine-grained control over routing, data fetching, and caching – without having to worry about the overhead of a large opinionated framework.</p>
<p>The integration between Vite and TanStack Router makes it a lightweight and blazing fast tool which can be great for greenfield projects and teams that want to have a modular setup.</p>
<h3 id="heading-when-to-use-nextjs">When to use Next.js</h3>
<p>Next.js is a great option when you need to have a production-ready scalability and extensive documentation, with a very large ecosystem. The framework has been a go to for startups as well as enterprises because of its tight integration with React Server Components, hosting with Vercel, and community driven packages.</p>
<p>So if SEO, SSR, or hybrid rendering are part of your team’s core needs, or if you want to shop something fast with a proven foundation, then Next.js is the safest and most mature way to go about doing it.</p>
<h3 id="heading-when-to-use-remixjs">When to use Remix.js</h3>
<p>Remix is a great choice when you want to have a strong focus on web fundamentals, progressive enhancement, and a reliable UX. It’s good for applications where you want to use a browser’s native capabilities such as forms, caching, and accessibility while also benefitting from a modern full-stack workflow.</p>
<p>It's also great for teams that want to have the simplicity of conventional routing and loaders while also remaining close to the original platform.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In this article, we built a CRUD app from scratch using:</p>
<ul>
<li><p>TanStack Start for the app structure and routing</p>
</li>
<li><p>TanStackDB for the reactive data management</p>
</li>
<li><p>RxDB for an offline-first experience and local persistence</p>
</li>
</ul>
<p>You learned how to initialise a local database and collections as well as perform CRUD operations safely.</p>
<p>The TanStack ecosystem is quite powerful, and there are many tools available. They all fit really well together to give you a next-gen, local-first, reactive web development experience. TanStack Start is likely to become one of your favourite ways for building React applications and has a lot of potential for growth.</p>
<p>The official TanStack demos are still available in your nav on the homepage, and they are well worth checking out. Give the TanStack ecosystem a try. I think it could easily become your main tech stack.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Clerk vs Kinde vs Better Auth: How to Choose the Right Next.js Authentication Library ]]>
                </title>
                <description>
                    <![CDATA[ Authentication is an important aspect when building applications, especially if they hold financial information or require users to sign into accounts. Building an auth library can be a lot of work, and there is no need to reinvent the wheel when so ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-choose-the-right-nextjs-authentication-library/</link>
                <guid isPermaLink="false">68d2074ea281a4364a16d324</guid>
                
                    <category>
                        <![CDATA[ Next.js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ authentication ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Andrew Baisden ]]>
                </dc:creator>
                <pubDate>Tue, 23 Sep 2025 02:34:54 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1758594098828/8b2e3142-9067-4a02-b1e5-63319dde45de.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Authentication is an important aspect when building applications, especially if they hold financial information or require users to sign into accounts. Building an auth library can be a lot of work, and there is no need to reinvent the wheel when so many efficient libraries already exist.</p>
<p>In this article, we’ll compare some libraries that you can use for authentication in your Next app. They include: <a target="_blank" href="https://clerk.com/">Clerk</a>, <a target="_blank" href="https://kinde.com/">Kinde</a> and <a target="_blank" href="https://www.better-auth.com/">Better Auth</a>. You’ll learn how to set up these tools in a Next.js application, with the goal of creating at least one authenticated, protected page route.</p>
<p>The aim here is simply to see how each tool works when it comes to speed of setup and ease of use.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#heading-what-are-clerk-kinde-and-better-auth">What are Clerk, Kinde and Better Auth?</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-authentication-using-clerk">How to Set Up Authentication Using Clerk</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-authentication-using-kinde">How to Set Up Authentication Using Kinde</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-set-up-authentication-using-better-auth">How to Set Up Authentication Using Better Auth</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-when-to-use-each-library">When To Use Each Library</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></p>
</li>
</ul>
<h2 id="heading-what-are-clerk-kinde-and-better-auth">What are Clerk, Kinde and Better Auth?</h2>
<p>Clerk, Kinde and Better Auth are basically modern authentication providers, much like <a target="_blank" href="https://authjs.dev/">Auth.js</a>, which have been built with developers in mind. Although they share similarities, they have certain aspects that differentiate them from each other.</p>
<p>To begin with, Clerk is more full-featured. It's more of a hosted solution that offers components which are ready-made, as well as user management and other integrations, which allow you to get up and running pretty quickly.</p>
<p>Kinde, on the other hand, is more of a developer platform, which has authentication, feature flags and team management all in one place.</p>
<p>Better Auth is more of an open-source and code-first place that gives developers the building blocks to create authentication without having to be locked into an ecosystem.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>The prerequisites for this tutorial are minimal, and the databases and Prisma ORM are only required for Better Auth. Alternatively, you can use any of the databases inside a Docker container instead of installing them locally, but that is outside of the scope of this tutorial.</p>
<p>You’ll need these to follow along:</p>
<ul>
<li><p>Node and npm installed</p>
</li>
<li><p><a target="_blank" href="https://www.prisma.io/">Prisma ORM</a></p>
</li>
<li><p>SQLite, PostgreSQL, or MySQL database set up locally</p>
</li>
<li><p>Code editor/IDE</p>
</li>
</ul>
<p>Let's see how to set up authentication with all three auth platforms in a Next.js application. We’ll use separate Next.js applications to set up each library so that the codebase will remain clean, and you can experience what it's like to set them up from scratch.</p>
<p>First, decide on a location for your project, like on the desktop and then use the command <code>npx create-next-app@latest</code> to set up a Next.js project. You can just use the default configuration. These are the settings I used:</p>
<p>✔ What is your project named? … my-app<br>✔ Would you like to use TypeScript? … No / <strong>Yes</strong><br>✔ Which linter would you like to use? › ESLint<br>✔ Would you like to use Tailwind CSS? … No / <strong>Yes</strong><br>✔ Would you like your code inside a <code>src/</code> directory? … No / <strong>Yes</strong><br>✔ Would you like to use App Router? (recommended) … No / <strong>Yes</strong><br>✔ Would you like to use Turbopack? (recommended) … No / <strong>Yes</strong><br>✔ Would you like to customize the import alias (<code>@/*</code> by default)? … <strong>No</strong> / Yes</p>
<p>We are creating three apps, so it's up to you if you want to duplicate the codebases now and give them different names like <code>my-app</code>, <code>my-app2</code> and <code>my-app3</code> or do them later when we reach each section.</p>
<h2 id="heading-how-to-set-up-authentication-using-clerk">How to Set Up Authentication Using Clerk</h2>
<p>With your Next.js project set up, <code>cd</code> into the <code>my-app</code> folder or whatever name you gave the project and run the following command to install the Next.js SDK for Clerk:</p>
<pre><code class="lang-shell">npm install @clerk/nextjs
</code></pre>
<p>Now we need to create a middleware file that will grant us access to user authentication throughout our entire app.</p>
<p>Create a <code>middleware.ts</code> file with this code inside the <code>/src</code> folder:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { clerkMiddleware } <span class="hljs-keyword">from</span> <span class="hljs-string">'@clerk/nextjs/server'</span>

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> clerkMiddleware()

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> config = {
  matcher: [
    <span class="hljs-comment">// Skip Next.js internals and all static files, unless found in search params</span>
    <span class="hljs-string">'/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)'</span>,
    <span class="hljs-comment">// Always run for API routes</span>
    <span class="hljs-string">'/(api|trpc)(.*)'</span>,
  ],
}
</code></pre>
<p>With this file, authentication is set up for different page routes.</p>
<p>All that's left is to add the <code>&lt;ClerkProvider&gt;</code> component to your app's <code>layout.tsx</code> file so that authentication is available throughout your entire app.</p>
<p>Just replace all of the code inside of <code>src/app/layout.tsx</code> with this code here:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { Metadata } <span class="hljs-keyword">from</span> <span class="hljs-string">'next'</span>;
<span class="hljs-keyword">import</span> { Geist, Geist_Mono } <span class="hljs-keyword">from</span> <span class="hljs-string">'next/font/google'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'./globals.css'</span>;
<span class="hljs-keyword">import</span> {
  ClerkProvider,
  SignInButton,
  SignUpButton,
  SignedIn,
  SignedOut,
  UserButton,
} <span class="hljs-keyword">from</span> <span class="hljs-string">'@clerk/nextjs'</span>;

<span class="hljs-keyword">const</span> geistSans = Geist({
  variable: <span class="hljs-string">'--font-geist-sans'</span>,
  subsets: [<span class="hljs-string">'latin'</span>],
});

<span class="hljs-keyword">const</span> geistMono = Geist_Mono({
  variable: <span class="hljs-string">'--font-geist-mono'</span>,
  subsets: [<span class="hljs-string">'latin'</span>],
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> metadata: Metadata = {
  title: <span class="hljs-string">'Create Next App'</span>,
  description: <span class="hljs-string">'Generated by create next app'</span>,
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">RootLayout</span>(<span class="hljs-params">{
  children,
}: Readonly&lt;{
  children: React.ReactNode;
}&gt;</span>) </span>{
  <span class="hljs-keyword">return</span> (
    &lt;ClerkProvider&gt;
      &lt;html lang=<span class="hljs-string">"en"</span>&gt;
        &lt;body
          className={<span class="hljs-string">`<span class="hljs-subst">${geistSans.variable}</span> <span class="hljs-subst">${geistMono.variable}</span> antialiased`</span>}
        &gt;
          &lt;header className=<span class="hljs-string">"flex justify-end items-center p-4 gap-4 h-16"</span>&gt;
            &lt;SignedOut&gt;
              &lt;SignInButton /&gt;
              &lt;SignUpButton&gt;
                &lt;button className=<span class="hljs-string">"bg-[#6c47ff] text-white rounded-full font-medium text-sm sm:text-base h-10 sm:h-12 px-4 sm:px-5 cursor-pointer"</span>&gt;
                  Sign Up
                &lt;/button&gt;
              &lt;/SignUpButton&gt;
            &lt;/SignedOut&gt;
            &lt;SignedIn&gt;
              &lt;UserButton /&gt;
            &lt;/SignedIn&gt;
          &lt;/header&gt;
          {children}
        &lt;/body&gt;
      &lt;/html&gt;
    &lt;/ClerkProvider&gt;
  );
}
</code></pre>
<p>What we did was to import the <code>ClerkProvider</code>, as well as the buttons for signing in and out using Clerk authentication. These additions have been added to the <code>layout.tsx</code> file which means that they are available throughout our entire application. So every page should display the sign in flow at the top of the page.</p>
<p>The <code>ClerkProvider</code> component is needed for integrating Clerk inside of our application, so now we can use session and user context with Clerks hooks and components.</p>
<p>Now you can run your Next.js app with <code>npm run dev</code>, and you should see the homepage, as well as a sign-in and sign-up button at the top of the page, as shown here:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757428533274/31f43e8f-7826-4d4c-bf10-99b5ea7d8a76.png" alt="Next.js homepage with Clerk authentication setup" class="image--center mx-auto" width="2482" height="2620" loading="lazy"></p>
<p>Clicking the signup button will take you to a sign-up form where you can use an email address or sign in with Google, which is pretty easy.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757428605871/94e754e7-897f-42f2-84df-361d97226617.png" alt="Clerk Sign up form" class="image--center mx-auto" width="856" height="1184" loading="lazy"></p>
<p>When you have signed in, you should see your profile picture and account information in the top right-hand corner of the screen. That's the hard part done - all that's left is to create a page and then make the route protected so that only a signed-in user can access it.</p>
<p>To begin with, lets update our <code>middleware.ts</code> file with some code which lets us protect a route:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { clerkMiddleware, createRouteMatcher } <span class="hljs-keyword">from</span> <span class="hljs-string">'@clerk/nextjs/server'</span>

<span class="hljs-keyword">const</span> isProtectedRoute = createRouteMatcher([<span class="hljs-string">'/dashboard(.*)'</span>])

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> clerkMiddleware(<span class="hljs-keyword">async</span> (auth, req) =&gt; {
  <span class="hljs-keyword">if</span> (isProtectedRoute(req)) <span class="hljs-keyword">await</span> auth.protect()
})

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> config = {
  matcher: [
    <span class="hljs-comment">// Skip Next.js internals and all static files, unless found in search params</span>
    <span class="hljs-string">'/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)'</span>,
    <span class="hljs-comment">// Always run for API routes</span>
    <span class="hljs-string">'/(api|trpc)(.*)'</span>,
  ],
}
</code></pre>
<p>We added some new imports for <code>createRouteMatcher</code>, which is a Clerk helper function that gives us the power to protect multiple routes. In this case, the dashboard page route in our application requires a user to be signed in to access the route. Now we need create a dashboard page. Create this folder and file inside the <code>app</code> folder: <code>dashboard/page.tsx</code>. Then complete the page by giving it some code like below:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Dashboard</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    &lt;&gt;
      &lt;h1&gt;Dashboard Page&lt;/h1&gt;
    &lt;/&gt;
  );
}
</code></pre>
<p>We created a simple page which has a heading that says Dashboard Page.</p>
<p>Congratulations, you have successfully added authentication to your Next app and protected a page route, and it only took a few steps! When you navigate to <a target="_blank" href="http://localhost:3000/dashboard">http://localhost:3000/dashboard</a> as a non-signed-in user, you should be redirected to a sign-in form as shown below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757428660606/5ac6984d-7f8c-46e2-bd0b-732920d5743e.png" alt="Clerk sign in form page" class="image--center mx-auto" width="848" height="1010" loading="lazy"></p>
<p>If you are already signed in, then you should see the Dashboard page. You can learn more using the <a target="_blank" href="https://clerk.com/docs">Clerk official documentation</a>.</p>
<h2 id="heading-how-to-set-up-authentication-using-kinde">How to Set Up Authentication Using Kinde</h2>
<p>Create another Next.js application for this project if you have not done so already. Kinde will require you to create an account on their platform before using their authentication library. Let's go through the sign-up process.</p>
<p>Firstly, go to the <a target="_blank" href="https://kinde.com/">Kinde</a> website, and you should see a button that says "Start for free" or similar.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757428704023/dd6bf59e-2858-4f0a-8aa4-90645641ebe8.png" alt="Kinde website homepage" class="image--center mx-auto" width="1840" height="1052" loading="lazy"></p>
<p>Clicking that button should take you to a page where you can create an account:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757428737190/0cd172f6-ed9a-4a54-b599-182a8becfcfa.png" alt="Kinde create your account" class="image--center mx-auto" width="3006" height="1938" loading="lazy"></p>
<p>An email code verification may be required:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757428803166/8a883160-6e95-4611-91d3-efbabd74653c.png" alt="Kinde email code verification" class="image--center mx-auto" width="454" height="544" loading="lazy"></p>
<p>On the next screen, you should be able to enter your business details, which can be anything you want. Every time you set up authentication for an app, you will have to create an application for it on your account. Give it any name you want, like <code>app-clerk-test3272346214</code>. The same name will be used for the business and the domain.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757428845977/7d365554-9c15-41e0-b317-a12d58f3e7a6.png" alt="Kinde form business and domain" class="image--center mx-auto" width="472" height="821" loading="lazy"></p>
<p>On the next screen, we’ll choose to use an existing codebase because we have a local project:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757428896749/cd468f14-1b36-4025-8459-b51509519fa0.png" alt="Kinde existing codebase select" class="image--center mx-auto" width="1352" height="762" loading="lazy"></p>
<p>The codebase is in Next.js, so select it from the list:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757428936691/cf13041f-bc74-4432-a7ba-4234daf7c2a3.png" alt="Kinde select tech stack" class="image--center mx-auto" width="1368" height="1954" loading="lazy"></p>
<p>The next important step is choosing how users are going to sign in. I chose email and Google. You can select whichever options you desire:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757428976433/2885ed25-d4d0-496e-ac46-2f8b96c35a20.png" alt="Kinde user sign in form" class="image--center mx-auto" width="2312" height="1644" loading="lazy"></p>
<p>Now, on the last screen, choose to explore at your own pace.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757429011937/23a9d95f-2935-4a9c-947f-91d7adaf452e.png" alt="Kinde explore at own pace screen" class="image--center mx-auto" width="1530" height="996" loading="lazy"></p>
<p>And finally, we’ve reach the dashboard screen.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757429051925/2250e252-e84d-4ae9-bff8-a9e687692f70.png" alt="Kinde dashboard screen" class="image--center mx-auto" width="1342" height="2442" loading="lazy"></p>
<p>Viewing details lets you see your app keys and environment variables, among other useful information.</p>
<p>That's the long part out of the way, let's get to some code. Navigate to your project folder and then install the package for Kinde:</p>
<pre><code class="lang-shell">npm i @kinde-oss/kinde-auth-nextjs
</code></pre>
<p>Now, create a <code>.env.local</code> file and put it in the root folder of your project with your environment variables. You can find your environment variables in the Quick Start page of your application.</p>
<p>Here's an example:</p>
<pre><code class="lang-shell">KINDE_CLIENT_ID=&lt;your_kinde_client_id&gt;
KINDE_CLIENT_SECRET=&lt;your_kinde_client_secret&gt;
KINDE_ISSUER_URL=https://&lt;your_kinde_subdomain&gt;.kinde.com
KINDE_SITE_URL=http://localhost:3000
KINDE_POST_LOGOUT_REDIRECT_URL=http://localhost:3000
KINDE_POST_LOGIN_REDIRECT_URL=http://localhost:3000/dashboard
</code></pre>
<p>Next, you need to create the following API endpoint and folder structure and files as shown here <code>src/app/api/auth/[kindeAuth]/route.ts</code>.</p>
<p>This is the code needed for the <code>route.ts</code> file:</p>
<pre><code class="lang-shell">import {handleAuth} from "@kinde-oss/kinde-auth-nextjs/server";

export const GET = handleAuth();
</code></pre>
<p>With this code, Kinde can now handle auth endpoints inside our application.</p>
<p>Once again, you’ll need a <code>middleware.ts</code> file so that authentication can be set up properly in your app. The file should be in the root directory and needs this code added to it:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { withAuth } <span class="hljs-keyword">from</span> <span class="hljs-string">"@kinde-oss/kinde-auth-nextjs/middleware"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">middleware</span>(<span class="hljs-params">req</span>) </span>{
  <span class="hljs-keyword">return</span> withAuth(req);
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> config = {
  matcher: [
    <span class="hljs-comment">// Run on everything but Next internals and static files</span>
    <span class="hljs-string">'/((?!_next|[^?]*\\.(?:html?|css|js(?!on)|jpe?g|webp|png|gif|svg|ttf|woff2?|ico|csv|docx?|xlsx?|zip|webmanifest)).*)'</span>,
  ]
};
</code></pre>
<p>Like before, we can now protect page routes with this file. Your app has to be wrapped in a Kinde Auth Provider so that you can access the data throughout your app.</p>
<p>Create an <code>AuthProvider.tsx</code> file inside the <code>app</code> directory with this code:</p>
<pre><code class="lang-typescript"><span class="hljs-string">"use client"</span>;
<span class="hljs-keyword">import</span> {KindeProvider} <span class="hljs-keyword">from</span> <span class="hljs-string">"@kinde-oss/kinde-auth-nextjs"</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> AuthProvider = <span class="hljs-function">(<span class="hljs-params">{children}</span>) =&gt;</span> {
  <span class="hljs-keyword">return</span> &lt;KindeProvider&gt;{children}&lt;/KindeProvider&gt;;
};
</code></pre>
<p>Kinde uses a React Context Provider to maintain its internal state throughout our application by using the <code>KindeProvider</code> component.</p>
<p>Next, replace and update the <code>layout.tsx</code> file, so it is wrapped in the Auth Provider:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> <span class="hljs-keyword">type</span> { Metadata } <span class="hljs-keyword">from</span> <span class="hljs-string">'next'</span>;
<span class="hljs-keyword">import</span> { Geist, Geist_Mono } <span class="hljs-keyword">from</span> <span class="hljs-string">'next/font/google'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'./globals.css'</span>;
<span class="hljs-keyword">import</span> { AuthProvider } <span class="hljs-keyword">from</span> <span class="hljs-string">'./AuthProvider'</span>;
<span class="hljs-keyword">import</span> {
  RegisterLink,
  LoginLink,
  LogoutLink,
} <span class="hljs-keyword">from</span> <span class="hljs-string">'@kinde-oss/kinde-auth-nextjs/components'</span>;

<span class="hljs-keyword">const</span> geistSans = Geist({
  variable: <span class="hljs-string">'--font-geist-sans'</span>,
  subsets: [<span class="hljs-string">'latin'</span>],
});

<span class="hljs-keyword">const</span> geistMono = Geist_Mono({
  variable: <span class="hljs-string">'--font-geist-mono'</span>,
  subsets: [<span class="hljs-string">'latin'</span>],
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> metadata: Metadata = {
  title: <span class="hljs-string">'Create Next App'</span>,
  description: <span class="hljs-string">'Generated by create next app'</span>,
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">RootLayout</span>(<span class="hljs-params">{
  children,
}: Readonly&lt;{
  children: React.ReactNode;
}&gt;</span>) </span>{
  <span class="hljs-keyword">return</span> (
    &lt;AuthProvider&gt;
      &lt;div className=<span class="hljs-string">"grid grid-flow-col gap2"</span>&gt;
        &lt;LoginLink&gt;Sign <span class="hljs-keyword">in</span>&lt;/LoginLink&gt;
        &lt;RegisterLink&gt;Sign up&lt;/RegisterLink&gt;
        &lt;LogoutLink&gt;Log out&lt;/LogoutLink&gt;
      &lt;/div&gt;
      &lt;html lang=<span class="hljs-string">"en"</span>&gt;
        &lt;body
          className={<span class="hljs-string">`<span class="hljs-subst">${geistSans.variable}</span> <span class="hljs-subst">${geistMono.variable}</span> antialiased`</span>}
        &gt;
          {children}
        &lt;/body&gt;
      &lt;/html&gt;
    &lt;/AuthProvider&gt;
  );
}
</code></pre>
<p>In this file, we also added buttons for signing up, signing in, and logging out which will be displayed at the top of every page as this is the main <code>layout.tsx</code> file. Our <code>AuthProvider</code> component is wrapped around our application which means we can now use Kinde throughout it.</p>
<p>The basic setup is now complete! Run the usual command to start the Next.js app, and you should see the sign-in flow buttons at the top of the screen.</p>
<p>You should be able to sign up and create an account. Doing so will redirect you to the dashboard screen, which shows a 404 error page. This is because we have not created a dashboard page yet.</p>
<p>This is what the Kinde register form looks like:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757429157871/c7344f32-cf4e-48d3-b43e-225f81912988.png" alt="Kinde Register form" class="image--center mx-auto" width="886" height="1388" loading="lazy"></p>
<p>And this is what the Kinde sign-in form looks like:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757429447577/3d30476a-9269-49a9-811c-dff0c16225d9.png" alt="Kinde sign in form" class="image--center mx-auto" width="862" height="950" loading="lazy"></p>
<p>Only one step remains now: creating a protected route for your authentication.</p>
<p>Create the following file structure and file for your dashboard page: <code>src/app/dashboard/page.tsx</code>.</p>
<p>Then add this code to the file:</p>
<pre><code class="lang-typescript"><span class="hljs-string">'use client'</span>;

<span class="hljs-keyword">import</span> { useKindeBrowserClient } <span class="hljs-keyword">from</span> <span class="hljs-string">'@kinde-oss/kinde-auth-nextjs'</span>;
<span class="hljs-keyword">import</span> { LoginLink } <span class="hljs-keyword">from</span> <span class="hljs-string">'@kinde-oss/kinde-auth-nextjs/components'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Dashboard</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> { isAuthenticated, isLoading } = useKindeBrowserClient();

  <span class="hljs-keyword">if</span> (isLoading) <span class="hljs-keyword">return</span> &lt;div&gt;Loading...&lt;/div&gt;;

  <span class="hljs-keyword">return</span> isAuthenticated ? (
    &lt;div&gt;
      &lt;p&gt;
        Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque ut
        ante enim. Maecenas ut eros nec diam vulputate sollicitudin. Cras ut
        quam leo. Pellentesque semper, lacus sodales gravida suscipit, metus
        quam congue eros, nec sagittis est dolor eu turpis. Nulla congue
        tristique venenatis. Donec ac venenatis mauris. Donec commodo cursus
        magna, vitae tincidunt magna vestibulum eget.
      &lt;/p&gt;
    &lt;/div&gt;
  ) : (
    &lt;div&gt;
      You have to &lt;LoginLink&gt;Login&lt;/LoginLink&gt; to see <span class="hljs-built_in">this</span> page
    &lt;/div&gt;
  );
}
</code></pre>
<p>This page file checks to see if the user is authenticated and signed in. If they are signed in, they see the Lorem ipsum text, and if they are not signed in, they will see a message telling them that they have to log in to see the page.</p>
<p>All you have to do is go to the <a target="_blank" href="http://localhost:3000/dashboard">dashboard route</a> as a signed-in or signed-out user to see it for yourself. And that's pretty much the basics of authentication using the Kinde platform. See the <a target="_blank" href="https://docs.kinde.com/">online documentation</a> to learn everything there is to know about it.</p>
<h2 id="heading-how-to-set-up-authentication-using-better-auth">How to Set Up Authentication Using Better Auth</h2>
<p>And lastly, let's create a project that uses Better Auth. Better Auth requires a database to store user data, so the setup will require a few more steps. You can find the installation guide <a target="_blank" href="https://www.better-auth.com/docs/installation">here</a>, but, we’ll also go through it here.</p>
<p>Ok, just like before, create a Next.js project if you have not done so yet and then install the <code>better-auth</code> package with this command:</p>
<pre><code class="lang-shell">npm install better-auth
</code></pre>
<p>Next, you have to set up your environment variables, so create a <code>.env</code> file with these values:</p>
<pre><code class="lang-shell">BETTER_AUTH_SECRET= #Create your own secret key!
BETTER_AUTH_URL=http://localhost:3000 # Base URL of your app
</code></pre>
<p>Make sure that you create a secret key, as if you were generating a secure password with uppercase and lowercase letters and numbers.</p>
<p>Let's get our Prisma package and PostgreSQL database set up, so run these scripts to initialise them:</p>
<pre><code class="lang-shell">npm install prisma --save-dev
npx prisma init
npm install @prisma/client
</code></pre>
<p>In the next step, we have to create a better auth instance. In this case, we will put the file in the <code>src/lib/auth.ts</code>.</p>
<p>So create an <code>auth.ts</code> file with this code:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { betterAuth } <span class="hljs-keyword">from</span> <span class="hljs-string">'better-auth'</span>;
<span class="hljs-keyword">import</span> { anonymous } <span class="hljs-keyword">from</span> <span class="hljs-string">'better-auth/plugins'</span>;
<span class="hljs-keyword">import</span> { prismaAdapter } <span class="hljs-keyword">from</span> <span class="hljs-string">'better-auth/adapters/prisma'</span>;
<span class="hljs-comment">// If your Prisma file is located elsewhere, you can change the path</span>
<span class="hljs-keyword">import</span> { PrismaClient } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/generated/prisma'</span>;

<span class="hljs-keyword">const</span> prisma = <span class="hljs-keyword">new</span> PrismaClient();
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> auth = betterAuth({
  database: prismaAdapter(prisma, {
    provider: <span class="hljs-string">'postgresql'</span>, <span class="hljs-comment">// or "mysql", "postgresql", ...etc</span>
  }),
  plugins: [anonymous()],
});
</code></pre>
<p>In this file, we have configured Better Auth to use Prisma ORM for our database connection, and we will be connecting to a PostgreSQL database. If you want, you can change it to a different database, but the setup might be different, so bear that in mind. You could also use Docker if you know how to set it up. Anonymous sign-in is the default for users.</p>
<p>Now we need to create our database tables for saving user information, so use this command in the terminal to do that:</p>
<pre><code class="lang-shell">npx @better-auth/cli generate
</code></pre>
<p>You might see this warning, just select yes with "y":</p>
<pre><code class="lang-shell">prisma:warn In production, we recommend using `prisma generate --no-engine` (See: `prisma generate --help`)
✔ The file ./prisma/schema.prisma already exists. Do you want to overwrite the schema to the file? … yes
</code></pre>
<p>For handling API requests, we must have a route handler set up on our server. Create a folder structure and file for the <code>route.ts</code> file, like shown here <code>/app/api/auth/[...all]/route.ts</code>.</p>
<p>Add this code to the file:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { auth } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/lib/auth'</span>; <span class="hljs-comment">// path to your auth file</span>
<span class="hljs-keyword">import</span> { toNextJsHandler } <span class="hljs-keyword">from</span> <span class="hljs-string">'better-auth/next-js'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> { POST, GET } = toNextJsHandler(auth);
</code></pre>
<p>This file lets you handle POST and GET requests for your auth file.</p>
<p>Lastly, we have to create a <code>lib/auth-client.ts</code> file. This file allows you to interact with the auth server, and it has a plugin so users can sign in anonymously.</p>
<p>And here is the code to put inside this file:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { createAuthClient } <span class="hljs-keyword">from</span> <span class="hljs-string">'better-auth/react'</span>;
<span class="hljs-keyword">import</span> { anonymousClient } <span class="hljs-keyword">from</span> <span class="hljs-string">'better-auth/client/plugins'</span>;
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> authClient = createAuthClient({
  <span class="hljs-comment">/** The base URL of the server (optional if you're using the same domain) */</span>
  baseURL: <span class="hljs-string">'http://localhost:3000'</span>,
  plugins: [anonymousClient()],
});
</code></pre>
<p>With this file, it's possible for users to sign in anonymously without having to even create an account or use social sign-in, thanks to the plugin.</p>
<p>All that remains is to create another dashboard page, which has authentication like before. Create another dashboard page with this structure: <code>app/dashboard/page.tsx</code>, and then add this code to the file:</p>
<pre><code class="lang-typescript"><span class="hljs-string">'use client'</span>;

<span class="hljs-keyword">import</span> { useState, useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> { authClient } <span class="hljs-keyword">from</span> <span class="hljs-string">'@/lib/auth-client'</span>;

<span class="hljs-keyword">type</span> User = {
  id: <span class="hljs-built_in">string</span>;
  email: <span class="hljs-built_in">string</span>;
  emailVerified: <span class="hljs-built_in">boolean</span>;
  name: <span class="hljs-built_in">string</span>;
  createdAt: <span class="hljs-built_in">Date</span>;
  updatedAt: <span class="hljs-built_in">Date</span>;
  image?: <span class="hljs-built_in">string</span> | <span class="hljs-literal">null</span>;
  isAnonymous?: <span class="hljs-built_in">boolean</span> | <span class="hljs-literal">null</span>;
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Dashboard</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [user, setUser] = useState&lt;User | <span class="hljs-literal">null</span>&gt;(<span class="hljs-literal">null</span>);
  <span class="hljs-keyword">const</span> [isLoading, setIsLoading] = useState(<span class="hljs-literal">true</span>);
  <span class="hljs-keyword">const</span> [isSigningIn, setIsSigningIn] = useState(<span class="hljs-literal">false</span>);
  <span class="hljs-keyword">const</span> [error, setError] = useState&lt;<span class="hljs-built_in">string</span> | <span class="hljs-literal">null</span>&gt;(<span class="hljs-literal">null</span>);

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> checkAuth = <span class="hljs-keyword">async</span> () =&gt; {
      <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> session = <span class="hljs-keyword">await</span> authClient.getSession();
        <span class="hljs-keyword">if</span> (session.data?.user) {
          setUser(session.data.user);
        }
      } <span class="hljs-keyword">catch</span> (err) {
        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Auth check error:'</span>, err);
      } <span class="hljs-keyword">finally</span> {
        setIsLoading(<span class="hljs-literal">false</span>);
      }
    };

    checkAuth();
  }, []);

  <span class="hljs-keyword">const</span> handleAnonymousSignIn = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">try</span> {
      setIsSigningIn(<span class="hljs-literal">true</span>);
      setError(<span class="hljs-literal">null</span>);

      <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> authClient.signIn.anonymous();

      <span class="hljs-keyword">if</span> (result.data) {
        setUser(result.data.user);
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Anonymous user signed in:'</span>, result.data.user);
      } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (result.error) {
        setError(result.error.message || <span class="hljs-string">'Failed to sign in anonymously'</span>);
      }
    } <span class="hljs-keyword">catch</span> (err) {
      setError(<span class="hljs-string">'An unexpected error occurred'</span>);
      <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Anonymous sign-in error:'</span>, err);
    } <span class="hljs-keyword">finally</span> {
      setIsSigningIn(<span class="hljs-literal">false</span>);
    }
  };

  <span class="hljs-keyword">const</span> handleSignOut = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">await</span> authClient.signOut();
      setUser(<span class="hljs-literal">null</span>);
    } <span class="hljs-keyword">catch</span> (err) {
      <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Sign out error:'</span>, err);
    }
  };

  <span class="hljs-keyword">if</span> (isLoading) {
    <span class="hljs-keyword">return</span> (
      &lt;div className=<span class="hljs-string">"max-w-4xl mx-auto p-6"</span>&gt;
        &lt;div className=<span class="hljs-string">"flex items-center justify-center min-h-[400px]"</span>&gt;
          &lt;div className=<span class="hljs-string">"text-center"</span>&gt;
            &lt;div className=<span class="hljs-string">"animate-spin rounded-full h-12 w-12 border-b-2 border-blue-500 mx-auto mb-4"</span>&gt;&lt;/div&gt;
            &lt;p className=<span class="hljs-string">"text-gray-600"</span>&gt;Checking authentication...&lt;/p&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    );
  }

  <span class="hljs-keyword">if</span> (!user) {
    <span class="hljs-keyword">return</span> (
      &lt;div className=<span class="hljs-string">"max-w-4xl mx-auto p-6"</span>&gt;
        &lt;div className=<span class="hljs-string">"text-center"</span>&gt;
          &lt;h1 className=<span class="hljs-string">"text-3xl font-bold mb-6"</span>&gt;Access Required&lt;/h1&gt;
          &lt;p className=<span class="hljs-string">"text-gray-600 mb-8"</span>&gt;
            You need to be signed <span class="hljs-keyword">in</span> to access our dashboard. Choose an option
            below to <span class="hljs-keyword">continue</span>.
          &lt;/p&gt;

          {error &amp;&amp; (
            &lt;div className=<span class="hljs-string">"bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-6 max-w-md mx-auto"</span>&gt;
              {error}
            &lt;/div&gt;
          )}

          &lt;div className=<span class="hljs-string">"bg-gray-50 p-8 rounded-lg border max-w-md mx-auto"</span>&gt;
            &lt;h2 className=<span class="hljs-string">"text-xl font-semibold mb-4 text-black"</span>&gt;
              Sign In Options
            &lt;/h2&gt;

            &lt;button
              onClick={handleAnonymousSignIn}
              disabled={isSigningIn}
              className=<span class="hljs-string">"w-full bg-blue-500 hover:bg-blue-600 disabled:bg-blue-300 text-white px-6 py-3 rounded font-medium mb-4"</span>
            &gt;
              {isSigningIn ? <span class="hljs-string">'Signing in...'</span> : <span class="hljs-string">'Sign In Anonymously'</span>}
            &lt;/button&gt;

            &lt;p className=<span class="hljs-string">"text-sm text-gray-500 mb-4"</span>&gt;
              Anonymous access allows you to use our dashboard without creating
              an account. You can always link your account later.
            &lt;/p&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
    );
  }

  <span class="hljs-keyword">return</span> (
    &lt;div className=<span class="hljs-string">"max-w-4xl mx-auto p-6"</span>&gt;
      &lt;div className=<span class="hljs-string">"bg-green-50 border border-green-200 rounded-lg p-4 mb-6"</span>&gt;
        &lt;div className=<span class="hljs-string">"flex items-center justify-between"</span>&gt;
          &lt;div&gt;
            &lt;h2 className=<span class="hljs-string">"text-lg font-semibold text-green-800"</span>&gt;
              Welcome, {user.isAnonymous ? <span class="hljs-string">'Anonymous User'</span> : user.name}!
            &lt;/h2&gt;
            &lt;p className=<span class="hljs-string">"text-sm text-green-600"</span>&gt;
              {user.isAnonymous
                ? <span class="hljs-string">'You are signed in anonymously'</span>
                : <span class="hljs-string">`Signed in as <span class="hljs-subst">${user.email}</span>`</span>}
            &lt;/p&gt;
          &lt;/div&gt;
          &lt;button
            onClick={handleSignOut}
            className=<span class="hljs-string">"bg-red-500 hover:bg-red-600 text-white px-4 py-2 rounded text-sm"</span>
          &gt;
            Sign Out
          &lt;/button&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;h1 className=<span class="hljs-string">"text-3xl font-bold mb-6"</span>&gt;Dashboard&lt;/h1&gt;

      {process.env.NODE_ENV === <span class="hljs-string">'development'</span> &amp;&amp; (
        &lt;div className=<span class="hljs-string">"mt-8 bg-gray-100 p-4 rounded-lg"</span>&gt;
          &lt;h3 className=<span class="hljs-string">"font-semibold mb-2 text-black"</span>&gt;Debug Info:&lt;/h3&gt;
          &lt;pre className=<span class="hljs-string">"text-xs text-gray-600 overflow-auto"</span>&gt;
            {<span class="hljs-built_in">JSON</span>.stringify(user, <span class="hljs-literal">null</span>, <span class="hljs-number">2</span>)}
          &lt;/pre&gt;
        &lt;/div&gt;
      )}
    &lt;/div&gt;
  );
}
</code></pre>
<p>This code creates a dashboard page which requires a user to be authenticated and signed in to use it. After a user signs in anonymously, they can view some debug info about their profile. So basically, this page is an authentication flow for our dashboard view which integrates with our custom <code>authClient</code>. This page also handles loading error state and sign out for anonymous users.</p>
<p>Ok, now we probably need to reset our Prisma database again, or we could get schema and table errors.</p>
<p>First, make sure that your Prisma development database is running with this command:</p>
<pre><code class="lang-shell">npx prisma dev
</code></pre>
<p>And then run these commands to reset the database and apply the new migrations for the schema:</p>
<pre><code class="lang-shell">npx prisma migrate reset
npx prisma migrate dev
</code></pre>
<p>You might need to restart the Prisma development server and then run it with the command <code>npx prisma dev</code>.</p>
<p>Our Better Auth app needs two servers to be running.</p>
<ol>
<li><p>The Prisma development server</p>
</li>
<li><p>The Next.js application</p>
</li>
</ol>
<p>With the Prisma development server running, you can now start the Next.js app with the usual command <code>npm run dev</code>.</p>
<p>If you encounter any problems with the Prisma ORM or database, like tables missing or schema mismatches, then here are some useful commands which could hopefully resolve them.</p>
<pre><code class="lang-shell"># ⚠️ WARNING: This will drop the database, recreate it, and apply all migrations from scratch.
npx prisma migrate reset

# Applies any new migrations that haven’t been run yet (or creates a new one if your schema changed).
npx prisma migrate dev

# Starts Prisma Studio (a GUI for exploring and editing your database).
npx prisma dev

# Runs Better Auth CLI migrations (sets up any database tables/changes required for authentication).
npx @better-auth/cli migrate
</code></pre>
<p>These commands are self-explanatory and let us run migrations on our database when there are changes, and we can see our database inside of Prisma Studio.</p>
<p>This is what the dashboard page looks like when a user is not signed in:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1757429500439/9c0846fc-cfc5-4232-9a02-4e313c2c4698.png" alt="Better Auth app Dashboard page" class="image--center mx-auto" width="1310" height="792" loading="lazy"></p>
<p>Signing in will show the dashboard screen. To learn more about Better Auth, you can read their <a target="_blank" href="https://www.better-auth.com/">official documentation</a>.</p>
<h2 id="heading-when-to-use-each-library">When To Use Each Library</h2>
<p>Each of these authentication libraries have their pros and cons, and can suit various needs depending on your project. Knowing when to use each one can better prepare you for real-world conditions and when building for production, so let's go through them and see how they compare.</p>
<h3 id="heading-when-to-use-clerk">When to Use Clerk</h3>
<p>Clerk excels when you need to use auth quickly without worrying about managing multiple servers, and when you need to have pre-made interfaces and management systems. It's great for startups and teams that want to prioritize developer experience and fast implementation.</p>
<p>It has a streamlined and user-friendly setup, which is still able to support the bare minimum essentials, so it's a really good option for small teams and projects that must have an easy implementation. If you are building a SaaS that needs a good interface and components from the start, or if you require social logins without a ton of code, then it's a very good solution, especially if you want speed and cost effectiveness in a smaller project.</p>
<h3 id="heading-when-to-use-kinde">When to Use Kinde</h3>
<p>Kinde is a fantastic choice when you are keen on having transparent pricing and quick auth integration in more frameworks. Kinde has been designed to be a cost-effective alternative to Clerk and offers more transparent pricing and a more generous, free tier.</p>
<p>It's great for teams that need to have a reliable authentication option but want lower costs and better framework support. Kinde is effective when used in medium-sized projects that need more than a basic authentication system, but also don't have the need to have enterprise-grade tooling.</p>
<h3 id="heading-when-to-use-better-auth">When to Use Better Auth</h3>
<p>Better Auth is a great solution when you have a need for an expansive set of features out of the box. It is also worth noting that Better Auth has a plugin ecosystem that can simplify adding more advanced functionalities with only a few lines of code. Of all the options discussed in this article, Better Auth is by far the cleanest; however, it requires more coding knowledge and skills.</p>
<p>It's a good option if you are building a TypeScript application and want to have full control over the customization and auth data flows. The framework is agnostic and has features such as 2FA, multi-tenant support and other complex features so developers can get the best out of the tool, as there is no vendor lock-in. Functionality can easily be expanded with the plugin ecosystem, so developers can tailor it to their needs.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>All three platforms are fairly good at their job, and I do not doubt that they are going to remain popular options for adding authentication to our applications. Auth.js is one of the most well-known out there; however, Clerk, Kinde and Better Auth also appear to have growing followings, and judging by conversations on socials, they appear to be the first choice for many developers at the moment.</p>
<p>After experiencing what it's like to set them up for the first time, I would have to say that Clerk is the easiest to set up because you don't have to create an account, and you can get the authentication working fairly quickly with little troubleshooting. Kinde would be the second easiest to set up, in my opinion. You do have to register for an account to use the platform; however, the setup was also pretty easy and did not need any troubleshooting.</p>
<p>Better Auth is a great platform, but the setup requires a bit more work because a database is required for storing users' information, which makes the process slightly more difficult. I also found it easier to create authenticated routes with the other two auth options. However, the fact that the platform is open-source with no vendor lock-in works in its favour because developers can self-host and it's completely free, which means no paid plans.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Team of AI Agents for Your Website for Free Using Agno and Groq ]]>
                </title>
                <description>
                    <![CDATA[ AI is quickly changing the way we work, and more and more companies are using it to help them get and retain clients. Teams are also using AI to create innovative and responsive websites capable of engaging visitors while also providing helpful infor... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/build-a-team-of-ai-agents-for-your-website-for-free/</link>
                <guid isPermaLink="false">67eb1b3398e2cf5154940366</guid>
                
                    <category>
                        <![CDATA[ JavaScript ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ react js ]]>
                    </category>
                
                    <category>
                        <![CDATA[ programming ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Python ]]>
                    </category>
                
                    <category>
                        <![CDATA[ AI ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Artificial Intelligence ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Andrew Baisden ]]>
                </dc:creator>
                <pubDate>Mon, 31 Mar 2025 22:46:11 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1742397437476/0ffa13b0-c668-40d7-864f-596f523f6101.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>AI is quickly changing the way we work, and more and more companies are using it to help them get and retain clients. Teams are also using AI to create innovative and responsive websites capable of engaging visitors while also providing helpful information.</p>
<p>AI agents are powerful tools for customer services. Having them power your platforms and websites might sound like an expensive proposition with high technical expertise required. But with the emergence of new modern platforms like Agno and Groq, it’s now easier to integrate an AI agent system into your website while still staying within budget.</p>
<p>In this article, you’ll go through the process of developing your own AI agent ecosystem (for free). This will enable you to have a website that can handle customer enquiries, create content, analyse a user's behaviour, and provide custom personal experiences for each user. It's a fantastic setup because you can automate part of your business, speeding up lead generation and freeing up your time to work on more high-priority tasks.</p>
<p>This article is for developers who are familiar with JavaScript, React, and Python. Even if you don’t have a complete understanding of all three, as long as you are a beginner or junior with some knowledge, you should be able to understand at least some of the code. For example, JavaScript and Python are pretty similar syntax-wise, so if you have experience with either of them, then just reading through the codebase will give you an idea of how everything works.</p>
<p>For this tutorial, we’ll build a portfolio website. But you can use the ideas and concepts you learn here for any type of website, regardless of whether you are a solo entrepreneur or part of a large company. With these tools and frameworks, it's possible to transform your web presence without going over budget.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ul>
<li><p><a class="post-section-overview" href="#prerequisites">Prerequisites</a></p>
</li>
<li><p><a class="post-section-overview" href="#what-are-ai-agents">What Are AI Agents?</a></p>
</li>
<li><p><a class="post-section-overview" href="#what-are-agno-and-groq-cloud">What Are Agno and Groq Cloud?</a></p>
</li>
<li><p><a class="post-section-overview" href="#what-you-will-be-building">What You Will Be Building</a></p>
</li>
<li><p><a class="post-section-overview" href="#building-our-python-backend">Building Our Python Backend</a></p>
<ul>
<li><p><a class="post-section-overview" href="#creating-an-account-on-groq-cloud">Creating an Account on Groq Cloud</a></p>
</li>
<li><p><a class="post-section-overview" href="#setting-up-our-python-project">Setting Up Our Python Project</a></p>
</li>
<li><p><a class="post-section-overview" href="#working-on-the-python-codebase">Working on the Python Codebase</a></p>
</li>
<li><p><a class="post-section-overview" href="#running-our-python-backend">Running Our Python Backend</a></p>
</li>
</ul>
</li>
<li><p><a class="post-section-overview" href="#building-our-react-frontend">Building Our React Frontend</a></p>
</li>
<li><p><a class="post-section-overview" href="#conclusion">Conclusion</a></p>
</li>
<li><p><a class="post-section-overview" href="#stay-up-to-date-with-tech-programming-productivity-and-ai">Stay Up to Date with Tech, Programming, Productivity, and AI</a></p>
</li>
</ul>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li><p>Prior knowledge of JavaScript, React, and Python</p>
</li>
<li><p><a target="_blank" href="https://www.python.org/">Python</a> installed and setup locally on your computer</p>
</li>
<li><p>An account on <a target="_blank" href="https://groq.com/">Groq Cloud</a></p>
</li>
<li><p>A code editor/IDE installed like <a target="_blank" href="https://www.cursor.com/en">Cursor</a>, <a target="_blank" href="https://codeium.com/windsurf">Windsurf</a>, or <a target="_blank" href="https://code.visualstudio.com/">VS Code</a></p>
</li>
</ul>
<h2 id="heading-what-are-ai-agents">What Are AI Agents?</h2>
<p>AI agents are computer systems or programs that are designed to use artificial intelligence to interact with their world and achieve certain objectives. They are able to perceive their world – through sensors, user input, or data – and act to achieve goals, typically with some degree of autonomy. This means that they will decide things for themselves, sometimes with little to no human intervention, depending on how they were designed.</p>
<h2 id="heading-what-are-agno-and-groq-cloud">What are Agno and Groq Cloud?</h2>
<p>Agno is a lightweight library that lets you build Multimodal Agents. It’s an AI inference engine designed to optimise LLMs for speed and performance. This means it can provide super-fast AI model inference with reduced latency and improved resource utilisation. It has the potential to replace current inference platforms like NVIDIA TensorRT or Hugging Face's Text Generation Inference (TGI).</p>
<p>Groq Cloud is a cloud-based AI inference platform based on Groq LPU (Language Processing Unit) chips, which are optimised for ultra-low-latency AI workloads. Groq is great at high-speed token generation rates, making it perfect for real-time AI applications like chatbots, AI coding help, and other latency-sensitive workloads. The Groq Cloud platform offers free access to its large language models (LLMs) through a free tier, but there are some usage limits.</p>
<p>If you go to the <a target="_blank" href="https://console.groq.com/playground">Groq Cloud Playground</a> you can find LLM models from different companies like:</p>
<ul>
<li><p>Qwen</p>
</li>
<li><p>DeepSeek R1</p>
</li>
<li><p>Google Gemma 2</p>
</li>
<li><p>Hugging Face</p>
</li>
<li><p>Meta llama</p>
</li>
<li><p>Mistral AI</p>
</li>
<li><p>OpenAI</p>
</li>
</ul>
<p>This is great because Groq Cloud gives us the flexibility to choose from any of these AI LLM models for our AI agent application. Agno basically acts as the orchestration layer for multiple AI agents. In our case, that would be WelcomeAgent, ProjectAgent, CareerAgent, BusinessAdvisor and ResearchAgent.</p>
<p>The platform is able to manage their conversations, task delegation, and memory. When any of our AI agents need to reason or generate output, Agno then uses Groq Cloud, which can run large language models (LLMs), and it does this with ultra-low latency. The advantage to this is that it ensures that it has fast and efficient responses. Groq Cloud itself is not an LLM – rather, it is a high-performance inference engine which hosts and serves LLMs from lots of different providers.</p>
<p>For this project, we will use Meta’s LLaMA 3 model because it strikes a strong balance between performance and accuracy and is openly accessible. This means that it is well-suited for the AI agents in our portfolio website.</p>
<p>It's worth mentioning that we could have used the LLaMA model from <a target="_blank" href="http://llama.com">llama.com</a>. Still, instead we will use it via Groq Cloud, because, this way, we get better optimisation, more capabilities, and better-quality responses for each AI agent. This is because Groq Cloud gives us the flexibility to test and choose between different AI models if we wish to do so, and that means that we can get the best one for our needs.</p>
<h2 id="heading-what-you-will-be-building">What You Will Be Building</h2>
<p>Today, you will be building a portfolio website that incorporates AI agents with which anyone can interact. These AI agents are like customer service representatives because anyone can ask them questions about your skills and portfolio, and they will provide the person with information.</p>
<p>This is great because it means that potential clients can learn anything about you 24/7 without having actually to talk to you when you are unavailable. You could even use this portfolio as a template for building your portfolio website or as inspiration for creating one.</p>
<p>In total, there will be five AI agents on your portfolio website:</p>
<ul>
<li><p>WelcomeAgent: a specialist in helping users navigate the website, whether the user is an employer, client, or fellow programmer</p>
</li>
<li><p>ProjectAgent: a project specialist that can provide information about projects, technology, and challenges</p>
</li>
<li><p>CareerAgent: a career specialist that can provide information about skills, experience, and professional background</p>
</li>
<li><p>BusinessAdvisor: a client specialist that can provide information about services, pricing, and project details</p>
</li>
<li><p>ResearchAgent: a research specialist that can provide information about technology, trends, and industry news</p>
</li>
</ul>
<p>The massive benefit of incorporating AI agents into a portfolio website is that they can create a personalised experience by providing an interactive experience which is tailor-made and not as easily replicated on other, more generic websites.</p>
<p>This can set your website apart because, as opposed to having a static website for showcasing your talent, an AI agent is capable of guiding visitors, answering queries about your projects, and recommending relevant work based on an interest.</p>
<p>Another great feature is the ability to simulate a conversation, which can make the portfolio feel more dynamic, engaging, and immersive while also demonstrating how good you are at working with modern tooling.</p>
<p>All of this combined provides you with a practical and approachable way to explore AI agents. This can be a real-world example and a personal project that does not require the implementation of a full-scale business application to see how valuable this type of concept can be.</p>
<p>The website will have the following six pages:</p>
<ul>
<li><p>Home – the main webpage</p>
</li>
<li><p>Projects – showcasing some featured projects and technical skills</p>
</li>
<li><p>Career – showing skills, experience, education, and certifications</p>
</li>
<li><p>Services – client services and the engagement process</p>
</li>
<li><p>Research – a way to search the web regarding the tech industry</p>
</li>
<li><p>Contact – a page with a form to contact the user</p>
</li>
</ul>
<p>You can see what your frontend React application will look like below:</p>
<p>First, you have your portfolio homepage:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741977313487/4ac8fd65-4d3a-4da1-80b8-4b4ff5136e7e.png" alt="AI Portfolio Home Page" class="image--center mx-auto" width="2538" height="2668" loading="lazy"></p>
<p>Next is your Projects page:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741977426609/1c05544d-5255-40c2-85da-d072c8ecd6fc.png" alt="AI Portfolio Projects Page" class="image--center mx-auto" width="2492" height="2656" loading="lazy"></p>
<p>Now you have your Career page:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741977482985/ce61c17e-d948-49b5-83fa-7a77556796b5.png" alt="AI Portfolio Career Page" class="image--center mx-auto" width="2478" height="2664" loading="lazy"></p>
<p>Then you have the Services page:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741977517562/45614042-68b1-466a-9c43-b5f6aa5fde26.png" alt="AI Portfolio Services Page" class="image--center mx-auto" width="2488" height="2666" loading="lazy"></p>
<p>Then you can see your Research and Insights page:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741977558018/c2c083be-bd9a-4fac-9713-ff6c895d0cb0.png" alt="AI Portfolio Research &amp; Insights Page" class="image--center mx-auto" width="2512" height="2664" loading="lazy"></p>
<p>Lastly, you have your Contact page:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741977630020/3c73726a-7de2-46af-a474-ce03fa3ace7b.png" alt="AI Contact Me Page" class="image--center mx-auto" width="2498" height="2654" loading="lazy"></p>
<p>Now, let's begin building your application, starting with the Python Backend.</p>
<h2 id="heading-building-our-python-backend">Building Our Python Backend</h2>
<p>For this tutorial I will be using macOS, and the commands should also work on Linux. If you’re a Windows user, most of the commands should work (although there are some differences like activating a Python environment). You can find the correct commands by searching if need be – and you’ll know if your terminal gives you errors when trying to run a command.</p>
<h3 id="heading-creating-an-account-on-groq-cloud">Creating An Account On Groq Cloud</h3>
<p>As mentioned earlier, we will use Meta’s LLaMA 3 via Groq Cloud, which is ideal. So, first, we have to create an account on <a target="_blank" href="https://console.groq.com/login">Groq Cloud</a>, as shown here.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741977123301/80f8a1a6-de52-4a3d-870a-25c1067c13eb.png" alt="Creating an account on Groq Cloud" class="image--center mx-auto" width="2052" height="1350" loading="lazy"></p>
<p>Once you have created an account on Groq Cloud, go to the API Keys page and create an API Key as shown in this example. I gave mine the name <code>team-ai-agents</code>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741977205655/7c7dcc3e-685b-4383-b80a-4d8088be7d2d.png" alt="Creating a Groq Cloud API Key" class="image--center mx-auto" width="1498" height="321" loading="lazy"></p>
<p>You should have an API Key that looks like this example, so make sure that you save it somewhere safe – we will need it later.</p>
<pre><code class="lang-shell">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
</code></pre>
<h3 id="heading-setting-up-our-python-project">Setting Up Our Python Project</h3>
<p>Ok, now let's quickly set up our project. Navigate to a location on your computer, like the desktop, and create a folder called <code>ai-agent-app</code>. <code>cd</code> into the project folder and get ready – we’re going to start building our backend using Python.</p>
<p>I recommend installing <code>agno</code> and <code>groq</code> locally in a Python virtual environment. First, use this terminal command to setup a Python virtual environment inside of your <code>ai-agent-app</code> folder:</p>
<pre><code class="lang-shell">python3 -m venv venv
source venv/bin/activate
cd venv
</code></pre>
<p>Note: depending on your local Python environment, you might need to use either the <code>python</code> or <code>python3</code> command for running Python commands. In my environment and examples, I use <code>python3</code>, so adjust the command to suit your needs.</p>
<p>The same applies when using either <code>pip</code> or <code>pip3</code> when installing Python packages. You can check to see which version of Python and pip you have installed with the <code>python --version</code>, <code>python3 --version</code> , <code>pip --version</code> and <code>pip3 --version</code> commands in your terminal window.</p>
<p>The above command should create a <code>venv</code> folder inside of your <code>ai-agent-app</code> folder. This will be your REST backend with all of your API endpoints which your React frontend will use later on in this tutorial. Your Python virtual environment has also been activated.</p>
<p>To activate and deactivate your Python environment, you can use these commands:</p>
<pre><code class="lang-shell"># Activate on macOS/Linux
source venv/bin/activate

# Activate on Windows
venv\Scripts\activate

# Deactivate works on all platforms
conda deactivate
</code></pre>
<p>At this point, its a good idea to open the project in your code editor. Now you’ll need to install <code>agno</code> and <code>groq</code> using <code>pip</code> alongside a few other packages: <code>flask</code>, <code>requests</code>, and <code>python-dotenv</code>. You need these packages for setting up your server environment, so go ahead and install them all with this command:</p>
<pre><code class="lang-shell">pip install agno
pip install groq
pip install flask
pip install flask_cors
pip install requests
pip install python-dotenv
</code></pre>
<p>With these Python packages installed, you’re now ready to set up your API for this project. We’ll be using the Python web application framework Flask, along with the CORS package so that we can access the server anywhere. At the same time, we’ll also use the requests module, which allows us to send HTTP requests using Python.</p>
<p>Note that you’ll also need a <code>.env</code> file for your API keys, so make sure you have installed the <code>python-dotenv</code> package in your Python environment, although in some cases, it's installed automatically.</p>
<h3 id="heading-working-on-the-python-codebase">Working On The Python Codebase</h3>
<p>Alright, time to make a start on the codebase. But first, let's generate all of the files for your project. You can do this simply by running the run script I created for the project. Run this command in the <code>venv</code> folder:</p>
<pre><code class="lang-shell">mkdir agents
touch .env main.py
cd agents
touch __init__.py base_agent.py career_agent.py client_agent.py project_agent.py research_agent.py welcome_agent.py
</code></pre>
<p>With this script, you should now have:</p>
<ul>
<li><p>Created a <code>.env</code> file for your API Keys</p>
</li>
<li><p>Created an agents folder with all of the files for creating your different AI agents</p>
</li>
<li><p>Created a <code>main.py</code> file, which will be the main project file for your entire backend app</p>
</li>
</ul>
<p>Ok, your files are set. All that’s left is to add the codebase, and the backend is complete. Let's start with the <code>.env</code> file, as it only needs one line of code and that is for your API key. See my example and update it with your own API Key:</p>
<pre><code class="lang-shell">GROQ_API_KEY="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"
</code></pre>
<p>Your application now has an API key, which gives you access to Groq Cloud. Now let’s start to add the code for all the various AI agents. The first file we’ll work on will be the <code>__init__.py</code> which holds the imports for all of the AI agent files.</p>
<p>Add this code to the file:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> agents.welcome_agent <span class="hljs-keyword">import</span> WelcomeAgent
<span class="hljs-keyword">from</span> agents.project_agent <span class="hljs-keyword">import</span> ProjectAgent
<span class="hljs-keyword">from</span> agents.career_agent <span class="hljs-keyword">import</span> CareerAgent
<span class="hljs-keyword">from</span> agents.client_agent <span class="hljs-keyword">import</span> ClientAgent
<span class="hljs-keyword">from</span> agents.research_agent <span class="hljs-keyword">import</span> ResearchAgent

<span class="hljs-comment"># Export all agents</span>
__all__ = [<span class="hljs-string">'WelcomeAgent'</span>, <span class="hljs-string">'ProjectAgent'</span>, <span class="hljs-string">'CareerAgent'</span>, <span class="hljs-string">'ClientAgent'</span>, <span class="hljs-string">'ResearchAgent'</span>]
</code></pre>
<p>As you can see, all of the classes for the AI agents will be imported and exported from here so you can use them in your <code>main.py</code> file later.</p>
<p>Ok, good. Now, we have 6 AI agent files to work on, beginning with the <code>base_agent.py</code> file.</p>
<p>Make sure that you add this code to the file:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> agno.agent <span class="hljs-keyword">import</span> Agent
<span class="hljs-keyword">from</span> agno.models.groq <span class="hljs-keyword">import</span> Groq
<span class="hljs-keyword">import</span> os


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BaseAgent</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, name, description, avatar=<span class="hljs-string">"default_avatar.png"</span></span>):</span>

        self.name = name
        self.description = description
        self.avatar = avatar
        self.model = Groq(id=<span class="hljs-string">"llama-3.3-70b-versatile"</span>)
        self.agent = Agent(model=self.model, markdown=<span class="hljs-literal">True</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_response</span>(<span class="hljs-params">self, query, stream=False</span>):</span>

        <span class="hljs-keyword">return</span> self.agent.get_response(query, stream=stream)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">print_response</span>(<span class="hljs-params">self, query, stream=True</span>):</span>

        <span class="hljs-keyword">return</span> self.agent.print_response(query, stream=stream)
</code></pre>
<p>This class uses the <code>agno</code> framework to create AI agents powered by Groq's LLama 3.3 70B model, which is free to use with some usage restrictions for API calls. This should be fine for your project. It provides the basic structure that other specialised agents in the application can inherit from and extend with domain-specific functionality.</p>
<p>The model we chose is available on the Groq Cloud platform, and we can change it if we want to. Each model has pros and cons, and a cut-off date for how up-to-date it is, so you can expect to get different results. Just keep in mind that using an up to date LLM like OpenAI will provide better results, but you might have to pay for it.</p>
<p>The next file we will work on will be the <code>career_agent.py</code> file.</p>
<p>And this is this code required for it:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> agents.base_agent <span class="hljs-keyword">import</span> BaseAgent


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CareerAgent</span>(<span class="hljs-params">BaseAgent</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        super().__init__(
            name=<span class="hljs-string">"CareerGuide"</span>,
            description=<span class="hljs-string">"I'm the career specialist. I can provide information about skills, experience, and job suitability."</span>,
            avatar=<span class="hljs-string">"career_avatar.png"</span>
        )

        self.skills = {
            <span class="hljs-string">"languages"</span>: [<span class="hljs-string">"Python"</span>, <span class="hljs-string">"JavaScript"</span>, <span class="hljs-string">"TypeScript"</span>, <span class="hljs-string">"Java"</span>, <span class="hljs-string">"SQL"</span>],
            <span class="hljs-string">"frameworks"</span>: [<span class="hljs-string">"React"</span>, <span class="hljs-string">"Vue.js"</span>, <span class="hljs-string">"Node.js"</span>, <span class="hljs-string">"Django"</span>, <span class="hljs-string">"Flask"</span>, <span class="hljs-string">"Spring Boot"</span>],
            <span class="hljs-string">"tools"</span>: [<span class="hljs-string">"Git"</span>, <span class="hljs-string">"Docker"</span>, <span class="hljs-string">"AWS"</span>, <span class="hljs-string">"Azure"</span>, <span class="hljs-string">"CI/CD"</span>, <span class="hljs-string">"Kubernetes"</span>],
            <span class="hljs-string">"soft_skills"</span>: [<span class="hljs-string">"Team leadership"</span>, <span class="hljs-string">"Project management"</span>, <span class="hljs-string">"Agile methodologies"</span>, <span class="hljs-string">"Technical writing"</span>, <span class="hljs-string">"Client communication"</span>]
        }

        self.experience = [
            {
                <span class="hljs-string">"title"</span>: <span class="hljs-string">"Senior Full Stack Developer"</span>,
                <span class="hljs-string">"company"</span>: <span class="hljs-string">"Tech Innovations Inc."</span>,
                <span class="hljs-string">"period"</span>: <span class="hljs-string">"2020-Present"</span>,
                <span class="hljs-string">"responsibilities"</span>: [
                    <span class="hljs-string">"Led development of cloud-based SaaS platform"</span>,
                    <span class="hljs-string">"Managed team of 5 developers"</span>,
                    <span class="hljs-string">"Implemented CI/CD pipeline reducing deployment time by 40%"</span>,
                    <span class="hljs-string">"Architected microservices infrastructure"</span>
                ]
            },
            {
                <span class="hljs-string">"title"</span>: <span class="hljs-string">"Full Stack Developer"</span>,
                <span class="hljs-string">"company"</span>: <span class="hljs-string">"WebSolutions Co."</span>,
                <span class="hljs-string">"period"</span>: <span class="hljs-string">"2017-2020"</span>,
                <span class="hljs-string">"responsibilities"</span>: [
                    <span class="hljs-string">"Developed responsive web applications using React and Node.js"</span>,
                    <span class="hljs-string">"Implemented RESTful APIs and database schemas"</span>,
                    <span class="hljs-string">"Collaborated with UX/UI designers to implement user-friendly interfaces"</span>,
                    <span class="hljs-string">"Participated in code reviews and mentored junior developers"</span>
                ]
            },
            {
                <span class="hljs-string">"title"</span>: <span class="hljs-string">"Junior Developer"</span>,
                <span class="hljs-string">"company"</span>: <span class="hljs-string">"StartUp Labs"</span>,
                <span class="hljs-string">"period"</span>: <span class="hljs-string">"2015-2017"</span>,
                <span class="hljs-string">"responsibilities"</span>: [
                    <span class="hljs-string">"Built and maintained client websites"</span>,
                    <span class="hljs-string">"Developed custom WordPress plugins"</span>,
                    <span class="hljs-string">"Implemented responsive designs and cross-browser compatibility"</span>,
                    <span class="hljs-string">"Assisted in database design and optimization"</span>
                ]
            }
        ]

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_skills_summary</span>(<span class="hljs-params">self</span>):</span>

        prompt = <span class="hljs-string">f"""
        Generate a professional summary of the following skills for a portfolio website:

        Programming Languages: <span class="hljs-subst">{<span class="hljs-string">', '</span>.join(self.skills[<span class="hljs-string">'languages'</span>])}</span>
        Frameworks &amp; Libraries: <span class="hljs-subst">{<span class="hljs-string">', '</span>.join(self.skills[<span class="hljs-string">'frameworks'</span>])}</span>
        Tools &amp; Platforms: <span class="hljs-subst">{<span class="hljs-string">', '</span>.join(self.skills[<span class="hljs-string">'tools'</span>])}</span>
        Soft Skills: <span class="hljs-subst">{<span class="hljs-string">', '</span>.join(self.skills[<span class="hljs-string">'soft_skills'</span>])}</span>

        Format the response in markdown with appropriate sections and highlights.
        """</span>
        <span class="hljs-keyword">return</span> self.get_response(prompt)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_experience_summary</span>(<span class="hljs-params">self</span>):</span>

        experience_text = <span class="hljs-string">"# Work Experience\n\n"</span>
        <span class="hljs-keyword">for</span> job <span class="hljs-keyword">in</span> self.experience:
            experience_text += <span class="hljs-string">f"## <span class="hljs-subst">{job[<span class="hljs-string">'title'</span>]}</span> at <span class="hljs-subst">{job[<span class="hljs-string">'company'</span>]}</span>\n"</span>
            experience_text += <span class="hljs-string">f"**<span class="hljs-subst">{job[<span class="hljs-string">'period'</span>]}</span>**\n\n"</span>
            experience_text += <span class="hljs-string">"**Responsibilities:**\n"</span>
            <span class="hljs-keyword">for</span> resp <span class="hljs-keyword">in</span> job[<span class="hljs-string">'responsibilities'</span>]:
                experience_text += <span class="hljs-string">f"- <span class="hljs-subst">{resp}</span>\n"</span>
            experience_text += <span class="hljs-string">"\n"</span>

        prompt = <span class="hljs-string">f"""
        Based on the following work experience, generate a professional career summary for a portfolio website:

        <span class="hljs-subst">{experience_text}</span>

        Highlight career progression, key achievements, and growth. Format the response in markdown.
        """</span>
        <span class="hljs-keyword">return</span> self.get_response(prompt)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">assess_job_fit</span>(<span class="hljs-params">self, job_description</span>):</span>

        skills_flat = []
        <span class="hljs-keyword">for</span> skill_category <span class="hljs-keyword">in</span> self.skills.values():
            skills_flat.extend(skill_category)

        experience_flat = []
        <span class="hljs-keyword">for</span> job <span class="hljs-keyword">in</span> self.experience:
            experience_flat.extend(job[<span class="hljs-string">'responsibilities'</span>])

        prompt = <span class="hljs-string">f"""
        Assess the fit for the following job description based on the skills and experience provided:

        Job Description:
        <span class="hljs-subst">{job_description}</span>

        Skills:
        <span class="hljs-subst">{<span class="hljs-string">', '</span>.join(skills_flat)}</span>

        Experience:
        <span class="hljs-subst">{<span class="hljs-string">' '</span>.join(experience_flat)}</span>

        Provide an analysis of strengths, potential gaps, and overall suitability for the role. Format the response in markdown.
        """</span>
        <span class="hljs-keyword">return</span> self.get_response(prompt)
</code></pre>
<p>This agent is designed to help users with career-related tasks such as:</p>
<ul>
<li><p>Creating professional summaries of technical and soft skills</p>
</li>
<li><p>Generating career narratives based on work experience</p>
</li>
<li><p>Evaluating job fit by comparing skills and experience against job descriptions</p>
</li>
</ul>
<p>The agent uses the LLM capabilities of the base agent (using Groq's LLama 3.3 70B model) to generate natural language responses that are formatted in markdown, making them suitable for inclusion in portfolio websites, résumés, or job applications. This file has sample career data, and in a real implementation, this would come from a database</p>
<p>Ok time for the next AI agent – this time it’s <code>client_agent.py</code>, which receives this code:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> agents.base_agent <span class="hljs-keyword">import</span> BaseAgent


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ClientAgent</span>(<span class="hljs-params">BaseAgent</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        super().__init__(
            name=<span class="hljs-string">"BusinessAdvisor"</span>,
            description=<span class="hljs-string">"I'm the client specialist. I can provide information about services, pricing, and project details."</span>,
            avatar=<span class="hljs-string">"client_avatar.png"</span>
        )

        self.services = {
            <span class="hljs-string">"web_development"</span>: {
                <span class="hljs-string">"name"</span>: <span class="hljs-string">"Web Development"</span>,
                <span class="hljs-string">"description"</span>: <span class="hljs-string">"Custom web application development using modern frameworks and best practices."</span>,
                <span class="hljs-string">"pricing_model"</span>: <span class="hljs-string">"Project-based or hourly"</span>,
                <span class="hljs-string">"price_range"</span>: <span class="hljs-string">"$5,000 - $50,000 depending on complexity"</span>,
                <span class="hljs-string">"timeline"</span>: <span class="hljs-string">"4-12 weeks depending on scope"</span>,
                <span class="hljs-string">"technologies"</span>: [<span class="hljs-string">"React"</span>, <span class="hljs-string">"Vue.js"</span>, <span class="hljs-string">"Node.js"</span>, <span class="hljs-string">"Django"</span>, <span class="hljs-string">"Flask"</span>]
            },
            <span class="hljs-string">"mobile_development"</span>: {
                <span class="hljs-string">"name"</span>: <span class="hljs-string">"Mobile App Development"</span>,
                <span class="hljs-string">"description"</span>: <span class="hljs-string">"Native and cross-platform mobile application development for iOS and Android."</span>,
                <span class="hljs-string">"pricing_model"</span>: <span class="hljs-string">"Project-based"</span>,
                <span class="hljs-string">"price_range"</span>: <span class="hljs-string">"$8,000 - $60,000 depending on complexity"</span>,
                <span class="hljs-string">"timeline"</span>: <span class="hljs-string">"6-16 weeks depending on scope"</span>,
                <span class="hljs-string">"technologies"</span>: [<span class="hljs-string">"React Native"</span>, <span class="hljs-string">"Flutter"</span>, <span class="hljs-string">"Swift"</span>, <span class="hljs-string">"Kotlin"</span>]
            },
            <span class="hljs-string">"consulting"</span>: {
                <span class="hljs-string">"name"</span>: <span class="hljs-string">"Technical Consulting"</span>,
                <span class="hljs-string">"description"</span>: <span class="hljs-string">"Expert advice on architecture, technology stack, and development practices."</span>,
                <span class="hljs-string">"pricing_model"</span>: <span class="hljs-string">"Hourly"</span>,
                <span class="hljs-string">"price_range"</span>: <span class="hljs-string">"$150 - $250 per hour"</span>,
                <span class="hljs-string">"timeline"</span>: <span class="hljs-string">"Ongoing or as needed"</span>,
                <span class="hljs-string">"technologies"</span>: [<span class="hljs-string">"Various based on client needs"</span>]
            }
        }

        self.process = [
            <span class="hljs-string">"Initial consultation to understand requirements"</span>,
            <span class="hljs-string">"Proposal and quote preparation"</span>,
            <span class="hljs-string">"Contract signing and project kickoff"</span>,
            <span class="hljs-string">"Design and prototyping phase"</span>,
            <span class="hljs-string">"Development sprints with regular client feedback"</span>,
            <span class="hljs-string">"Testing and quality assurance"</span>,
            <span class="hljs-string">"Deployment and launch"</span>,
            <span class="hljs-string">"Post-launch support and maintenance"</span>
        ]

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_services_overview</span>(<span class="hljs-params">self</span>):</span>

        services_text = <span class="hljs-string">"# Services Offered\n\n"</span>
        <span class="hljs-keyword">for</span> service_id, service <span class="hljs-keyword">in</span> self.services.items():
            services_text += <span class="hljs-string">f"## <span class="hljs-subst">{service[<span class="hljs-string">'name'</span>]}</span>\n"</span>
            services_text += <span class="hljs-string">f"<span class="hljs-subst">{service[<span class="hljs-string">'description'</span>]}</span>\n\n"</span>
            services_text += <span class="hljs-string">f"**Pricing Model**: <span class="hljs-subst">{service[<span class="hljs-string">'pricing_model'</span>]}</span>\n"</span>
            services_text += <span class="hljs-string">f"**Price Range**: <span class="hljs-subst">{service[<span class="hljs-string">'price_range'</span>]}</span>\n"</span>
            services_text += <span class="hljs-string">f"**Timeline**: <span class="hljs-subst">{service[<span class="hljs-string">'timeline'</span>]}</span>\n"</span>
            services_text += <span class="hljs-string">f"**Technologies**: <span class="hljs-subst">{<span class="hljs-string">', '</span>.join(service[<span class="hljs-string">'technologies'</span>])}</span>\n\n"</span>

        prompt = <span class="hljs-string">f"""
        Generate a professional overview of the following services for a programmer's portfolio website:

        <span class="hljs-subst">{services_text}</span>

        Format the response in markdown with appropriate sections and highlights.
        """</span>
        <span class="hljs-keyword">return</span> self.get_response(prompt)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_service_details</span>(<span class="hljs-params">self, service_id</span>):</span>

        <span class="hljs-keyword">if</span> service_id <span class="hljs-keyword">in</span> self.services:
            service = self.services[service_id]
            prompt = <span class="hljs-string">f"""
            Generate a detailed description for the following service:

            Service Name: <span class="hljs-subst">{service[<span class="hljs-string">'name'</span>]}</span>
            Description: <span class="hljs-subst">{service[<span class="hljs-string">'description'</span>]}</span>
            Pricing Model: <span class="hljs-subst">{service[<span class="hljs-string">'pricing_model'</span>]}</span>
            Price Range: <span class="hljs-subst">{service[<span class="hljs-string">'price_range'</span>]}</span>
            Timeline: <span class="hljs-subst">{service[<span class="hljs-string">'timeline'</span>]}</span>
            Technologies: <span class="hljs-subst">{<span class="hljs-string">', '</span>.join(service[<span class="hljs-string">'technologies'</span>])}</span>

            Include information about the value proposition, typical deliverables, and client benefits. Format the response in markdown.
            """</span>
            <span class="hljs-keyword">return</span> self.get_response(prompt)
        <span class="hljs-keyword">else</span>:
            <span class="hljs-keyword">return</span> <span class="hljs-string">"Service not found. Please check the service ID and try again."</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">explain_process</span>(<span class="hljs-params">self</span>):</span>

        process_text = <span class="hljs-string">"# Client Engagement Process\n\n"</span>
        <span class="hljs-keyword">for</span> i, step <span class="hljs-keyword">in</span> enumerate(self.process, <span class="hljs-number">1</span>):
            process_text += <span class="hljs-string">f"## Step <span class="hljs-subst">{i}</span>: <span class="hljs-subst">{step}</span>\n\n"</span>

        prompt = <span class="hljs-string">f"""
        Based on the following client engagement process, generate a detailed explanation for potential clients:

        <span class="hljs-subst">{process_text}</span>

        For each step, provide a brief explanation of what happens, what the client can expect, and any deliverables. Format the response in markdown.
        """</span>
        <span class="hljs-keyword">return</span> self.get_response(prompt)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">generate_proposal</span>(<span class="hljs-params">self, project_description</span>):</span>

        prompt = <span class="hljs-string">f"""
        Generate a professional project proposal based on the following client requirements:

        Project Description:
        <span class="hljs-subst">{project_description}</span>

        Include the following sections:
        1. Project Understanding
        2. Proposed Approach
        3. Estimated Timeline
        4. Estimated Budget Range
        5. Next Steps

        Base your proposal on the services and processes described in the portfolio. Format the response in markdown.
        """</span>
        <span class="hljs-keyword">return</span> self.get_response(prompt)
</code></pre>
<p>This agent is designed to help users with client and business-related tasks such as:</p>
<ul>
<li><p>Providing overviews of available services for marketing materials</p>
</li>
<li><p>Generating detailed service descriptions for specific offerings</p>
</li>
<li><p>Explaining the client engagement process to potential clients</p>
</li>
<li><p>Creating customised project proposals based on client requirements</p>
</li>
</ul>
<p>The agent also uses the LLM capabilities of the base agent (using Groq's LLama 3.3 70B model) to generate professional, business-oriented content formatted in markdown. Like before, this file also has sample service data.</p>
<p>Now we can start to work on the <code>project_agent.py</code> file and add this code to its codebase:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> agents.base_agent <span class="hljs-keyword">import</span> BaseAgent


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ProjectAgent</span>(<span class="hljs-params">BaseAgent</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        super().__init__(
            name=<span class="hljs-string">"TechExpert"</span>,
            description=<span class="hljs-string">"I'm the project specialist. I can provide detailed information about any project in this portfolio."</span>,
            avatar=<span class="hljs-string">"project_avatar.png"</span>
        )

        self.projects = {
            <span class="hljs-string">"project1"</span>: {
                <span class="hljs-string">"name"</span>: <span class="hljs-string">"E-commerce Platform"</span>,
                <span class="hljs-string">"tech_stack"</span>: [<span class="hljs-string">"React"</span>, <span class="hljs-string">"Node.js"</span>, <span class="hljs-string">"MongoDB"</span>, <span class="hljs-string">"Express"</span>],
                <span class="hljs-string">"description"</span>: <span class="hljs-string">"A full-stack e-commerce platform with user authentication, product catalog, shopping cart, and payment processing."</span>,
                <span class="hljs-string">"highlights"</span>: [<span class="hljs-string">"Responsive design"</span>, <span class="hljs-string">"RESTful API"</span>, <span class="hljs-string">"Stripe integration"</span>, <span class="hljs-string">"JWT authentication"</span>],
                <span class="hljs-string">"github_link"</span>: <span class="hljs-string">"https://github.com/username/ecommerce-platform"</span>,
                <span class="hljs-string">"demo_link"</span>: <span class="hljs-string">"https://ecommerce-demo.example.com"</span>
            },
            <span class="hljs-string">"project2"</span>: {
                <span class="hljs-string">"name"</span>: <span class="hljs-string">"Task Management App"</span>,
                <span class="hljs-string">"tech_stack"</span>: [<span class="hljs-string">"Vue.js"</span>, <span class="hljs-string">"Firebase"</span>, <span class="hljs-string">"Tailwind CSS"</span>],
                <span class="hljs-string">"description"</span>: <span class="hljs-string">"A real-time task management application with collaborative features, notifications, and progress tracking."</span>,
                <span class="hljs-string">"highlights"</span>: [<span class="hljs-string">"Real-time updates"</span>, <span class="hljs-string">"User collaboration"</span>, <span class="hljs-string">"Drag-and-drop interface"</span>, <span class="hljs-string">"Progressive Web App"</span>],
                <span class="hljs-string">"github_link"</span>: <span class="hljs-string">"https://github.com/username/task-manager"</span>,
                <span class="hljs-string">"demo_link"</span>: <span class="hljs-string">"https://taskmanager-demo.example.com"</span>
            },
            <span class="hljs-string">"project3"</span>: {
                <span class="hljs-string">"name"</span>: <span class="hljs-string">"Data Visualization Dashboard"</span>,
                <span class="hljs-string">"tech_stack"</span>: [<span class="hljs-string">"Python"</span>, <span class="hljs-string">"Django"</span>, <span class="hljs-string">"D3.js"</span>, <span class="hljs-string">"PostgreSQL"</span>],
                <span class="hljs-string">"description"</span>: <span class="hljs-string">"An interactive dashboard for visualizing complex datasets with filtering, sorting, and export capabilities."</span>,
                <span class="hljs-string">"highlights"</span>: [<span class="hljs-string">"Interactive charts"</span>, <span class="hljs-string">"Data filtering"</span>, <span class="hljs-string">"CSV/PDF export"</span>, <span class="hljs-string">"Responsive design"</span>],
                <span class="hljs-string">"github_link"</span>: <span class="hljs-string">"https://github.com/username/data-dashboard"</span>,
                <span class="hljs-string">"demo_link"</span>: <span class="hljs-string">"https://dataviz-demo.example.com"</span>
            }
        }

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_project_list</span>(<span class="hljs-params">self</span>):</span>

        project_list = <span class="hljs-string">"# Available Projects\n\n"</span>
        <span class="hljs-keyword">for</span> project_id, project <span class="hljs-keyword">in</span> self.projects.items():
            project_list += <span class="hljs-string">f"## <span class="hljs-subst">{project[<span class="hljs-string">'name'</span>]}</span>\n"</span>
            project_list += <span class="hljs-string">f"**Tech Stack**: <span class="hljs-subst">{<span class="hljs-string">', '</span>.join(project[<span class="hljs-string">'tech_stack'</span>])}</span>\n"</span>
            project_list += <span class="hljs-string">f"<span class="hljs-subst">{project[<span class="hljs-string">'description'</span>]}</span>\n\n"</span>

        <span class="hljs-keyword">return</span> project_list

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_project_details</span>(<span class="hljs-params">self, project_id</span>):</span>

        <span class="hljs-keyword">if</span> project_id <span class="hljs-keyword">in</span> self.projects:
            project = self.projects[project_id]
            prompt = <span class="hljs-string">f"""
            Generate a detailed description for the following project:

            Project Name: <span class="hljs-subst">{project[<span class="hljs-string">'name'</span>]}</span>
            Tech Stack: <span class="hljs-subst">{<span class="hljs-string">', '</span>.join(project[<span class="hljs-string">'tech_stack'</span>])}</span>
            Description: <span class="hljs-subst">{project[<span class="hljs-string">'description'</span>]}</span>
            Highlights: <span class="hljs-subst">{<span class="hljs-string">', '</span>.join(project[<span class="hljs-string">'highlights'</span>])}</span>
            GitHub: <span class="hljs-subst">{project[<span class="hljs-string">'github_link'</span>]}</span>
            Demo: <span class="hljs-subst">{project[<span class="hljs-string">'demo_link'</span>]}</span>

            Include technical details about implementation challenges and solutions. Format the response in markdown.
            """</span>
            <span class="hljs-keyword">return</span> self.get_response(prompt)
        <span class="hljs-keyword">else</span>:
            <span class="hljs-keyword">return</span> <span class="hljs-string">"Project not found. Please check the project ID and try again."</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">answer_technical_question</span>(<span class="hljs-params">self, project_id, question</span>):</span>

        <span class="hljs-keyword">if</span> project_id <span class="hljs-keyword">in</span> self.projects:
            project = self.projects[project_id]
            prompt = <span class="hljs-string">f"""
            Answer the following technical question about this project:

            Project Name: <span class="hljs-subst">{project[<span class="hljs-string">'name'</span>]}</span>
            Tech Stack: <span class="hljs-subst">{<span class="hljs-string">', '</span>.join(project[<span class="hljs-string">'tech_stack'</span>])}</span>
            Description: <span class="hljs-subst">{project[<span class="hljs-string">'description'</span>]}</span>
            Highlights: <span class="hljs-subst">{<span class="hljs-string">', '</span>.join(project[<span class="hljs-string">'highlights'</span>])}</span>

            Question: <span class="hljs-subst">{question}</span>

            Provide a detailed technical answer with code examples if relevant.
            """</span>
            <span class="hljs-keyword">return</span> self.get_response(prompt)
        <span class="hljs-keyword">else</span>:
            <span class="hljs-keyword">return</span> <span class="hljs-string">"Project not found. Please check the project ID and try again."</span>
</code></pre>
<p>This agent is designed to help users with project-related tasks such as:</p>
<ul>
<li><p>Providing an overview of all projects in a portfolio</p>
</li>
<li><p>Generating detailed descriptions of specific projects</p>
</li>
<li><p>Answering technical questions about implementation details</p>
</li>
</ul>
<p>The agent, like in the previous examples, uses the LLM capabilities of the base agent (using Groq's LLama 3.3 70B model) to generate technical, project-oriented content formatted in markdown. This is good for technical documentation, or when responding to inquiries about project implementations. We’re using mock data here as opposed to a database.</p>
<p>With that file completed, we have two left. The next is the <code>research_agent.py</code> file, so go ahead and add this code:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> agents.base_agent <span class="hljs-keyword">import</span> BaseAgent
<span class="hljs-keyword">import</span> requests
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">import</span> json


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ResearchAgent</span>(<span class="hljs-params">BaseAgent</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        super().__init__(
            name=<span class="hljs-string">"ResearchAssistant"</span>,
            description=<span class="hljs-string">"I'm the research specialist. I can search the web for information about technologies, trends, and industry news."</span>,
            avatar=<span class="hljs-string">"research_avatar.png"</span>
        )
        self.api_key = os.getenv(<span class="hljs-string">"GROQ_API_KEY"</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">search_web</span>(<span class="hljs-params">self, query</span>):</span>

        headers = {
            <span class="hljs-string">"Authorization"</span>: <span class="hljs-string">f"Bearer <span class="hljs-subst">{self.api_key}</span>"</span>,
            <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>
        }

        payload = {
            <span class="hljs-string">"model"</span>: <span class="hljs-string">"llama-3.3-70b-versatile"</span>,
            <span class="hljs-string">"messages"</span>: [
                {<span class="hljs-string">"role"</span>: <span class="hljs-string">"system"</span>, <span class="hljs-string">"content"</span>: <span class="hljs-string">"You are a helpful research assistant."</span>},
                {<span class="hljs-string">"role"</span>: <span class="hljs-string">"user"</span>, <span class="hljs-string">"content"</span>: <span class="hljs-string">f"Search the web for: <span class="hljs-subst">{query}</span>"</span>}
            ],
            <span class="hljs-string">"tools"</span>: [
                {
                    <span class="hljs-string">"type"</span>: <span class="hljs-string">"web_search"</span>
                }
            ]
        }

        <span class="hljs-keyword">try</span>:
            response = requests.post(
                <span class="hljs-string">"https://api.groq.com/openai/v1/chat/completions"</span>,
                headers=headers,
                json=payload
            )

            <span class="hljs-keyword">if</span> response.status_code == <span class="hljs-number">200</span>:
                result = response.json()
                <span class="hljs-keyword">return</span> result[<span class="hljs-string">"choices"</span>][<span class="hljs-number">0</span>][<span class="hljs-string">"message"</span>][<span class="hljs-string">"content"</span>]
            <span class="hljs-keyword">else</span>:
                <span class="hljs-keyword">return</span> <span class="hljs-string">f"Error searching the web: <span class="hljs-subst">{response.status_code}</span> - <span class="hljs-subst">{response.text}</span>"</span>
        <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
            <span class="hljs-keyword">return</span> <span class="hljs-string">f"Error searching the web: <span class="hljs-subst">{str(e)}</span>"</span>

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">research_technology</span>(<span class="hljs-params">self, technology</span>):</span>

        query = <span class="hljs-string">f"latest developments and best practices for <span class="hljs-subst">{technology}</span> in software development"</span>
        search_results = self.search_web(query)

        prompt = <span class="hljs-string">f"""
        Based on the following search results about <span class="hljs-subst">{technology}</span>, provide a concise summary of:
        1. What it is
        2. Current state and popularity
        3. Key features and benefits
        4. Common use cases
        5. Future trends

        Search Results:
        <span class="hljs-subst">{search_results}</span>

        Format the response in markdown with appropriate sections.
        """</span>
        <span class="hljs-keyword">return</span> self.get_response(prompt)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">compare_technologies</span>(<span class="hljs-params">self, tech1, tech2</span>):</span>

        query = <span class="hljs-string">f"comparison between <span class="hljs-subst">{tech1}</span> and <span class="hljs-subst">{tech2}</span> for software development"</span>
        search_results = self.search_web(query)

        prompt = <span class="hljs-string">f"""
        Based on the following search results comparing <span class="hljs-subst">{tech1}</span> and <span class="hljs-subst">{tech2}</span>, provide a detailed comparison including:
        6. Core differences
        7. Performance considerations
        8. Learning curve
        9. Community support
        10. Use case recommendations

        Search Results:
        <span class="hljs-subst">{search_results}</span>

        Format the response in markdown with a comparison table and explanatory text.
        """</span>
        <span class="hljs-keyword">return</span> self.get_response(prompt)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_industry_trends</span>(<span class="hljs-params">self</span>):</span>

        query = <span class="hljs-string">"latest trends in software development industry"</span>
        search_results = self.search_web(query)

        prompt = <span class="hljs-string">f"""
        Based on the following search results about software development trends, provide a summary of:
        11. Emerging technologies
        12. Industry shifts
        13. In-demand skills
        14. Future predictions

        Search Results:
        <span class="hljs-subst">{search_results}</span>

        Format the response in markdown with appropriate sections and highlights.
        """</span>
        <span class="hljs-keyword">return</span> self.get_response(prompt)
</code></pre>
<p>This agent is designed to help users with research-related tasks such as:</p>
<ul>
<li><p>Researching specific technologies to understand their features, benefits, and use cases</p>
</li>
<li><p>Comparing different technologies to make informed decisions</p>
</li>
<li><p>Staying updated on industry trends and emerging technologies</p>
</li>
</ul>
<p>What makes this agent unique compared to the other agents is that it actively fetches real-time information from the web using the Groq Toolhouse API's web search capability instead of relying solely on pre-defined data or the LLM's training data. This allows it to provide more current and comprehensive information about rapidly evolving technology topics.</p>
<p>Ok, now we have one last AI agent to create and it’s the <code>welcome_agent.py</code> file. Add this code to the file:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> agents.base_agent <span class="hljs-keyword">import</span> BaseAgent


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">WelcomeAgent</span>(<span class="hljs-params">BaseAgent</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        super().__init__(
            name=<span class="hljs-string">"Greeter"</span>,
            description=<span class="hljs-string">"I'm the welcome agent for this portfolio. I can help guide you to the right section based on your interests."</span>,
            avatar=<span class="hljs-string">"welcome_avatar.png"</span>
        )

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">greet</span>(<span class="hljs-params">self, visitor_type=None</span>):</span>

        <span class="hljs-keyword">if</span> visitor_type == <span class="hljs-string">"employer"</span>:
            <span class="hljs-keyword">return</span> self.get_response(
                <span class="hljs-string">"Generate a friendly, professional greeting for a potential employer visiting a programmer's portfolio website. "</span>
                <span class="hljs-string">"Mention that they can explore the Projects section to see technical skills and the Career section for professional experience."</span>
            )
        <span class="hljs-keyword">elif</span> visitor_type == <span class="hljs-string">"client"</span>:
            <span class="hljs-keyword">return</span> self.get_response(
                <span class="hljs-string">"Generate a friendly, business-oriented greeting for a potential client visiting a programmer's portfolio website. "</span>
                <span class="hljs-string">"Mention that they can check out the Projects section for examples of past work and the Client section for service details."</span>
            )
        <span class="hljs-keyword">elif</span> visitor_type == <span class="hljs-string">"fellow_programmer"</span>:
            <span class="hljs-keyword">return</span> self.get_response(
                <span class="hljs-string">"Generate a friendly, casual greeting for a fellow programmer visiting a portfolio website. "</span>
                <span class="hljs-string">"Mention that they can explore the Projects section for technical details and code samples."</span>
            )
        <span class="hljs-keyword">else</span>:
            <span class="hljs-keyword">return</span> self.get_response(
                <span class="hljs-string">"Generate a friendly, general greeting for a visitor to a programmer's portfolio website. "</span>
                <span class="hljs-string">"Ask if they are an employer, client, or fellow programmer to provide more tailored information."</span>
            )

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">suggest_section</span>(<span class="hljs-params">self, interest</span>):</span>

        prompt = <span class="hljs-string">f"Based on a visitor expressing interest in '<span class="hljs-subst">{interest}</span>', suggest which section of a programmer's portfolio they should visit. Options include: Projects, Career, Client Work, About Me, Contact. Explain why in 1-2 sentences."</span>
        <span class="hljs-keyword">return</span> self.get_response(prompt)
</code></pre>
<p>This agent is designed to serve as the initial point of contact for visitors to a portfolio website, providing:</p>
<ul>
<li><p>Personalised greetings based on visitor type</p>
</li>
<li><p>Guidance to relevant sections based on specific interests</p>
</li>
<li><p>A friendly, conversational introduction to the portfolio</p>
</li>
</ul>
<p>The <code>WelcomeAgent</code> is simpler than some of the other agents we've looked at because it focuses on creating a positive first impression and helping visitors navigate to the content most relevant to their needs. It uses the LLM capabilities of the base agent to generate natural, contextually appropriate responses.</p>
<p>Ok good – your backend API is almost ready. You just have one last file to work on: the <code>main.py</code> file that completes your codebase. This file is quite big, so I will split it into three parts. You’ll need to copy and paste each section into the file. If you have not done so already, its worth installing the <a target="_blank" href="https://open-vsx.org/extension/ms-python/python">Python</a> extension for VS Code as this has debugging, linting, and formatting for Python files.</p>
<p>Alright, here is the first part of the codebase for our <code>main.py</code> file:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> flask <span class="hljs-keyword">import</span> Flask, request, jsonify
<span class="hljs-keyword">import</span> os
<span class="hljs-keyword">from</span> dotenv <span class="hljs-keyword">import</span> load_dotenv
<span class="hljs-keyword">import</span> json
<span class="hljs-keyword">import</span> requests
<span class="hljs-keyword">from</span> flask_cors <span class="hljs-keyword">import</span> CORS


load_dotenv()


app = Flask(__name__)
CORS(app)


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BaseAgent</span>:</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self, name, description</span>):</span>
        self.name = name
        self.description = description

        self.api_key = os.getenv(<span class="hljs-string">"GROQ_API_KEY"</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_response</span>(<span class="hljs-params">self, prompt</span>):</span>

        <span class="hljs-keyword">try</span>:
            headers = {
                <span class="hljs-string">"Authorization"</span>: <span class="hljs-string">f"Bearer <span class="hljs-subst">{self.api_key}</span>"</span>,
                <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>
            }

            data = {
                <span class="hljs-string">"model"</span>: <span class="hljs-string">"llama3-8b-8192"</span>,
                <span class="hljs-string">"messages"</span>: [
                    {<span class="hljs-string">"role"</span>: <span class="hljs-string">"system"</span>, <span class="hljs-string">"content"</span>: <span class="hljs-string">f"You are <span class="hljs-subst">{self.name}</span>, <span class="hljs-subst">{self.description}</span>. Respond in a helpful, concise, and professional manner."</span>},
                    {<span class="hljs-string">"role"</span>: <span class="hljs-string">"user"</span>, <span class="hljs-string">"content"</span>: prompt}
                ],
                <span class="hljs-string">"temperature"</span>: <span class="hljs-number">0.7</span>,
                <span class="hljs-string">"max_tokens"</span>: <span class="hljs-number">500</span>
            }

            response = requests.post(
                <span class="hljs-string">"https://api.groq.com/openai/v1/chat/completions"</span>,
                headers=headers,
                json=data
            )

            <span class="hljs-keyword">if</span> response.status_code == <span class="hljs-number">200</span>:
                <span class="hljs-keyword">return</span> response.json()[<span class="hljs-string">"choices"</span>][<span class="hljs-number">0</span>][<span class="hljs-string">"message"</span>][<span class="hljs-string">"content"</span>]
            <span class="hljs-keyword">else</span>:
                <span class="hljs-keyword">return</span> <span class="hljs-string">f"Error: <span class="hljs-subst">{response.status_code}</span> - <span class="hljs-subst">{response.text}</span>"</span>
        <span class="hljs-keyword">except</span> Exception <span class="hljs-keyword">as</span> e:
            <span class="hljs-keyword">return</span> <span class="hljs-string">f"An error occurred: <span class="hljs-subst">{str(e)}</span>"</span>


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">WelcomeAgent</span>(<span class="hljs-params">BaseAgent</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        super().__init__(
            <span class="hljs-string">"WelcomeAgent"</span>,
            <span class="hljs-string">"a welcome specialist who greets visitors and helps them navigate the portfolio website"</span>
        )

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">greet</span>(<span class="hljs-params">self, visitor_type=None</span>):</span>
        <span class="hljs-keyword">if</span> visitor_type == <span class="hljs-string">"employer"</span>:
            <span class="hljs-keyword">return</span> self.get_response(<span class="hljs-string">"Generate a warm welcome message for an employer visiting a programmer's portfolio website. Suggest they check out the Projects and Career sections."</span>)
        <span class="hljs-keyword">elif</span> visitor_type == <span class="hljs-string">"client"</span>:
            <span class="hljs-keyword">return</span> self.get_response(<span class="hljs-string">"Generate a warm welcome message for a potential client visiting a programmer's portfolio website. Suggest they check out the Services section."</span>)
        <span class="hljs-keyword">elif</span> visitor_type == <span class="hljs-string">"fellow_programmer"</span>:
            <span class="hljs-keyword">return</span> self.get_response(<span class="hljs-string">"Generate a warm welcome message for a fellow programmer visiting a programmer's portfolio website. Suggest they check out the Projects and Research sections."</span>)
        <span class="hljs-keyword">else</span>:
            <span class="hljs-keyword">return</span> self.get_response(<span class="hljs-string">"Generate a general welcome message for a visitor to a programmer's portfolio website. Ask if they are an employer, client, or fellow programmer."</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">suggest_section</span>(<span class="hljs-params">self, interest</span>):</span>
        <span class="hljs-keyword">return</span> self.get_response(<span class="hljs-string">f"A visitor to my portfolio website has expressed interest in <span class="hljs-subst">{interest}</span>. Suggest which section(s) of the website they should visit based on this interest."</span>)


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ProjectAgent</span>(<span class="hljs-params">BaseAgent</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        super().__init__(
            <span class="hljs-string">"ProjectAgent"</span>,
            <span class="hljs-string">"a project specialist who provides detailed information about the programmer's projects"</span>
        )

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_project_list</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> self.get_response(<span class="hljs-string">"Generate a list of 3-5 impressive software development projects that could be in a programmer's portfolio. Include a brief description for each."</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_project_details</span>(<span class="hljs-params">self, project_id</span>):</span>
        project_prompts = {
            <span class="hljs-string">"project1"</span>: <span class="hljs-string">"Describe in detail an e-commerce platform project for a programmer's portfolio. Include technologies used, challenges overcome, and key features."</span>,
            <span class="hljs-string">"project2"</span>: <span class="hljs-string">"Describe in detail a task management application project for a programmer's portfolio. Include technologies used, challenges overcome, and key features."</span>,
            <span class="hljs-string">"project3"</span>: <span class="hljs-string">"Describe in detail a data visualization dashboard project for a programmer's portfolio. Include technologies used, challenges overcome, and key features."</span>
        }

        prompt = project_prompts.get(
            project_id, <span class="hljs-string">f"Describe a project called <span class="hljs-subst">{project_id}</span> in detail."</span>)
        <span class="hljs-keyword">return</span> self.get_response(prompt)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">answer_technical_question</span>(<span class="hljs-params">self, project_id, question</span>):</span>
        <span class="hljs-keyword">return</span> self.get_response(<span class="hljs-string">f"Answer this technical question about a project: '<span class="hljs-subst">{question}</span>'. The project is <span class="hljs-subst">{project_id}</span>."</span>)
</code></pre>
<p>This part of the code has your imports, set up, and some greetings for the AI agent.</p>
<p>Now for part two, add this code to the file underneath the first code you added:</p>
<pre><code class="lang-python">
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CareerAgent</span>(<span class="hljs-params">BaseAgent</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        super().__init__(
            <span class="hljs-string">"CareerAgent"</span>,
            <span class="hljs-string">"a career specialist who provides information about the programmer's skills and experience"</span>
        )

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_skills_summary</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> self.get_response(<span class="hljs-string">"Generate a comprehensive summary of technical and professional skills for a full-stack developer's portfolio."</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_experience_summary</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> self.get_response(<span class="hljs-string">"Generate a summary of work experience for a full-stack developer with 5+ years of experience."</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">assess_job_fit</span>(<span class="hljs-params">self, job_description</span>):</span>
        <span class="hljs-keyword">return</span> self.get_response(<span class="hljs-string">f"Assess how well a full-stack developer with 5+ years of experience would fit this job description: '<span class="hljs-subst">{job_description}</span>'. Highlight matching skills and experience."</span>)


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ClientAgent</span>(<span class="hljs-params">BaseAgent</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        super().__init__(
            <span class="hljs-string">"ClientAgent"</span>,
            <span class="hljs-string">"a client specialist who provides information about services, pricing, and the client engagement process"</span>
        )

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_services_overview</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> self.get_response(<span class="hljs-string">"Generate an overview of services that a freelance full-stack developer might offer to clients."</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_service_details</span>(<span class="hljs-params">self, service_type</span>):</span>
        service_prompts = {
            <span class="hljs-string">"web_development"</span>: <span class="hljs-string">"Describe web development services offered by a freelance full-stack developer, including technologies, pricing range, and typical timeline."</span>,
            <span class="hljs-string">"mobile_development"</span>: <span class="hljs-string">"Describe mobile app development services offered by a freelance full-stack developer, including technologies, pricing range, and typical timeline."</span>,
            <span class="hljs-string">"consulting"</span>: <span class="hljs-string">"Describe technical consulting services offered by a freelance full-stack developer, including areas of expertise, hourly rate range, and engagement model."</span>
        }

        prompt = service_prompts.get(
            service_type, <span class="hljs-string">f"Describe <span class="hljs-subst">{service_type}</span> services in detail."</span>)
        <span class="hljs-keyword">return</span> self.get_response(prompt)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">explain_process</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> self.get_response(<span class="hljs-string">"Explain the client engagement process for a freelance full-stack developer, from initial consultation to project delivery."</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">generate_proposal</span>(<span class="hljs-params">self, project_description</span>):</span>
        <span class="hljs-keyword">return</span> self.get_response(<span class="hljs-string">f"Generate a project proposal for this client request: '<span class="hljs-subst">{project_description}</span>'. Include estimated timeline, cost range, and approach."</span>)


<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ResearchAgent</span>(<span class="hljs-params">BaseAgent</span>):</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">__init__</span>(<span class="hljs-params">self</span>):</span>
        super().__init__(
            <span class="hljs-string">"ResearchAgent"</span>,
            <span class="hljs-string">"a research specialist who provides information about technologies, trends, and industry news"</span>
        )

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">search_web</span>(<span class="hljs-params">self, query</span>):</span>
        <span class="hljs-keyword">return</span> self.get_response(<span class="hljs-string">f"Provide information about '<span class="hljs-subst">{query}</span>' as if you've just searched the web for the latest information. Include key points and insights."</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">compare_technologies</span>(<span class="hljs-params">self, tech1, tech2</span>):</span>
        <span class="hljs-keyword">return</span> self.get_response(<span class="hljs-string">f"Compare <span class="hljs-subst">{tech1}</span> vs <span class="hljs-subst">{tech2}</span> in terms of features, performance, use cases, community support, and future prospects."</span>)

    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">get_industry_trends</span>(<span class="hljs-params">self</span>):</span>
        <span class="hljs-keyword">return</span> self.get_response(<span class="hljs-string">"Describe current trends in software development and technology that are important for developers to be aware of."</span>)


welcome_agent = WelcomeAgent()
project_agent = ProjectAgent()
career_agent = CareerAgent()
client_agent = ClientAgent()
research_agent = ResearchAgent()


<span class="hljs-meta">@app.route('/static/images/default_avatar.png')</span>
<span class="hljs-meta">@app.route('/static/images/default_project.jpg')</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">block_default_images</span>():</span>

    response = app.make_response(
        <span class="hljs-string">b'GIF89a\x01\x00\x01\x00\x80\x00\x00\xff\xff\xff\x00\x00\x00!\xf9\x04\x01\x00\x00\x00\x00,\x00\x00\x00\x00\x01\x00\x01\x00\x00\x02\x02D\x01\x00;'</span>)
    response.headers[<span class="hljs-string">'Content-Type'</span>] = <span class="hljs-string">'image/gif'</span>

    response.headers[<span class="hljs-string">'Cache-Control'</span>] = <span class="hljs-string">'public, max-age=31536000'</span>
    response.headers[<span class="hljs-string">'Expires'</span>] = <span class="hljs-string">'Thu, 31 Dec 2037 23:59:59 GMT'</span>
    <span class="hljs-keyword">return</span> response


<span class="hljs-meta">@app.route('/api/welcome', methods=['POST'])</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">welcome_agent_endpoint</span>():</span>
    data = request.json
    message = data.get(<span class="hljs-string">'message'</span>, <span class="hljs-string">''</span>)

    visitor_type = <span class="hljs-literal">None</span>
    <span class="hljs-keyword">if</span> <span class="hljs-string">'employer'</span> <span class="hljs-keyword">in</span> message.lower():
        visitor_type = <span class="hljs-string">'employer'</span>
    <span class="hljs-keyword">elif</span> <span class="hljs-string">'client'</span> <span class="hljs-keyword">in</span> message.lower():
        visitor_type = <span class="hljs-string">'client'</span>
    <span class="hljs-keyword">elif</span> <span class="hljs-string">'programmer'</span> <span class="hljs-keyword">in</span> message.lower() <span class="hljs-keyword">or</span> <span class="hljs-string">'developer'</span> <span class="hljs-keyword">in</span> message.lower():
        visitor_type = <span class="hljs-string">'fellow_programmer'</span>

    <span class="hljs-keyword">if</span> <span class="hljs-string">'interest'</span> <span class="hljs-keyword">in</span> message.lower() <span class="hljs-keyword">or</span> <span class="hljs-string">'looking for'</span> <span class="hljs-keyword">in</span> message.lower():

        interest = message.replace(<span class="hljs-string">'interest'</span>, <span class="hljs-string">''</span>).replace(
            <span class="hljs-string">'looking for'</span>, <span class="hljs-string">''</span>).strip()
        response = welcome_agent.suggest_section(interest)
    <span class="hljs-keyword">else</span>:
        response = welcome_agent.greet(visitor_type)

    <span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'response'</span>: response})
</code></pre>
<p>In this part of your codebase, you have more AI responses and a welcome API route.</p>
<p>Lastly, complete the code by adding this final piece at the end:</p>
<pre><code class="lang-python">
<span class="hljs-meta">@app.route('/api/project', methods=['POST'])</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">project_agent_endpoint</span>():</span>
    data = request.json
    message = data.get(<span class="hljs-string">'message'</span>, <span class="hljs-string">''</span>)

    project_id = <span class="hljs-literal">None</span>
    <span class="hljs-keyword">if</span> <span class="hljs-string">'e-commerce'</span> <span class="hljs-keyword">in</span> message.lower() <span class="hljs-keyword">or</span> <span class="hljs-string">'ecommerce'</span> <span class="hljs-keyword">in</span> message.lower():
        project_id = <span class="hljs-string">'project1'</span>
    <span class="hljs-keyword">elif</span> <span class="hljs-string">'task'</span> <span class="hljs-keyword">in</span> message.lower() <span class="hljs-keyword">or</span> <span class="hljs-string">'management'</span> <span class="hljs-keyword">in</span> message.lower():
        project_id = <span class="hljs-string">'project2'</span>
    <span class="hljs-keyword">elif</span> <span class="hljs-string">'data'</span> <span class="hljs-keyword">in</span> message.lower() <span class="hljs-keyword">or</span> <span class="hljs-string">'visualization'</span> <span class="hljs-keyword">in</span> message.lower() <span class="hljs-keyword">or</span> <span class="hljs-string">'dashboard'</span> <span class="hljs-keyword">in</span> message.lower():
        project_id = <span class="hljs-string">'project3'</span>

    <span class="hljs-keyword">if</span> project_id <span class="hljs-keyword">and</span> (<span class="hljs-string">'tell me more'</span> <span class="hljs-keyword">in</span> message.lower() <span class="hljs-keyword">or</span> <span class="hljs-string">'details'</span> <span class="hljs-keyword">in</span> message.lower()):
        response = project_agent.get_project_details(project_id)
    <span class="hljs-keyword">elif</span> <span class="hljs-string">'list'</span> <span class="hljs-keyword">in</span> message.lower() <span class="hljs-keyword">or</span> <span class="hljs-string">'all projects'</span> <span class="hljs-keyword">in</span> message.lower():
        response = project_agent.get_project_list()
    <span class="hljs-keyword">elif</span> project_id:

        response = project_agent.answer_technical_question(project_id, message)
    <span class="hljs-keyword">else</span>:

        response = project_agent.get_response(
            <span class="hljs-string">f"The user asked: '<span class="hljs-subst">{message}</span>'. Respond as if you are a project specialist for a portfolio website. "</span>
            <span class="hljs-string">"If they're asking about a specific project, suggest they mention one of the projects: "</span>
            <span class="hljs-string">"E-commerce Platform, Task Management App, or Data Visualization Dashboard."</span>
        )

    <span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'response'</span>: response})


<span class="hljs-meta">@app.route('/api/career', methods=['POST'])</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">career_agent_endpoint</span>():</span>
    data = request.json
    message = data.get(<span class="hljs-string">'message'</span>, <span class="hljs-string">''</span>)

    <span class="hljs-keyword">if</span> <span class="hljs-string">'skills'</span> <span class="hljs-keyword">in</span> message.lower():
        response = career_agent.get_skills_summary()
    <span class="hljs-keyword">elif</span> <span class="hljs-string">'experience'</span> <span class="hljs-keyword">in</span> message.lower() <span class="hljs-keyword">or</span> <span class="hljs-string">'work history'</span> <span class="hljs-keyword">in</span> message.lower():
        response = career_agent.get_experience_summary()
    <span class="hljs-keyword">elif</span> <span class="hljs-string">'job'</span> <span class="hljs-keyword">in</span> message.lower() <span class="hljs-keyword">or</span> <span class="hljs-string">'position'</span> <span class="hljs-keyword">in</span> message.lower() <span class="hljs-keyword">or</span> <span class="hljs-string">'role'</span> <span class="hljs-keyword">in</span> message.lower():

        response = career_agent.assess_job_fit(message)
    <span class="hljs-keyword">else</span>:

        response = career_agent.get_response(
            <span class="hljs-string">f"The user asked: '<span class="hljs-subst">{message}</span>'. Respond as if you are a career specialist for a portfolio website. "</span>
            <span class="hljs-string">"Suggest they ask about skills, experience, or job fit assessment."</span>
        )

    <span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'response'</span>: response})


<span class="hljs-meta">@app.route('/api/client', methods=['POST'])</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">client_agent_endpoint</span>():</span>
    data = request.json
    message = data.get(<span class="hljs-string">'message'</span>, <span class="hljs-string">''</span>)

    <span class="hljs-keyword">if</span> <span class="hljs-string">'services'</span> <span class="hljs-keyword">in</span> message.lower() <span class="hljs-keyword">or</span> <span class="hljs-string">'offerings'</span> <span class="hljs-keyword">in</span> message.lower():
        response = client_agent.get_services_overview()
    <span class="hljs-keyword">elif</span> <span class="hljs-string">'web'</span> <span class="hljs-keyword">in</span> message.lower() <span class="hljs-keyword">and</span> <span class="hljs-string">'development'</span> <span class="hljs-keyword">in</span> message.lower():
        response = client_agent.get_service_details(<span class="hljs-string">'web_development'</span>)
    <span class="hljs-keyword">elif</span> <span class="hljs-string">'mobile'</span> <span class="hljs-keyword">in</span> message.lower() <span class="hljs-keyword">and</span> <span class="hljs-string">'development'</span> <span class="hljs-keyword">in</span> message.lower():
        response = client_agent.get_service_details(<span class="hljs-string">'mobile_development'</span>)
    <span class="hljs-keyword">elif</span> <span class="hljs-string">'consulting'</span> <span class="hljs-keyword">in</span> message.lower() <span class="hljs-keyword">or</span> <span class="hljs-string">'technical consulting'</span> <span class="hljs-keyword">in</span> message.lower():
        response = client_agent.get_service_details(<span class="hljs-string">'consulting'</span>)
    <span class="hljs-keyword">elif</span> <span class="hljs-string">'process'</span> <span class="hljs-keyword">in</span> message.lower() <span class="hljs-keyword">or</span> <span class="hljs-string">'how does it work'</span> <span class="hljs-keyword">in</span> message.lower():
        response = client_agent.explain_process()
    <span class="hljs-keyword">elif</span> <span class="hljs-string">'proposal'</span> <span class="hljs-keyword">in</span> message.lower() <span class="hljs-keyword">or</span> <span class="hljs-string">'quote'</span> <span class="hljs-keyword">in</span> message.lower() <span class="hljs-keyword">or</span> <span class="hljs-string">'estimate'</span> <span class="hljs-keyword">in</span> message.lower():

        response = client_agent.generate_proposal(message)
    <span class="hljs-keyword">else</span>:

        response = client_agent.get_response(
            <span class="hljs-string">f"The user asked: '<span class="hljs-subst">{message}</span>'. Respond as if you are a client specialist for a portfolio website. "</span>
            <span class="hljs-string">"Suggest they ask about services, the client engagement process, or request a proposal."</span>
        )

    <span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'response'</span>: response})


<span class="hljs-meta">@app.route('/api/research', methods=['POST'])</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">research_agent_endpoint</span>():</span>
    data = request.json
    message = data.get(<span class="hljs-string">'message'</span>, <span class="hljs-string">''</span>)

    <span class="hljs-keyword">if</span> <span class="hljs-string">'compare'</span> <span class="hljs-keyword">in</span> message.lower() <span class="hljs-keyword">and</span> (<span class="hljs-string">'vs'</span> <span class="hljs-keyword">in</span> message.lower() <span class="hljs-keyword">or</span> <span class="hljs-string">'versus'</span> <span class="hljs-keyword">in</span> message.lower()):

        tech_parts = message.lower().replace(<span class="hljs-string">'compare'</span>, <span class="hljs-string">''</span>).replace(
            <span class="hljs-string">'vs'</span>, <span class="hljs-string">' '</span>).replace(<span class="hljs-string">'versus'</span>, <span class="hljs-string">' '</span>).split()
        tech1 = tech_parts[<span class="hljs-number">0</span>] <span class="hljs-keyword">if</span> len(tech_parts) &gt; <span class="hljs-number">0</span> <span class="hljs-keyword">else</span> <span class="hljs-string">''</span>
        tech2 = tech_parts[<span class="hljs-number">-1</span>] <span class="hljs-keyword">if</span> len(tech_parts) &gt; <span class="hljs-number">1</span> <span class="hljs-keyword">else</span> <span class="hljs-string">''</span>
        response = research_agent.compare_technologies(tech1, tech2)
    <span class="hljs-keyword">elif</span> <span class="hljs-string">'trends'</span> <span class="hljs-keyword">in</span> message.lower() <span class="hljs-keyword">or</span> <span class="hljs-string">'industry'</span> <span class="hljs-keyword">in</span> message.lower():
        response = research_agent.get_industry_trends()
    <span class="hljs-keyword">else</span>:
        response = research_agent.search_web(message)

    <span class="hljs-keyword">return</span> jsonify({<span class="hljs-string">'response'</span>: response})


<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">'__main__'</span>:

    app.config[<span class="hljs-string">'SEND_FILE_MAX_AGE_DEFAULT'</span>] = <span class="hljs-number">0</span>
    app.config[<span class="hljs-string">'TEMPLATES_AUTO_RELOAD'</span>] = <span class="hljs-literal">True</span>   <span class="hljs-comment"># Ensure templates reload</span>

<span class="hljs-meta">    @app.after_request</span>
    <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">add_header</span>(<span class="hljs-params">response</span>):</span>
        response.headers[<span class="hljs-string">'Cache-Control'</span>] = <span class="hljs-string">'no-store, no-cache, must-revalidate, max-age=0'</span>
        response.headers[<span class="hljs-string">'Pragma'</span>] = <span class="hljs-string">'no-cache'</span>
        response.headers[<span class="hljs-string">'Expires'</span>] = <span class="hljs-string">'0'</span>
        <span class="hljs-keyword">return</span> response

    app.run(host=<span class="hljs-string">'0.0.0.0'</span>, port=<span class="hljs-number">5001</span>, debug=<span class="hljs-literal">True</span>,
            use_reloader=<span class="hljs-literal">False</span>, threaded=<span class="hljs-literal">True</span>)
</code></pre>
<p>Okay, if your file has errors, they're probably caused by the Python indentation. Hopefully, the formatting will not make them too difficult to fix.</p>
<p>The file is now complete, and you’ve created the rest of your AI API routes.</p>
<h3 id="heading-running-our-python-backend">Running Our Python Backend</h3>
<p>All that's left is to run your Flask server and get the backend up and running. You can do that with this run script inside the <code>venv</code> folder:</p>
<pre><code class="lang-shell">python3 main.py
</code></pre>
<p>Your backend should now be running on <a target="_blank" href="http://127.0.0.1:5001/">http://127.0.0.1:5001/</a>. If you go to the page you will see an error like this:</p>
<pre><code class="lang-markdown">Not Found

The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.
</code></pre>
<p>This is expected, because if you have checked the codebase, you’ll realise that there are no GET routes, only POST routes. To see them working, you need to use an HTTP client like Postman. Another option is to create some <code>curl</code> commands that send a POST request, which you can run from your terminal. Let's use <code>curl</code> because there is less setup. You’ll need to copy and paste the commands.</p>
<p>Each POST request will use exactly one API call on Groq Cloud for your API Key which you can view here <a target="_blank" href="https://console.groq.com/keys">https://console.groq.com/keys</a>. Remember that it’s free to use but there are usage limits which you can read about in their documentation on <a target="_blank" href="https://console.groq.com/docs/rate-limits">Rate Limits</a>.</p>
<p>I have provided some sample curl commands below – just copy and paste them into your terminal and hit enter, and you should see the response message:</p>
<p><strong>1. Testing the Welcome Agent Endpoint</strong></p>
<pre><code class="lang-shell">curl -X POST http://localhost:5001/api/welcome \
  -H "Content-Type: application/json" \
  -d '{"message": "I am an employer looking for a skilled developer"}'
</code></pre>
<p><strong>2. Testing the Project Agent Endpoint</strong></p>
<pre><code class="lang-shell">curl -X POST http://localhost:5001/api/project \
  -H "Content-Type: application/json" \
  -d '{"message": "Tell me more about the e-commerce project"}'
</code></pre>
<p><strong>3. Testing the Career Agent Endpoint</strong></p>
<pre><code class="lang-shell">curl -X POST http://localhost:5001/api/career \
  -H "Content-Type: application/json" \
  -d '{"message": "What skills do you have?"}'
</code></pre>
<p><strong>4. Testing the Client Agent Endpoint</strong></p>
<pre><code class="lang-shell">curl -X POST http://localhost:5001/api/client \
  -H "Content-Type: application/json" \
  -d '{"message": "What services do you offer?"}'
</code></pre>
<p><strong>5. Testing the Research Agent Endpoint</strong></p>
<pre><code class="lang-shell">curl -X POST http://localhost:5001/api/research \
  -H "Content-Type: application/json" \
  -d '{"message": "What are the current trends in web development?"}'
</code></pre>
<h2 id="heading-building-our-react-frontend">Building Our React Frontend</h2>
<p>We have reached halfway point, and all that's left is to build your front end. We’ll build the front end using <a target="_blank" href="https://vite.dev/">Vite</a>, and the website will have six pages. Make sure that you are now inside the root folder for the <code>ai-agent-app</code> project. You can leave the Python server running because your front end is going to connect to the API routes you created.</p>
<p>Now, run the commands below to setup your React project using Vite, Tailwind CSS, react-router and Axios, which we need for page routing and fetch requests:</p>
<pre><code class="lang-shell">npm create vite@latest frontend -- --template react
cd frontend
npm install -D tailwindcss@3 postcss autoprefixer react-router axios
npx tailwindcss init -p
npm install
</code></pre>
<p>Great, now with those packages installed and our dependencies set up, we are almost ready to start on the codebase. But before that, we need to run one more script, which is going to create all of the files and folders for our project. It's much faster than doing them all manually.</p>
<p>Run this command inside the frontend folder:</p>
<pre><code class="lang-shell">mkdir -p src/components src/pages
touch src/style.css src/components/{Chat,Footer,Layout,Navbar}.jsx
touch src/pages/{Career,Contact,Home,Projects,Research,Services}.jsx
</code></pre>
<p>Our React frontend should now have a project structure like the example shown below:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741977692485/9940901d-bd7a-49dd-a18b-ab3edf5e3714.png" alt="AI Agent App frontend project structure" class="image--center mx-auto" width="500" height="1548" loading="lazy"></p>
<p>We are now ready to start writing some code.</p>
<p>Up first is the <code>tailwind.config.js</code> file. This is the only configuration file you’ll need to work on, as the others already have the configuration we need. Replace all of the code in the file with the code below:</p>
<pre><code class="lang-javascript"><span class="hljs-comment">/** <span class="hljs-doctag">@type <span class="hljs-type">{import('tailwindcss').Config}</span> </span>*/</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-attr">content</span>: [<span class="hljs-string">'./index.html'</span>, <span class="hljs-string">'./src/**/*.{js,ts,jsx,tsx}'</span>],
  <span class="hljs-attr">theme</span>: {
    <span class="hljs-attr">extend</span>: {},
  },
  <span class="hljs-attr">plugins</span>: [],
};
</code></pre>
<p>All this code does is add the paths to all of your template files.</p>
<p>Ok, next, you are going to work on your styles and Tailwind CSS. There are three CSS files to work on: <code>App.css</code>, <code>index.css</code>, and <code>style.css</code>.</p>
<p>First up is the <code>App.css</code> file. Replace all of the code with this code here:</p>
<pre><code class="lang-css"><span class="hljs-selector-id">#root</span> {
  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">text-align</span>: left;
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">flex-direction</span>: column;
  <span class="hljs-attribute">min-height</span>: <span class="hljs-number">100vh</span>;
}

<span class="hljs-selector-tag">main</span> {
  <span class="hljs-attribute">flex</span>: <span class="hljs-number">1</span>;
}
</code></pre>
<p>We just have some basic layout styles here for <code>root</code> and <code>main</code>.</p>
<p>Next is the <code>index.css</code> file. Below is the code you’ll need, so replace everything in the file with it:</p>
<pre><code class="lang-css"><span class="hljs-keyword">@tailwind</span> base;
<span class="hljs-keyword">@tailwind</span> components;
<span class="hljs-keyword">@tailwind</span> utilities;

<span class="hljs-selector-pseudo">:root</span> {
  <span class="hljs-attribute">font-family</span>: system-ui, Avenir, Helvetica, Arial, sans-serif;
  <span class="hljs-attribute">font-synthesis</span>: none;
  <span class="hljs-attribute">text-rendering</span>: optimizeLegibility;
  <span class="hljs-attribute">-webkit-font-smoothing</span>: antialiased;
  <span class="hljs-attribute">-moz-osx-font-smoothing</span>: grayscale;
}

<span class="hljs-keyword">@layer</span> components {
  <span class="hljs-selector-class">.chat-container</span> {
    @apply w-full h-96 flex flex-col;
  }

  <span class="hljs-selector-class">.chat-messages</span> {
    @apply flex-1 overflow-y-auto p-4;
  }

  <span class="hljs-selector-class">.message</span> {
    @apply flex mb-4;
  }

  <span class="hljs-selector-class">.user-message</span> {
    @apply justify-end;
  }

  <span class="hljs-selector-class">.agent-message</span> {
    @apply justify-start;
  }

  <span class="hljs-selector-class">.message-avatar</span> {
    @apply flex-shrink-0 mr-2;
  }

  <span class="hljs-selector-class">.avatar-placeholder</span> {
    @apply w-10 h-10 rounded-full bg-blue-500 text-white flex items-center justify-center font-bold;
  }

  <span class="hljs-selector-class">.message-content</span> {
    @apply p-3 rounded-lg max-w-xs <span class="hljs-attribute">sm</span>:max-w-sm md:max-w-md;
  }

  <span class="hljs-selector-class">.user-message</span> <span class="hljs-selector-class">.message-content</span> {
    @apply bg-blue-500 text-white;
  }

  <span class="hljs-selector-class">.agent-message</span> <span class="hljs-selector-class">.message-content</span> {
    @apply bg-gray-200 text-gray-800;
  }

  <span class="hljs-selector-class">.chat-input-container</span> {
    @apply p-4 border-t border-gray-200;
  }

  <span class="hljs-selector-class">.chat-input-group</span> {
    @apply flex;
  }

  <span class="hljs-selector-class">.chat-input</span> {
    @apply flex-1 border border-gray-300 rounded-l-lg p-2 <span class="hljs-attribute">focus</span>:outline-none focus:ring-<span class="hljs-number">2</span> focus:ring-blue-<span class="hljs-number">500</span>;
  }

  <span class="hljs-selector-class">.chat-send-button</span> {
    @apply bg-blue-500 text-white px-4 py-2 rounded-r-lg <span class="hljs-attribute">hover</span>:bg-blue-<span class="hljs-number">600</span> focus:outline-none focus:ring-<span class="hljs-number">2</span> focus:ring-blue-<span class="hljs-number">500</span>;
  }

  <span class="hljs-selector-class">.loading-dots</span><span class="hljs-selector-pseudo">:after</span> {
    @apply content-['...'] animate-pulse;
  }

  <span class="hljs-selector-class">.project-image-placeholder</span> {
    @apply h-48 bg-gray-300 flex items-center justify-center text-gray-600 font-semibold;
  }

  <span class="hljs-selector-class">.agent-avatar-placeholder</span> {
    @apply w-16 h-16 rounded-full bg-blue-500 text-white flex items-center justify-center font-bold mx-auto;
  }
}
</code></pre>
<p>All of these styles relate to your Tailwind CSS setup throughout your project.</p>
<p>Just one file remains for the CSS and it’s the <code>style.css</code> file. This is a big file, so I will split the code into two parts – just copy and paste them into the file.</p>
<p>Here is the first part:</p>
<pre><code class="lang-css"><span class="hljs-comment">/* Main Styles */</span>
<span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">font-family</span>: <span class="hljs-string">'Inter'</span>, -apple-system, BlinkMacSystemFont, <span class="hljs-string">'Segoe UI'</span>, Roboto, Oxygen,
    Ubuntu, Cantarell, <span class="hljs-string">'Open Sans'</span>, <span class="hljs-string">'Helvetica Neue'</span>, sans-serif;
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#333</span>;
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#f8f9fa</span>;
}

<span class="hljs-comment">/* Layout Styles */</span>
<span class="hljs-selector-id">#root</span> {
  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">100%</span>;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0</span>;
  <span class="hljs-attribute">text-align</span>: left;
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">flex-direction</span>: column;
  <span class="hljs-attribute">min-height</span>: <span class="hljs-number">100vh</span>;
}

<span class="hljs-selector-tag">main</span> {
  <span class="hljs-attribute">flex</span>: <span class="hljs-number">1</span>;
}

<span class="hljs-selector-tag">h1</span>,
<span class="hljs-selector-tag">h2</span>,
<span class="hljs-selector-tag">h3</span>,
<span class="hljs-selector-tag">h4</span>,
<span class="hljs-selector-tag">h5</span>,
<span class="hljs-selector-tag">h6</span> {
  <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">600</span>;
}

<span class="hljs-selector-tag">footer</span> {
  <span class="hljs-attribute">margin-top</span>: auto;
}

<span class="hljs-comment">/* Navbar Styles */</span>
<span class="hljs-selector-class">.navbar</span> {
  <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">2px</span> <span class="hljs-number">4px</span> <span class="hljs-built_in">rgba</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.1</span>);
}

<span class="hljs-selector-class">.navbar-brand</span> {
  <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">700</span>;
}

<span class="hljs-selector-class">.navbar</span> <span class="hljs-selector-class">.container</span> {
  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">1320px</span>;
}

<span class="hljs-comment">/* Card Styles */</span>
<span class="hljs-selector-class">.card</span> {
  <span class="hljs-attribute">border</span>: none;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">0.5rem</span>;
  <span class="hljs-attribute">transition</span>: transform <span class="hljs-number">0.3s</span> ease, box-shadow <span class="hljs-number">0.3s</span> ease;
  <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">1rem</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">2em</span>;
}

<span class="hljs-selector-class">.card</span><span class="hljs-selector-pseudo">:hover</span> {
  <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">translateY</span>(-<span class="hljs-number">5px</span>);
  <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">10px</span> <span class="hljs-number">20px</span> <span class="hljs-built_in">rgba</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.1</span>);
}

<span class="hljs-comment">/* Agent Styles */</span>
<span class="hljs-selector-class">.agent-avatar-placeholder</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">80px</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">80px</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">50%</span>;
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#6c757d</span>;
  <span class="hljs-attribute">color</span>: white;
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">justify-content</span>: center;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">24px</span>;
  <span class="hljs-attribute">font-weight</span>: bold;
  <span class="hljs-attribute">margin</span>: <span class="hljs-number">0</span> auto;
  <span class="hljs-attribute">border</span>: <span class="hljs-number">3px</span> solid <span class="hljs-number">#fff</span>;
  <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">2px</span> <span class="hljs-number">4px</span> <span class="hljs-built_in">rgba</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.1</span>);
}

<span class="hljs-selector-class">.avatar-placeholder</span> {
  <span class="hljs-attribute">width</span>: <span class="hljs-number">40px</span>;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">40px</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">50%</span>;
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#6c757d</span>;
  <span class="hljs-attribute">color</span>: white;
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">align-items</span>: center;
  <span class="hljs-attribute">justify-content</span>: center;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">16px</span>;
  <span class="hljs-attribute">font-weight</span>: bold;
}

<span class="hljs-comment">/* Chat Container Styles */</span>
<span class="hljs-selector-class">.chat-container</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">flex-direction</span>: column;
  <span class="hljs-attribute">height</span>: <span class="hljs-number">400px</span>;
  <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#dee2e6</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">0.25rem</span>;
  <span class="hljs-attribute">overflow</span>: hidden;
}

<span class="hljs-selector-class">.chat-messages</span> {
  <span class="hljs-attribute">flex</span>: <span class="hljs-number">1</span>;
  <span class="hljs-attribute">overflow-y</span>: auto;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">1rem</span>;
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#f8f9fa</span>;
}
</code></pre>
<p>And here is the second part:</p>
<pre><code class="lang-css"><span class="hljs-selector-class">.chat-input-container</span> {
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.5rem</span>;
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#fff</span>;
  <span class="hljs-attribute">border-top</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#dee2e6</span>;
}

<span class="hljs-selector-class">.chat-input-group</span> {
  <span class="hljs-attribute">display</span>: flex;
}

<span class="hljs-selector-class">.chat-input</span> {
  <span class="hljs-attribute">flex</span>: <span class="hljs-number">1</span>;
  <span class="hljs-attribute">margin-right</span>: <span class="hljs-number">0.5rem</span>;
  <span class="hljs-attribute">border</span>: <span class="hljs-number">1px</span> solid <span class="hljs-number">#dee2e6</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">0.25rem</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.5rem</span>;
}

<span class="hljs-selector-class">.chat-send-button</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#007bff</span>;
  <span class="hljs-attribute">color</span>: white;
  <span class="hljs-attribute">border</span>: none;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">0.25rem</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.5rem</span> <span class="hljs-number">1rem</span>;
  <span class="hljs-attribute">cursor</span>: pointer;
}

<span class="hljs-selector-class">.chat-send-button</span><span class="hljs-selector-pseudo">:hover</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#0069d9</span>;
}

<span class="hljs-comment">/* Message Styles */</span>
<span class="hljs-selector-class">.message</span> {
  <span class="hljs-attribute">margin-bottom</span>: <span class="hljs-number">1rem</span>;
  <span class="hljs-attribute">max-width</span>: <span class="hljs-number">80%</span>;
}

<span class="hljs-selector-class">.user-message</span> {
  <span class="hljs-attribute">margin-left</span>: auto;
  <span class="hljs-attribute">text-align</span>: right;
}

<span class="hljs-selector-class">.agent-message</span> {
  <span class="hljs-attribute">display</span>: flex;
  <span class="hljs-attribute">align-items</span>: flex-start;
}

<span class="hljs-selector-class">.message-avatar</span> {
  <span class="hljs-attribute">margin-right</span>: <span class="hljs-number">0.5rem</span>;
}

<span class="hljs-selector-class">.message-content</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#fff</span>;
  <span class="hljs-attribute">padding</span>: <span class="hljs-number">0.75rem</span>;
  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">0.5rem</span>;
  <span class="hljs-attribute">box-shadow</span>: <span class="hljs-number">0</span> <span class="hljs-number">1px</span> <span class="hljs-number">2px</span> <span class="hljs-built_in">rgba</span>(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0.05</span>);
}

<span class="hljs-selector-class">.user-message</span> <span class="hljs-selector-class">.message-content</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#007bff</span>;
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#fff</span>;
}

<span class="hljs-selector-class">.agent-message</span> <span class="hljs-selector-class">.message-content</span> {
  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#fff</span>;
}

<span class="hljs-comment">/* Loading Animation */</span>
<span class="hljs-selector-class">.loading-dots</span><span class="hljs-selector-pseudo">:after</span> {
  <span class="hljs-attribute">content</span>: <span class="hljs-string">'.'</span>;
  <span class="hljs-attribute">animation</span>: dots <span class="hljs-number">1.5s</span> <span class="hljs-built_in">steps</span>(<span class="hljs-number">5</span>, end) infinite;
}

<span class="hljs-keyword">@keyframes</span> dots {
  0%,
  20% {
    <span class="hljs-attribute">content</span>: <span class="hljs-string">'.'</span>;
  }
  40% {
    <span class="hljs-attribute">content</span>: <span class="hljs-string">'..'</span>;
  }
  60% {
    <span class="hljs-attribute">content</span>: <span class="hljs-string">'...'</span>;
  }
  80%,
  100% {
    <span class="hljs-attribute">content</span>: <span class="hljs-string">''</span>;
  }
}
</code></pre>
<p>This code has the main styles for the layout of the website’s content. That takes care of the styling. We just have the components and pages left, and then you can run your app. Before we start on those folders, let’s quickly do the <code>App.jsx</code> and <code>main.jsx</code> files in the <code>src</code> folder.</p>
<p>So, add this code to the <code>App.jsx</code> file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { BrowserRouter <span class="hljs-keyword">as</span> Router, Routes, Route } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-router'</span>;
<span class="hljs-keyword">import</span> Layout <span class="hljs-keyword">from</span> <span class="hljs-string">'./components/Layout'</span>;
<span class="hljs-keyword">import</span> Home <span class="hljs-keyword">from</span> <span class="hljs-string">'./pages/Home'</span>;
<span class="hljs-keyword">import</span> Projects <span class="hljs-keyword">from</span> <span class="hljs-string">'./pages/Projects'</span>;
<span class="hljs-keyword">import</span> Career <span class="hljs-keyword">from</span> <span class="hljs-string">'./pages/Career'</span>;
<span class="hljs-keyword">import</span> Services <span class="hljs-keyword">from</span> <span class="hljs-string">'./pages/Services'</span>;
<span class="hljs-keyword">import</span> Research <span class="hljs-keyword">from</span> <span class="hljs-string">'./pages/Research'</span>;
<span class="hljs-keyword">import</span> Contact <span class="hljs-keyword">from</span> <span class="hljs-string">'./pages/Contact'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'./App.css'</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">App</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">Router</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">Layout</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Routes</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Home</span> /&gt;</span>} /&gt;
          <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/projects"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Projects</span> /&gt;</span>} /&gt;
          <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/career"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Career</span> /&gt;</span>} /&gt;
          <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/services"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Services</span> /&gt;</span>} /&gt;
          <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/research"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Research</span> /&gt;</span>} /&gt;
          <span class="hljs-tag">&lt;<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">"/contact"</span> <span class="hljs-attr">element</span>=<span class="hljs-string">{</span>&lt;<span class="hljs-attr">Contact</span> /&gt;</span>} /&gt;
        <span class="hljs-tag">&lt;/<span class="hljs-name">Routes</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">Layout</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">Router</span>&gt;</span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> App;
</code></pre>
<p>In this file, you have all of your routes. This is how you’ll navigate between pages using <code>BrowserRouter</code>.</p>
<p>Finally, replace and update all of the code inside of <code>main.jsx</code> with this:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { StrictMode } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> { createRoot } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-dom/client'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'./index.css'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'./style.css'</span>;
<span class="hljs-keyword">import</span> App <span class="hljs-keyword">from</span> <span class="hljs-string">'./App.jsx'</span>;

createRoot(<span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'root'</span>)).render(
  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">StrictMode</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">App</span> /&gt;</span>
  <span class="hljs-tag">&lt;/<span class="hljs-name">StrictMode</span>&gt;</span></span>
);
</code></pre>
<p>The only update we did here was add an import for <code>import './style.css'</code> so now you can access the styles from this file across your application.</p>
<p>Time to work on your component files, starting with the <code>Chat.jsx</code> file. I split the codebase because it’s a big file, so make sure you add it all together.</p>
<p>Like before, here is the first part:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useState, useEffect, useRef, useCallback } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">"axios"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Chat</span>(<span class="hljs-params">{ agentType, initialMessage, agentInitials, directQuestion }</span>) </span>{
  <span class="hljs-keyword">const</span> [messages, setMessages] = useState([]);
  <span class="hljs-keyword">const</span> [input, setInput] = useState(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [isLoading, setIsLoading] = useState(<span class="hljs-literal">false</span>);
  <span class="hljs-keyword">const</span> messagesEndRef = useRef(<span class="hljs-literal">null</span>);
  <span class="hljs-keyword">const</span> [processedQuestions, setProcessedQuestions] = useState([]);

  <span class="hljs-keyword">const</span> API_BASE_URL = <span class="hljs-string">"http://127.0.0.1:5001"</span>;

  <span class="hljs-keyword">const</span> scrollToBottom = <span class="hljs-function">() =&gt;</span> {
    messagesEndRef.current?.scrollIntoView({ <span class="hljs-attr">behavior</span>: <span class="hljs-string">"smooth"</span> });
  };

  <span class="hljs-keyword">const</span> handleSendMessage = useCallback(
    <span class="hljs-keyword">async</span> (questionOverride = <span class="hljs-literal">null</span>) =&gt; {
      <span class="hljs-keyword">const</span> messageToSend = questionOverride || input;

      <span class="hljs-keyword">if</span> (!messageToSend.trim()) <span class="hljs-keyword">return</span>;

      <span class="hljs-keyword">const</span> userMessage = {
        <span class="hljs-attr">content</span>: messageToSend,
        <span class="hljs-attr">isUser</span>: <span class="hljs-literal">true</span>,
      };

      setMessages(<span class="hljs-function">(<span class="hljs-params">prev</span>) =&gt;</span> [...prev, userMessage]);

      <span class="hljs-keyword">if</span> (!questionOverride) {
        setInput(<span class="hljs-string">""</span>);
      }

      setIsLoading(<span class="hljs-literal">true</span>);

      <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> axios.post(
          <span class="hljs-string">`<span class="hljs-subst">${API_BASE_URL}</span>/api/<span class="hljs-subst">${agentType}</span>`</span>,
          {
            <span class="hljs-attr">message</span>: messageToSend,
          },
          {
            <span class="hljs-attr">headers</span>: {
              <span class="hljs-string">"Content-Type"</span>: <span class="hljs-string">"application/json"</span>,
              <span class="hljs-string">"Access-Control-Allow-Origin"</span>: <span class="hljs-string">"*"</span>,
            },
          }
        );

        <span class="hljs-keyword">if</span> (response.data &amp;&amp; response.data.response) {
          setMessages(<span class="hljs-function">(<span class="hljs-params">prev</span>) =&gt;</span> [
            ...prev,
            {
              <span class="hljs-attr">content</span>: response.data.response,
              <span class="hljs-attr">isUser</span>: <span class="hljs-literal">false</span>,
            },
          ]);
        }
      } <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Error sending message:"</span>, error);
        setMessages(<span class="hljs-function">(<span class="hljs-params">prev</span>) =&gt;</span> [
          ...prev,
          {
            <span class="hljs-attr">content</span>:
              <span class="hljs-string">"Sorry, there was an error connecting to the AI agent. Please make sure the Flask server is running at http://127.0.0.1:5001/"</span>,
            <span class="hljs-attr">isUser</span>: <span class="hljs-literal">false</span>,
          },
        ]);
      } <span class="hljs-keyword">finally</span> {
        setIsLoading(<span class="hljs-literal">false</span>);
      }
    },
    [input, agentType, API_BASE_URL]
  );

  <span class="hljs-keyword">const</span> handleKeyPress = <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
    <span class="hljs-keyword">if</span> (e.key === <span class="hljs-string">"Enter"</span>) {
      handleSendMessage();
    }
  };

  <span class="hljs-keyword">const</span> cleanQuestion = <span class="hljs-function">(<span class="hljs-params">question</span>) =&gt;</span> {
    <span class="hljs-keyword">return</span> question.replace(<span class="hljs-regexp">/\s*\[\d+\]\s*$/</span>, <span class="hljs-string">""</span>);
  };

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (initialMessage) {
      setMessages([
        {
          <span class="hljs-attr">content</span>: initialMessage,
          <span class="hljs-attr">isUser</span>: <span class="hljs-literal">false</span>,
        },
      ]);
    }
  }, [initialMessage]);

  useEffect(<span class="hljs-function">() =&gt;</span> {
    scrollToBottom();
  }, [messages]);
</code></pre>
<p>The first part of this code has your imports, base URL to connect to the backend, and the functions.</p>
<p>Now let’s add the second part of the codebase:</p>
<pre><code class="lang-javascript">  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">if</span> (
      directQuestion &amp;&amp;
      directQuestion.trim() !== <span class="hljs-string">""</span> &amp;&amp;
      !processedQuestions.includes(directQuestion)
    ) {
      <span class="hljs-keyword">const</span> cleanedQuestion = cleanQuestion(directQuestion);
      setInput(cleanedQuestion);
      handleSendMessage(cleanedQuestion);
      setProcessedQuestions(<span class="hljs-function">(<span class="hljs-params">prev</span>) =&gt;</span> [...prev, directQuestion]);
    }
  }, [directQuestion, processedQuestions, handleSendMessage]);

  <span class="hljs-keyword">const</span> renderContent = <span class="hljs-function">(<span class="hljs-params">content</span>) =&gt;</span> {
    <span class="hljs-keyword">let</span> formattedContent = content;

    formattedContent = formattedContent.replace(
      <span class="hljs-regexp">/#{6}\s+(.*?)(?=\n|$)/g</span>,
      <span class="hljs-string">"&lt;h6&gt;$1&lt;/h6&gt;"</span>
    );
    formattedContent = formattedContent.replace(
      <span class="hljs-regexp">/#{5}\s+(.*?)(?=\n|$)/g</span>,
      <span class="hljs-string">"&lt;h5&gt;$1&lt;/h5&gt;"</span>
    );
    formattedContent = formattedContent.replace(
      <span class="hljs-regexp">/#{4}\s+(.*?)(?=\n|$)/g</span>,
      <span class="hljs-string">"&lt;h4&gt;$1&lt;/h4&gt;"</span>
    );
    formattedContent = formattedContent.replace(
      <span class="hljs-regexp">/#{3}\s+(.*?)(?=\n|$)/g</span>,
      <span class="hljs-string">"&lt;h3&gt;$1&lt;/h3&gt;"</span>
    );
    formattedContent = formattedContent.replace(
      <span class="hljs-regexp">/#{2}\s+(.*?)(?=\n|$)/g</span>,
      <span class="hljs-string">"&lt;h2&gt;$1&lt;/h2&gt;"</span>
    );
    formattedContent = formattedContent.replace(
      <span class="hljs-regexp">/#{1}\s+(.*?)(?=\n|$)/g</span>,
      <span class="hljs-string">"&lt;h1&gt;$1&lt;/h1&gt;"</span>
    );

    formattedContent = formattedContent.replace(
      <span class="hljs-regexp">/\*\*(.*?)\*\*/g</span>,
      <span class="hljs-string">"&lt;strong&gt;$1&lt;/strong&gt;"</span>
    );

    formattedContent = formattedContent.replace(<span class="hljs-regexp">/\*(.*?)\*/g</span>, <span class="hljs-string">"&lt;em&gt;$1&lt;/em&gt;"</span>);

    formattedContent = formattedContent.replace(<span class="hljs-regexp">/`(.*?)`/g</span>, <span class="hljs-string">"&lt;code&gt;$1&lt;/code&gt;"</span>);

    formattedContent = formattedContent.replace(
      <span class="hljs-regexp">/\[(.*?)\]\((.*?)\)/g</span>,
      <span class="hljs-string">'&lt;a href="$2" target="_blank"&gt;$1&lt;/a&gt;'</span>
    );

    formattedContent = formattedContent.replace(
      <span class="hljs-regexp">/^\s*\*\s+(.*?)(?=\n|$)/gm</span>,
      <span class="hljs-string">"&lt;li&gt;$1&lt;/li&gt;"</span>
    );
    formattedContent = formattedContent.replace(
      <span class="hljs-regexp">/&lt;li&gt;(.*?)&lt;\/li&gt;(?:\s*&lt;li&gt;.*?&lt;\/li&gt;)*/g</span>,
      <span class="hljs-string">"&lt;ul&gt;$&amp;&lt;/ul&gt;"</span>
    );

    formattedContent = formattedContent.replace(
      <span class="hljs-regexp">/^\s*\d+\.\s+(.*?)(?=\n|$)/gm</span>,
      <span class="hljs-string">"&lt;li&gt;$1&lt;/li&gt;"</span>
    );
    formattedContent = formattedContent.replace(
      <span class="hljs-regexp">/&lt;li&gt;(.*?)&lt;\/li&gt;(?:\s*&lt;li&gt;.*?&lt;\/li&gt;)*/g</span>,
      <span class="hljs-string">"&lt;ol&gt;$&amp;&lt;/ol&gt;"</span>
    );

    <span class="hljs-keyword">return</span> { <span class="hljs-attr">__html</span>: formattedContent };
  };

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"chat-container"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"chat-messages"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">{</span>`${<span class="hljs-attr">agentType</span>}<span class="hljs-attr">-messages</span>`}&gt;</span>
        {messages.map((message, index) =&gt; (
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span>
            <span class="hljs-attr">key</span>=<span class="hljs-string">{index}</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">message</span> ${
              <span class="hljs-attr">message.isUser</span> ? "<span class="hljs-attr">user-message</span>" <span class="hljs-attr">:</span> "<span class="hljs-attr">agent-message</span>"
            }`}
          &gt;</span>
            {!message.isUser &amp;&amp; (
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"message-avatar"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"avatar-placeholder"</span>&gt;</span>
                  {agentInitials || "AI"}
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            )}
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"message-content"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">dangerouslySetInnerHTML</span>=<span class="hljs-string">{renderContent(message.content)}</span> /&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        ))}
        {isLoading &amp;&amp; (
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"message agent-message"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"message-avatar"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"avatar-placeholder"</span>&gt;</span>{agentInitials || "AI"}<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"message-content"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"loading-dots"</span>&gt;</span>Thinking<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        )}
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">ref</span>=<span class="hljs-string">{messagesEndRef}</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"chat-input-container"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"chat-input-group"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
            <span class="hljs-attr">id</span>=<span class="hljs-string">{</span>`${<span class="hljs-attr">agentType</span>}<span class="hljs-attr">-input</span>`}
            <span class="hljs-attr">className</span>=<span class="hljs-string">"chat-input"</span>
            <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Type your message..."</span>
            <span class="hljs-attr">value</span>=<span class="hljs-string">{input}</span>
            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setInput(e.target.value)}
            onKeyPress={handleKeyPress}
          /&gt;
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
            <span class="hljs-attr">id</span>=<span class="hljs-string">{</span>`${<span class="hljs-attr">agentType</span>}<span class="hljs-attr">-send</span>`}
            <span class="hljs-attr">className</span>=<span class="hljs-string">"chat-send-button"</span>
            <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> handleSendMessage()}
          &gt;
            <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"fa-solid fa-paper-plane mr-2"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>Send
          <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Chat;
</code></pre>
<p>The second part of the code mostly has the JSX for the components.</p>
<p>Right, next let’s do the <code>Footer.jsx</code> file by adding this code to the file:</p>
<pre><code class="lang-javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Footer</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">footer</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-dark text-white py-4"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"container"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"row"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"col-md-6"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h5</span>&gt;</span>Portfolio<span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>Showcasing my work with the help of AI agents<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"col-md-6 text-md-end"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h5</span>&gt;</span>Connect<span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"social-links"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-white me-2"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-white me-2"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">a</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-white me-2"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"row mt-3"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"col-12 text-center"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mb-0"</span>&gt;</span>
              <span class="hljs-symbol">&amp;copy;</span> {new Date().getFullYear()} Portfolio. All rights reserved.
            <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">footer</span>&gt;</span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Footer;
</code></pre>
<p>The code is pretty much self-explanatory – it has some contact details which will show up at the bottom of your page in the footer section.</p>
<p>Now we can work on the <code>Layout.jsx</code>. I have also split it into two parts.</p>
<p>Add the first part of the codebase here:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { Link, useLocation } <span class="hljs-keyword">from</span> <span class="hljs-string">"react-router"</span>;
<span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Layout</span>(<span class="hljs-params">{ children }</span>) </span>{
  <span class="hljs-keyword">const</span> location = useLocation();
  <span class="hljs-keyword">const</span> [isMenuOpen, setIsMenuOpen] = useState(<span class="hljs-literal">false</span>);

  <span class="hljs-keyword">return</span> (
    &lt;div className="flex flex-col min-h-screen"&gt;
      &lt;nav className="bg-gray-800 text-white"&gt;
        &lt;div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"&gt;
          &lt;div className="flex justify-between h-16"&gt;
            &lt;div className="flex items-center"&gt;
              &lt;Link className="text-xl font-bold" to="/"&gt;
                Portfolio
              &lt;/Link&gt;
            &lt;/div&gt;
            &lt;div className="hidden md:block"&gt;
              &lt;div className="ml-10 flex items-center space-x-4"&gt;
                &lt;Link
                  className={`px-3 py-2 rounded-md text-sm font-medium ${
                    location.pathname === "/"
                      ? "bg-gray-900 text-white"
                      : "text-gray-300 hover:bg-gray-700 hover:text-white"
                  }`}
                  to="/"
                &gt;
                  Home
                &lt;/Link&gt;
                &lt;Link
                  className={`px-3 py-2 rounded-md text-sm font-medium ${
                    location.pathname === "/projects"
                      ? "bg-gray-900 text-white"
                      : "text-gray-300 hover:bg-gray-700 hover:text-white"
                  }`}
                  to="/projects"
                &gt;
                  Projects
                &lt;/Link&gt;
                &lt;Link
                  className={`px-3 py-2 rounded-md text-sm font-medium ${
                    location.pathname === "/career"
                      ? "bg-gray-900 text-white"
                      : "text-gray-300 hover:bg-gray-700 hover:text-white"
                  }`}
                  to="/career"
                &gt;
                  Career
                &lt;/Link&gt;
                &lt;Link
                  className={`px-3 py-2 rounded-md text-sm font-medium ${
                    location.pathname === "/services"
                      ? "bg-gray-900 text-white"
                      : "text-gray-300 hover:bg-gray-700 hover:text-white"
                  }`}
                  to="/services"
                &gt;
                  Services
                &lt;/Link&gt;
                &lt;Link
                  className={`px-3 py-2 rounded-md text-sm font-medium ${
                    location.pathname === "/research"
                      ? "bg-gray-900 text-white"
                      : "text-gray-300 hover:bg-gray-700 hover:text-white"
                  }`}
                  to="/research"
                &gt;
                  Research
                &lt;/Link&gt;
                &lt;Link
                  className={`px-3 py-2 rounded-md text-sm font-medium ${
                    location.pathname === "/contact"
                      ? "bg-gray-900 text-white"
                      : "text-gray-300 hover:bg-gray-700 hover:text-white"
                  }`}
                  to="/contact"
                &gt;
                  Contact
                &lt;/Link&gt;
              &lt;/div&gt;
            &lt;/div&gt;
            &lt;div className="md:hidden flex items-center"&gt;
              &lt;button
                onClick={() =&gt; setIsMenuOpen(!isMenuOpen)}
                className="inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-white hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white"
              &gt;
                &lt;span className="sr-only"&gt;Open main menu&lt;/span&gt;
                {isMenuOpen ? (
                  &lt;svg
                    className="block h-6 w-6"
                    xmlns="http://www.w3.org/2000/svg"
                    fill="none"
                    viewBox="0 0 24 24"
                    stroke="currentColor"
                    aria-hidden="true"
                  &gt;
                    &lt;path
                      strokeLinecap="round"
                      strokeLinejoin="round"
                      strokeWidth="2"
                      d="M6 18L18 6M6 6l12 12"
                    /&gt;
                  &lt;/svg&gt;
                ) : (
                  &lt;svg
                    className="block h-6 w-6"
                    xmlns="http://www.w3.org/2000/svg"
                    fill="none"
                    viewBox="0 0 24 24"
                    stroke="currentColor"
                    aria-hidden="true"
                  &gt;
                    &lt;path
                      strokeLinecap="round"
                      strokeLinejoin="round"
                      strokeWidth="2"
                      d="M4 6h16M4 12h16M4 18h16"
                    /&gt;
                  &lt;/svg&gt;
                )}
              &lt;/button&gt;
            &lt;/div&gt;
          &lt;/div&gt;
        &lt;/div&gt;
</code></pre>
<p>This part of the code has a lot of components, as expected for the layout.</p>
<p>Here is the second part of the code to be added to the file:</p>
<pre><code class="lang-javascript">        {<span class="hljs-comment">/* Mobile menu, show/hide based on menu state */</span>}
        {isMenuOpen &amp;&amp; (
          <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"md:hidden"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"px-2 pt-2 pb-3 space-y-1 sm:px-3"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">Link</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">block</span> <span class="hljs-attr">px-3</span> <span class="hljs-attr">py-2</span> <span class="hljs-attr">rounded-md</span> <span class="hljs-attr">text-base</span> <span class="hljs-attr">font-medium</span> ${
                  <span class="hljs-attr">location.pathname</span> === <span class="hljs-string">"/"</span>
                    ? "<span class="hljs-attr">bg-gray-900</span> <span class="hljs-attr">text-white</span>"
                    <span class="hljs-attr">:</span> "<span class="hljs-attr">text-gray-300</span> <span class="hljs-attr">hover:bg-gray-700</span> <span class="hljs-attr">hover:text-white</span>"
                }`}
                <span class="hljs-attr">to</span>=<span class="hljs-string">"/"</span>
                <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setIsMenuOpen(false)}
              &gt;
                Home
              <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">Link</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">block</span> <span class="hljs-attr">px-3</span> <span class="hljs-attr">py-2</span> <span class="hljs-attr">rounded-md</span> <span class="hljs-attr">text-base</span> <span class="hljs-attr">font-medium</span> ${
                  <span class="hljs-attr">location.pathname</span> === <span class="hljs-string">"/projects"</span>
                    ? "<span class="hljs-attr">bg-gray-900</span> <span class="hljs-attr">text-white</span>"
                    <span class="hljs-attr">:</span> "<span class="hljs-attr">text-gray-300</span> <span class="hljs-attr">hover:bg-gray-700</span> <span class="hljs-attr">hover:text-white</span>"
                }`}
                <span class="hljs-attr">to</span>=<span class="hljs-string">"/projects"</span>
                <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setIsMenuOpen(false)}
              &gt;
                Projects
              <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">Link</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">block</span> <span class="hljs-attr">px-3</span> <span class="hljs-attr">py-2</span> <span class="hljs-attr">rounded-md</span> <span class="hljs-attr">text-base</span> <span class="hljs-attr">font-medium</span> ${
                  <span class="hljs-attr">location.pathname</span> === <span class="hljs-string">"/career"</span>
                    ? "<span class="hljs-attr">bg-gray-900</span> <span class="hljs-attr">text-white</span>"
                    <span class="hljs-attr">:</span> "<span class="hljs-attr">text-gray-300</span> <span class="hljs-attr">hover:bg-gray-700</span> <span class="hljs-attr">hover:text-white</span>"
                }`}
                <span class="hljs-attr">to</span>=<span class="hljs-string">"/career"</span>
                <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setIsMenuOpen(false)}
              &gt;
                Career
              <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">Link</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">block</span> <span class="hljs-attr">px-3</span> <span class="hljs-attr">py-2</span> <span class="hljs-attr">rounded-md</span> <span class="hljs-attr">text-base</span> <span class="hljs-attr">font-medium</span> ${
                  <span class="hljs-attr">location.pathname</span> === <span class="hljs-string">"/services"</span>
                    ? "<span class="hljs-attr">bg-gray-900</span> <span class="hljs-attr">text-white</span>"
                    <span class="hljs-attr">:</span> "<span class="hljs-attr">text-gray-300</span> <span class="hljs-attr">hover:bg-gray-700</span> <span class="hljs-attr">hover:text-white</span>"
                }`}
                <span class="hljs-attr">to</span>=<span class="hljs-string">"/services"</span>
                <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setIsMenuOpen(false)}
              &gt;
                Services
              <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">Link</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">block</span> <span class="hljs-attr">px-3</span> <span class="hljs-attr">py-2</span> <span class="hljs-attr">rounded-md</span> <span class="hljs-attr">text-base</span> <span class="hljs-attr">font-medium</span> ${
                  <span class="hljs-attr">location.pathname</span> === <span class="hljs-string">"/research"</span>
                    ? "<span class="hljs-attr">bg-gray-900</span> <span class="hljs-attr">text-white</span>"
                    <span class="hljs-attr">:</span> "<span class="hljs-attr">text-gray-300</span> <span class="hljs-attr">hover:bg-gray-700</span> <span class="hljs-attr">hover:text-white</span>"
                }`}
                <span class="hljs-attr">to</span>=<span class="hljs-string">"/research"</span>
                <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setIsMenuOpen(false)}
              &gt;
                Research
              <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">Link</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">block</span> <span class="hljs-attr">px-3</span> <span class="hljs-attr">py-2</span> <span class="hljs-attr">rounded-md</span> <span class="hljs-attr">text-base</span> <span class="hljs-attr">font-medium</span> ${
                  <span class="hljs-attr">location.pathname</span> === <span class="hljs-string">"/contact"</span>
                    ? "<span class="hljs-attr">bg-gray-900</span> <span class="hljs-attr">text-white</span>"
                    <span class="hljs-attr">:</span> "<span class="hljs-attr">text-gray-300</span> <span class="hljs-attr">hover:bg-gray-700</span> <span class="hljs-attr">hover:text-white</span>"
                }`}
                <span class="hljs-attr">to</span>=<span class="hljs-string">"/contact"</span>
                <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> setIsMenuOpen(false)}
              &gt;
                Contact
              <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
        )}
      &lt;/nav&gt;

      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">main</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex-grow max-w-7xl w-full mx-auto px-4 sm:px-6 lg:px-8 py-8"</span>&gt;</span>
        {children}
      <span class="hljs-tag">&lt;/<span class="hljs-name">main</span>&gt;</span></span>

      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">footer</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-gray-800 text-white py-8"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"md:flex md:justify-between"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mb-8 md:mb-0"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h5</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-lg font-semibold mb-2"</span>&gt;</span>Portfolio<span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-300"</span>&gt;</span>
                Showcasing my work with the help of AI agents
              <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mt-8 border-t border-gray-700 pt-8 text-center"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-300"</span>&gt;</span>
              <span class="hljs-symbol">&amp;copy;</span> 2025 Portfolio. All rights reserved.
            <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">footer</span>&gt;</span></span>
    &lt;/div&gt;
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Layout;
</code></pre>
<p>This code has more components, which completes the Layout component.</p>
<p>We’re almost done. Now for the last component, <code>Navbar.jsx</code>, before we move on to the pages.</p>
<p>This is the code you need for the file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { Link, useLocation } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-router'</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Navbar</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> location = useLocation();

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">nav</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"navbar navbar-expand-lg navbar-dark bg-dark"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"container"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">Link</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"navbar-brand"</span> <span class="hljs-attr">to</span>=<span class="hljs-string">"/"</span>&gt;</span>
          Portfolio
        <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
          <span class="hljs-attr">className</span>=<span class="hljs-string">"navbar-toggler"</span>
          <span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span>
          <span class="hljs-attr">data-bs-toggle</span>=<span class="hljs-string">"collapse"</span>
          <span class="hljs-attr">data-bs-target</span>=<span class="hljs-string">"#navbarNav"</span>
        &gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"navbar-toggler-icon"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"collapse navbar-collapse"</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"navbarNav"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"navbar-nav ms-auto"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"nav-item"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">Link</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">nav-link</span> ${
                  <span class="hljs-attr">location.pathname</span> === <span class="hljs-string">'/'</span> ? '<span class="hljs-attr">active</span>' <span class="hljs-attr">:</span> ''
                }`}
                <span class="hljs-attr">to</span>=<span class="hljs-string">"/"</span>
              &gt;</span>
                Home
              <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"nav-item"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">Link</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">nav-link</span> ${
                  <span class="hljs-attr">location.pathname</span> === <span class="hljs-string">'/projects'</span> ? '<span class="hljs-attr">active</span>' <span class="hljs-attr">:</span> ''
                }`}
                <span class="hljs-attr">to</span>=<span class="hljs-string">"/projects"</span>
              &gt;</span>
                Projects
              <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"nav-item"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">Link</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">nav-link</span> ${
                  <span class="hljs-attr">location.pathname</span> === <span class="hljs-string">'/career'</span> ? '<span class="hljs-attr">active</span>' <span class="hljs-attr">:</span> ''
                }`}
                <span class="hljs-attr">to</span>=<span class="hljs-string">"/career"</span>
              &gt;</span>
                Career
              <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"nav-item"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">Link</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">nav-link</span> ${
                  <span class="hljs-attr">location.pathname</span> === <span class="hljs-string">'/services'</span> ? '<span class="hljs-attr">active</span>' <span class="hljs-attr">:</span> ''
                }`}
                <span class="hljs-attr">to</span>=<span class="hljs-string">"/services"</span>
              &gt;</span>
                Services
              <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"nav-item"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">Link</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">nav-link</span> ${
                  <span class="hljs-attr">location.pathname</span> === <span class="hljs-string">'/research'</span> ? '<span class="hljs-attr">active</span>' <span class="hljs-attr">:</span> ''
                }`}
                <span class="hljs-attr">to</span>=<span class="hljs-string">"/research"</span>
              &gt;</span>
                Research
              <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"nav-item"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">Link</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">nav-link</span> ${
                  <span class="hljs-attr">location.pathname</span> === <span class="hljs-string">'/contact'</span> ? '<span class="hljs-attr">active</span>' <span class="hljs-attr">:</span> ''
                }`}
                <span class="hljs-attr">to</span>=<span class="hljs-string">"/contact"</span>
              &gt;</span>
                Contact
              <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">nav</span>&gt;</span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Navbar;
</code></pre>
<p>The navbar component has your navigation links, which lets you navigate between pages using <code>react-router</code>.</p>
<p>Alright, the component codebase is ready! All that remains is the six page routes in our pages folder.</p>
<p>The first file we’ll work on will be the <code>Career.jsx</code> file. I will split the codebase for readability like before, so copy the different sections starting with the first part here:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> Chat <span class="hljs-keyword">from</span> <span class="hljs-string">"../components/Chat"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Career</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> initialMessage =
    <span class="hljs-string">"Hello! I'm CareerAgent, the career specialist. I can provide information about skills, experience, and professional background. What would you like to know?"</span>;

  <span class="hljs-keyword">const</span> [currentQuestion, setCurrentQuestion] = useState(<span class="hljs-string">""</span>);

  <span class="hljs-keyword">const</span> askCareerQuestion = <span class="hljs-function">(<span class="hljs-params">question</span>) =&gt;</span> {
    setCurrentQuestion(<span class="hljs-string">`<span class="hljs-subst">${question}</span> [<span class="hljs-subst">${<span class="hljs-built_in">Date</span>.now()}</span>]`</span>);

    <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
      setCurrentQuestion(<span class="hljs-string">""</span>);
    }, <span class="hljs-number">500</span>);
  };

  <span class="hljs-keyword">return</span> (
    &lt;div&gt;
      &lt;div className="flex flex-col md:flex-row gap-8 mb-12"&gt;
        &lt;div className="md:w-1/3"&gt;
          &lt;h1 className="text-3xl font-bold mb-4"&gt;Career&lt;/h1&gt;
          &lt;p className="text-lg mb-4"&gt;
            Here you can find information about my professional background,
            skills, and experience. Feel free to ask CareerAgent for more
            details.
          &lt;/p&gt;
        &lt;/div&gt;
        &lt;div className="md:w-2/3"&gt;
          &lt;div className="bg-white rounded-lg shadow-md overflow-hidden"&gt;
            &lt;div className="p-6"&gt;
              &lt;h5 className="text-xl font-semibold mb-2"&gt;
                Chat with CareerAgent
              &lt;/h5&gt;
              &lt;p className="text-gray-600 mb-4"&gt;
                Our career specialist can provide information about skills,
                experience, and professional background.
              &lt;/p&gt;
              &lt;Chat
                agentType="career"
                initialMessage={initialMessage}
                agentInitials="CA"
                directQuestion={currentQuestion}
              /&gt;
            &lt;/div&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div className="mb-12"&gt;
        &lt;div className="mb-6"&gt;
          &lt;h2 className="text-2xl font-bold mb-4"&gt;Skills&lt;/h2&gt;
        &lt;/div&gt;
        &lt;div className="grid grid-cols-1 md:grid-cols-3 gap-6"&gt;
          &lt;div className="bg-white rounded-lg shadow-md overflow-hidden h-full"&gt;
            &lt;div className="p-6"&gt;
              &lt;h5 className="text-xl font-semibold mb-4"&gt;
                Frontend Development
              &lt;/h5&gt;
              &lt;ul className="divide-y divide-gray-200"&gt;
                &lt;li className="py-3 flex justify-between items-center"&gt;
                  React
                  &lt;span className="px-2.5 py-0.5 bg-blue-500 text-white text-xs font-medium rounded-full"&gt;
                    Expert
                  &lt;/span&gt;
                &lt;/li&gt;
                &lt;li className="py-3 flex justify-between items-center"&gt;
                  Vue.js
                  &lt;span className="px-2.5 py-0.5 bg-blue-500 text-white text-xs font-medium rounded-full"&gt;
                    Advanced
                  &lt;/span&gt;
                &lt;/li&gt;
                &lt;li className="py-3 flex justify-between items-center"&gt;
                  Angular
                  &lt;span className="px-2.5 py-0.5 bg-blue-500 text-white text-xs font-medium rounded-full"&gt;
                    Intermediate
                  &lt;/span&gt;
                &lt;/li&gt;
                &lt;li className="py-3 flex justify-between items-center"&gt;
                  TypeScript
                  &lt;span className="px-2.5 py-0.5 bg-blue-500 text-white text-xs font-medium rounded-full"&gt;
                    Advanced
                  &lt;/span&gt;
                &lt;/li&gt;
                &lt;li className="py-3 flex justify-between items-center"&gt;
                  CSS/SASS
                  &lt;span className="px-2.5 py-0.5 bg-blue-500 text-white text-xs font-medium rounded-full"&gt;
                    Expert
                  &lt;/span&gt;
                &lt;/li&gt;
              &lt;/ul&gt;
              &lt;button
                className="mt-4 py-1.5 px-3 text-sm border border-blue-500 text-blue-500 rounded-md hover:bg-blue-50 transition-colors"
                onClick={() =&gt;
                  askCareerQuestion(
                    "Tell me more about your frontend development skills"
                  )
                }
              &gt;
                Ask About Frontend Skills
              &lt;/button&gt;
</code></pre>
<p>Like before, we have imports, states, and some components. Now for the second part, which is here:</p>
<pre><code class="lang-javascript"> &lt;/div&gt;
          &lt;/div&gt;
          <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-white rounded-lg shadow-md overflow-hidden h-full"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-6"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h5</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl font-semibold mb-4"</span>&gt;</span>
                Backend Development
              <span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"divide-y divide-gray-200"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"py-3 flex justify-between items-center"</span>&gt;</span>
                  Node.js
                  <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"px-2.5 py-0.5 bg-blue-500 text-white text-xs font-medium rounded-full"</span>&gt;</span>
                    Expert
                  <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"py-3 flex justify-between items-center"</span>&gt;</span>
                  Python
                  <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"px-2.5 py-0.5 bg-blue-500 text-white text-xs font-medium rounded-full"</span>&gt;</span>
                    Advanced
                  <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"py-3 flex justify-between items-center"</span>&gt;</span>
                  Django
                  <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"px-2.5 py-0.5 bg-blue-500 text-white text-xs font-medium rounded-full"</span>&gt;</span>
                    Intermediate
                  <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"py-3 flex justify-between items-center"</span>&gt;</span>
                  Flask
                  <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"px-2.5 py-0.5 bg-blue-500 text-white text-xs font-medium rounded-full"</span>&gt;</span>
                    Advanced
                  <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"py-3 flex justify-between items-center"</span>&gt;</span>
                  SQL/NoSQL
                  <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"px-2.5 py-0.5 bg-blue-500 text-white text-xs font-medium rounded-full"</span>&gt;</span>
                    Advanced
                  <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">"mt-4 py-1.5 px-3 text-sm border border-blue-500 text-blue-500 rounded-md hover:bg-blue-50 transition-colors"</span>
                <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span>
                  askCareerQuestion(
                    "Tell me more about your backend development skills"
                  )
                }
              &gt;
                Ask About Backend Skills
              <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
          <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-white rounded-lg shadow-md overflow-hidden h-full"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-6"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h5</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl font-semibold mb-4"</span>&gt;</span>Other Skills<span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"divide-y divide-gray-200"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"py-3 flex justify-between items-center"</span>&gt;</span>
                  DevOps
                  <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"px-2.5 py-0.5 bg-blue-500 text-white text-xs font-medium rounded-full"</span>&gt;</span>
                    Intermediate
                  <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"py-3 flex justify-between items-center"</span>&gt;</span>
                  UI/UX Design
                  <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"px-2.5 py-0.5 bg-blue-500 text-white text-xs font-medium rounded-full"</span>&gt;</span>
                    Advanced
                  <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"py-3 flex justify-between items-center"</span>&gt;</span>
                  Project Management
                  <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"px-2.5 py-0.5 bg-blue-500 text-white text-xs font-medium rounded-full"</span>&gt;</span>
                    Advanced
                  <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"py-3 flex justify-between items-center"</span>&gt;</span>
                  Agile Methodologies
                  <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"px-2.5 py-0.5 bg-blue-500 text-white text-xs font-medium rounded-full"</span>&gt;</span>
                    Expert
                  <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"py-3 flex justify-between items-center"</span>&gt;</span>
                  Technical Writing
                  <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"px-2.5 py-0.5 bg-blue-500 text-white text-xs font-medium rounded-full"</span>&gt;</span>
                    Intermediate
                  <span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">"mt-4 py-1.5 px-3 text-sm border border-blue-500 text-blue-500 rounded-md hover:bg-blue-50 transition-colors"</span>
                <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span>
                  askCareerQuestion("What other skills do you have?")
                }
              &gt;
                Ask About Other Skills
              <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
        &lt;/div&gt;
      &lt;/div&gt;
</code></pre>
<p>There is a lot more component code here for the career page. Lastly, lets add the last part of the code for this page:</p>
<pre><code class="lang-javascript">&lt;div className=<span class="hljs-string">"mb-12"</span>&gt;
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mb-6"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-2xl font-bold mb-4"</span>&gt;</span>Experience<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"space-y-6"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-white rounded-lg shadow-md overflow-hidden"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-6"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex justify-between items-start mb-2"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">h5</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl font-semibold"</span>&gt;</span>
                  Senior Full-Stack Developer
                <span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-500 text-sm"</span>&gt;</span>2020 - Present<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h6</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-600 mb-3"</span>&gt;</span>Tech Innovations Inc.<span class="hljs-tag">&lt;/<span class="hljs-name">h6</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-700 mb-4"</span>&gt;</span>
                Lead developer for multiple web and mobile applications,
                managing a team of 5 developers. Implemented CI/CD pipelines and
                improved development workflow efficiency by 30%.
              <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">"py-1.5 px-3 text-sm border border-blue-500 text-blue-500 rounded-md hover:bg-blue-50 transition-colors"</span>
                <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span>
                  askCareerQuestion(
                    "Tell me more about your experience at Tech Innovations Inc."
                  )
                }
              &gt;
                More Details
              <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-white rounded-lg shadow-md overflow-hidden"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-6"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex justify-between items-start mb-2"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">h5</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl font-semibold"</span>&gt;</span>Full-Stack Developer<span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-500 text-sm"</span>&gt;</span>2017 - 2020<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h6</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-600 mb-3"</span>&gt;</span>WebSolutions Co.<span class="hljs-tag">&lt;/<span class="hljs-name">h6</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-700 mb-4"</span>&gt;</span>
                Developed and maintained multiple client websites and web
                applications. Specialized in React frontend development and
                Node.js backend services.
              <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">"py-1.5 px-3 text-sm border border-blue-500 text-blue-500 rounded-md hover:bg-blue-50 transition-colors"</span>
                <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span>
                  askCareerQuestion(
                    "Tell me more about your experience at WebSolutions Co."
                  )
                }
              &gt;
                More Details
              <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-white rounded-lg shadow-md overflow-hidden"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-6"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex justify-between items-start mb-2"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">h5</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl font-semibold"</span>&gt;</span>Junior Web Developer<span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-500 text-sm"</span>&gt;</span>2015 - 2017<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h6</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-600 mb-3"</span>&gt;</span>Digital Creations Ltd.<span class="hljs-tag">&lt;/<span class="hljs-name">h6</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-700 mb-4"</span>&gt;</span>
                Worked on frontend development for e-commerce websites. Gained
                experience with JavaScript, CSS, and responsive design
                principles.
              <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">"py-1.5 px-3 text-sm border border-blue-500 text-blue-500 rounded-md hover:bg-blue-50 transition-colors"</span>
                <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span>
                  askCareerQuestion(
                    "Tell me more about your experience at Digital Creations Ltd."
                  )
                }
              &gt;
                More Details
              <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
      &lt;/div&gt;

      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"grid grid-cols-1 md:grid-cols-2 gap-6"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-white rounded-lg shadow-md overflow-hidden"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-6"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h5</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl font-semibold mb-4"</span>&gt;</span>Education<span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mb-6"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex justify-between items-start mb-1"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">h6</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-medium"</span>&gt;</span>
                  Master of Science in Computer Science
                <span class="hljs-tag">&lt;/<span class="hljs-name">h6</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-500 text-sm"</span>&gt;</span>2013 - 2015<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-600"</span>&gt;</span>University of Technology<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex justify-between items-start mb-1"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">h6</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-medium"</span>&gt;</span>
                  Bachelor of Science in Software Engineering
                <span class="hljs-tag">&lt;/<span class="hljs-name">h6</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-500 text-sm"</span>&gt;</span>2009 - 2013<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-600"</span>&gt;</span>State University<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"mt-4 py-1.5 px-3 text-sm border border-blue-500 text-blue-500 rounded-md hover:bg-blue-50 transition-colors"</span>
              <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span>
                askCareerQuestion(
                  "Tell me more about your educational background"
                )
              }
            &gt;
              Ask About Education
            <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-white rounded-lg shadow-md overflow-hidden"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-6"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h5</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl font-semibold mb-4"</span>&gt;</span>Certifications<span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"divide-y divide-gray-200"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"py-3 flex justify-between items-center"</span>&gt;</span>
                AWS Certified Solutions Architect
                <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-500 text-sm"</span>&gt;</span>2022<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"py-3 flex justify-between items-center"</span>&gt;</span>
                Google Cloud Professional Developer
                <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-500 text-sm"</span>&gt;</span>2021<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"py-3 flex justify-between items-center"</span>&gt;</span>
                Microsoft Certified: Azure Developer Associate
                <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-500 text-sm"</span>&gt;</span>2020<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"py-3 flex justify-between items-center"</span>&gt;</span>
                Certified Scrum Master
                <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-500 text-sm"</span>&gt;</span>2019<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"mt-4 py-1.5 px-3 text-sm border border-blue-500 text-blue-500 rounded-md hover:bg-blue-50 transition-colors"</span>
              <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span>
                askCareerQuestion("Tell me more about your certifications")
              }
            &gt;
              Ask About Certifications
            <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    &lt;/div&gt;
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Career;
</code></pre>
<p>And this completes our <code>Career.jsx</code> page: we have forms and more components in this part of the code.</p>
<p>Next is our <code>Contact.jsx</code> page. Like before, I will split the codebase for readability, so add the first part of this code to it:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Contact</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [formData, setFormData] = useState({
    <span class="hljs-attr">name</span>: <span class="hljs-string">""</span>,
    <span class="hljs-attr">email</span>: <span class="hljs-string">""</span>,
    <span class="hljs-attr">subject</span>: <span class="hljs-string">""</span>,
    <span class="hljs-attr">message</span>: <span class="hljs-string">""</span>,
  });
  <span class="hljs-keyword">const</span> [formResponse, setFormResponse] = useState(<span class="hljs-literal">null</span>);

  <span class="hljs-keyword">const</span> handleChange = <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> { id, value } = e.target;
    setFormData(<span class="hljs-function">(<span class="hljs-params">prevData</span>) =&gt;</span> ({
      ...prevData,
      [id]: value,
    }));
  };

  <span class="hljs-keyword">const</span> handleSubmit = <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
    e.preventDefault();

    setFormResponse({
      <span class="hljs-attr">type</span>: <span class="hljs-string">"success"</span>,
      <span class="hljs-attr">message</span>:
        <span class="hljs-string">"Thank you for your message! I'll get back to you as soon as possible."</span>,
    });

    setFormData({
      <span class="hljs-attr">name</span>: <span class="hljs-string">""</span>,
      <span class="hljs-attr">email</span>: <span class="hljs-string">""</span>,
      <span class="hljs-attr">subject</span>: <span class="hljs-string">""</span>,
      <span class="hljs-attr">message</span>: <span class="hljs-string">""</span>,
    });

    <span class="hljs-built_in">document</span>
      .getElementById(<span class="hljs-string">"form-response"</span>)
      .scrollIntoView({ <span class="hljs-attr">behavior</span>: <span class="hljs-string">"smooth"</span> });
  };

  <span class="hljs-keyword">return</span> (
    &lt;div&gt;
      &lt;div className="flex flex-col md:flex-row gap-8 mb-12"&gt;
        &lt;div className="md:w-2/3"&gt;
          &lt;h1 className="text-3xl font-bold mb-4"&gt;Contact Me&lt;/h1&gt;
          &lt;p className="text-lg mb-4"&gt;
            Have a question or want to discuss a potential project? Feel free to
            reach out using the form below or through any of my social media
            channels.
          &lt;/p&gt;
        &lt;/div&gt;
        &lt;div className="md:w-1/3"&gt;
          &lt;div className="bg-white rounded-lg shadow-md overflow-hidden"&gt;
            &lt;div className="p-6"&gt;
              &lt;h5 className="text-xl font-semibold mb-4"&gt;Quick Links&lt;/h5&gt;
              &lt;div className="flex flex-col gap-2"&gt;
                &lt;a
                  href="/projects"
                  className="py-2 px-4 border border-blue-500 text-blue-500 rounded-md hover:bg-blue-50 text-center transition-colors"
                &gt;
                  View Projects
                &lt;/a&gt;
                &lt;a
                  href="/services"
                  className="py-2 px-4 border border-blue-500 text-blue-500 rounded-md hover:bg-blue-50 text-center transition-colors"
                &gt;
                  Services &amp; Pricing
                &lt;/a&gt;
                &lt;a
                  href="/research"
                  className="py-2 px-4 border border-blue-500 text-blue-500 rounded-md hover:bg-blue-50 text-center transition-colors"
                &gt;
                  Research &amp; Resources
                &lt;/a&gt;
              &lt;/div&gt;
            &lt;/div&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-12"&gt;
        &lt;div className="bg-white rounded-lg shadow-md overflow-hidden"&gt;
          &lt;div className="p-6"&gt;
            &lt;h5 className="text-xl font-semibold mb-4"&gt;Contact Form&lt;/h5&gt;
            &lt;form id="contact-form" onSubmit={handleSubmit}&gt;
              &lt;div className="mb-4"&gt;
                &lt;label
                  htmlFor="name"
                  className="block text-sm font-medium text-gray-700 mb-1"
                &gt;
                  Name
                &lt;/label&gt;
                &lt;input
                  type="text"
                  className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
                  id="name"
                  placeholder="Your Name"
                  required
                  value={formData.name}
                  onChange={handleChange}
                /&gt;
              &lt;/div&gt;
</code></pre>
<p>We have our imports, functions, and part of the components here. Lastly, add the second part to complete this page:</p>
<pre><code class="lang-javascript">&lt;div className=<span class="hljs-string">"mb-4"</span>&gt;
                <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">label</span>
                  <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"email"</span>
                  <span class="hljs-attr">className</span>=<span class="hljs-string">"block text-sm font-medium text-gray-700 mb-1"</span>
                &gt;</span>
                  Email
                <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span></span>
                <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">input</span>
                  <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span>
                  <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"</span>
                  <span class="hljs-attr">id</span>=<span class="hljs-string">"email"</span>
                  <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"your.email@example.com"</span>
                  <span class="hljs-attr">required</span>
                  <span class="hljs-attr">value</span>=<span class="hljs-string">{formData.email}</span>
                  <span class="hljs-attr">onChange</span>=<span class="hljs-string">{handleChange}</span>
                /&gt;</span></span>
              &lt;/div&gt;
              <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mb-4"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">label</span>
                  <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"subject"</span>
                  <span class="hljs-attr">className</span>=<span class="hljs-string">"block text-sm font-medium text-gray-700 mb-1"</span>
                &gt;</span>
                  Subject
                <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
                  <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
                  <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"</span>
                  <span class="hljs-attr">id</span>=<span class="hljs-string">"subject"</span>
                  <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Subject"</span>
                  <span class="hljs-attr">required</span>
                  <span class="hljs-attr">value</span>=<span class="hljs-string">{formData.subject}</span>
                  <span class="hljs-attr">onChange</span>=<span class="hljs-string">{handleChange}</span>
                /&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
              <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mb-4"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">label</span>
                  <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"message"</span>
                  <span class="hljs-attr">className</span>=<span class="hljs-string">"block text-sm font-medium text-gray-700 mb-1"</span>
                &gt;</span>
                  Message
                <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span>
                  <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"</span>
                  <span class="hljs-attr">id</span>=<span class="hljs-string">"message"</span>
                  <span class="hljs-attr">rows</span>=<span class="hljs-string">"5"</span>
                  <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Your message..."</span>
                  <span class="hljs-attr">required</span>
                  <span class="hljs-attr">value</span>=<span class="hljs-string">{formData.message}</span>
                  <span class="hljs-attr">onChange</span>=<span class="hljs-string">{handleChange}</span>
                &gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">textarea</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
              <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">"px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 transition-colors"</span>
              &gt;</span>
                Send Message
              <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
            &lt;/form&gt;
            <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>
              <span class="hljs-attr">id</span>=<span class="hljs-string">"form-response"</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"mt-4"</span>
              <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">display:</span> <span class="hljs-attr">formResponse</span> ? "<span class="hljs-attr">block</span>" <span class="hljs-attr">:</span> "<span class="hljs-attr">none</span>" }}
            &gt;</span>
              {formResponse &amp;&amp; (
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span>
                  <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">p-4</span> ${
                    <span class="hljs-attr">formResponse.type</span> === <span class="hljs-string">"success"</span>
                      ? "<span class="hljs-attr">bg-green-100</span> <span class="hljs-attr">text-green-700</span>"
                      <span class="hljs-attr">:</span> "<span class="hljs-attr">bg-red-100</span> <span class="hljs-attr">text-red-700</span>"
                  } <span class="hljs-attr">rounded-md</span>`}
                &gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bi bi-check-circle-fill mr-2"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
                  {formResponse.message}
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
              )}
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
          &lt;/div&gt;
        &lt;/div&gt;
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"space-y-6"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-white rounded-lg shadow-md overflow-hidden"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-6"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h5</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl font-semibold mb-4"</span>&gt;</span>
                Contact Information
              <span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"space-y-3"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex items-center"</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bi bi-envelope mr-2"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">a</span>
                    <span class="hljs-attr">href</span>=<span class="hljs-string">"mailto:contact@example.com"</span>
                    <span class="hljs-attr">className</span>=<span class="hljs-string">"text-blue-500 hover:underline"</span>
                  &gt;</span>
                    contact@example.com
                  <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex items-center"</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bi bi-geo-alt mr-2"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span>
                  UK
                <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h5</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl font-semibold mt-6 mb-3"</span>&gt;</span>
                Connect on Social Media
              <span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-wrap gap-2"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">a</span>
                  <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span>
                  <span class="hljs-attr">className</span>=<span class="hljs-string">"px-3 py-1.5 border border-gray-800 text-gray-800 rounded-md hover:bg-gray-100 flex items-center transition-colors"</span>
                &gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bi bi-github mr-1"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span> GitHub
                <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">a</span>
                  <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span>
                  <span class="hljs-attr">className</span>=<span class="hljs-string">"px-3 py-1.5 border border-blue-500 text-blue-500 rounded-md hover:bg-blue-50 flex items-center transition-colors"</span>
                &gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bi bi-linkedin mr-1"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span> LinkedIn
                <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">a</span>
                  <span class="hljs-attr">href</span>=<span class="hljs-string">"#"</span>
                  <span class="hljs-attr">className</span>=<span class="hljs-string">"px-3 py-1.5 border border-gray-800 text-gray-800 rounded-md hover:bg-gray-100 flex items-center transition-colors"</span>
                &gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">i</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bi bi-twitter mr-1"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">i</span>&gt;</span> X
                <span class="hljs-tag">&lt;/<span class="hljs-name">a</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-white rounded-lg shadow-md overflow-hidden"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-6"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h5</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl font-semibold mb-3"</span>&gt;</span>Availability<span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-700 mb-3"</span>&gt;</span>
                I'm currently available for freelance work and consulting. My
                typical response time is within 24 hours.
              <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-700"</span>&gt;</span>
                For urgent inquiries, please call the phone number listed above.
              <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
      &lt;/div&gt;
    &lt;/div&gt;
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Contact;
</code></pre>
<p>With that, this page is now done, and we have the rest of the components and form.</p>
<p>Ok just four pages left: let’s work on the home page first. The code is not that big so we can do it all at once.</p>
<p>This is the code to add to the <code>Home.jsx</code> page file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { Link } <span class="hljs-keyword">from</span> <span class="hljs-string">'react-router'</span>;
<span class="hljs-keyword">import</span> Chat <span class="hljs-keyword">from</span> <span class="hljs-string">'../components/Chat'</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Home</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> initialMessage =
    <span class="hljs-string">"Hello! I'm WelcomeAgent, the welcome specialist. I can help you navigate this portfolio website. Are you an employer, client, or fellow programmer?"</span>;

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col md:flex-row gap-8 mb-12"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"md:w-1/3"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-3xl font-bold mb-4"</span>&gt;</span>Welcome to my Portfolio<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-lg mb-4"</span>&gt;</span>
            This portfolio showcases my work and skills with the help of
            specialized AI agents. Each agent is designed to assist you with
            different aspects of my portfolio.
          <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-700"</span>&gt;</span>
            Feel free to interact with the WelcomeAgent to get personalized
            recommendations on which sections of the portfolio to explore based
            on your interests.
          <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"md:w-2/3"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-white rounded-lg shadow-md overflow-hidden"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-6"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h5</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl font-semibold mb-2"</span>&gt;</span>
                Chat with WelcomeAgent
              <span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-600 mb-4"</span>&gt;</span>
                Our welcome specialist can help you navigate this portfolio
                website.
              <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">Chat</span>
                <span class="hljs-attr">agentType</span>=<span class="hljs-string">"welcome"</span>
                <span class="hljs-attr">initialMessage</span>=<span class="hljs-string">{initialMessage}</span>
                <span class="hljs-attr">agentInitials</span>=<span class="hljs-string">"WA"</span>
              /&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mb-12"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mb-6"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-2xl font-bold mb-4"</span>&gt;</span>Meet the Agents<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"grid grid-cols-1 md:grid-cols-3 gap-6"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-white rounded-lg shadow-md overflow-hidden h-full"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-6 flex flex-col items-center"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"agent-avatar-placeholder mb-4"</span>&gt;</span>PA<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h5</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl font-semibold mb-2"</span>&gt;</span>ProjectAgent<span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-600 mb-4 text-center"</span>&gt;</span>
                Provides detailed information about my projects, technologies
                used, and challenges overcome.
              <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">Link</span>
                <span class="hljs-attr">to</span>=<span class="hljs-string">"/projects"</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">"mt-auto py-2 px-4 border border-blue-500 text-blue-500 rounded-md hover:bg-blue-50 transition-colors"</span>
              &gt;</span>
                View Projects
              <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-white rounded-lg shadow-md overflow-hidden h-full"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-6 flex flex-col items-center"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"agent-avatar-placeholder mb-4"</span>&gt;</span>CA<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h5</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl font-semibold mb-2"</span>&gt;</span>CareerAgent<span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-600 mb-4 text-center"</span>&gt;</span>
                Shares information about my skills, experience, and professional
                background.
              <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">Link</span>
                <span class="hljs-attr">to</span>=<span class="hljs-string">"/career"</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">"mt-auto py-2 px-4 border border-blue-500 text-blue-500 rounded-md hover:bg-blue-50 transition-colors"</span>
              &gt;</span>
                View Career
              <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-white rounded-lg shadow-md overflow-hidden h-full"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-6 flex flex-col items-center"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"agent-avatar-placeholder mb-4"</span>&gt;</span>BA<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h5</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl font-semibold mb-2"</span>&gt;</span>BusinessAdvisor<span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-600 mb-4 text-center"</span>&gt;</span>
                Provides information about services, pricing, and client
                engagement process.
              <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">Link</span>
                <span class="hljs-attr">to</span>=<span class="hljs-string">"/services"</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">"mt-auto py-2 px-4 border border-blue-500 text-blue-500 rounded-md hover:bg-blue-50 transition-colors"</span>
              &gt;</span>
                View Services
              <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"grid grid-cols-1 md:grid-cols-2 gap-6"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-white rounded-lg shadow-md overflow-hidden h-full"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-6"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h5</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl font-semibold mb-2"</span>&gt;</span>Featured Projects<span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-600 mb-4"</span>&gt;</span>
              Check out some of my recent work:
            <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"divide-y divide-gray-200"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"py-3 px-2"</span>&gt;</span>E-commerce Platform<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"py-3 px-2"</span>&gt;</span>Task Management Application<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"py-3 px-2"</span>&gt;</span>Data Visualization Dashboard<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mt-4"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">Link</span>
                <span class="hljs-attr">to</span>=<span class="hljs-string">"/projects"</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">"inline-block py-1.5 px-3 text-sm border border-blue-500 text-blue-500 rounded-md hover:bg-blue-50 transition-colors"</span>
              &gt;</span>
                View All Projects
              <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-white rounded-lg shadow-md overflow-hidden h-full"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-6"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h5</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl font-semibold mb-2"</span>&gt;</span>Research &amp; Insights<span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-600 mb-4"</span>&gt;</span>
              Explore my research on emerging technologies and industry trends:
            <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"divide-y divide-gray-200"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"py-3 px-2"</span>&gt;</span>AI in Web Development<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"py-3 px-2"</span>&gt;</span>Modern Frontend Frameworks<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">li</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"py-3 px-2"</span>&gt;</span>Cloud Architecture Patterns<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mt-4"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">Link</span>
                <span class="hljs-attr">to</span>=<span class="hljs-string">"/research"</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">"inline-block py-1.5 px-3 text-sm border border-blue-500 text-blue-500 rounded-md hover:bg-blue-50 transition-colors"</span>
              &gt;</span>
                View Research
              <span class="hljs-tag">&lt;/<span class="hljs-name">Link</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Home;
</code></pre>
<p>This has the code for our home page and WelcomeAgent.</p>
<p>Alright, now let's work on the <code>Projects.jsx</code> page. For readability it's easier to split the code in half again. So here is the first part:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> Chat <span class="hljs-keyword">from</span> <span class="hljs-string">"../components/Chat"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Projects</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> initialMessage =
    <span class="hljs-string">"Hello! I'm ProjectAgent, the project specialist. I can provide detailed information about projects, technologies used, and challenges overcome. What would you like to know?"</span>;

  <span class="hljs-keyword">const</span> [currentQuestion, setCurrentQuestion] = useState(<span class="hljs-string">""</span>);

  <span class="hljs-keyword">const</span> askProjectQuestion = <span class="hljs-function">(<span class="hljs-params">question</span>) =&gt;</span> {
    setCurrentQuestion(<span class="hljs-string">`<span class="hljs-subst">${question}</span> [<span class="hljs-subst">${<span class="hljs-built_in">Date</span>.now()}</span>]`</span>);

    <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
      setCurrentQuestion(<span class="hljs-string">""</span>);
    }, <span class="hljs-number">500</span>);
  };

  <span class="hljs-keyword">return</span> (
    &lt;div&gt;
      &lt;div className="flex flex-col md:flex-row gap-8 mb-12"&gt;
        &lt;div className="md:w-1/3"&gt;
          &lt;h1 className="text-3xl font-bold mb-4"&gt;Projects&lt;/h1&gt;
          &lt;p className="text-lg mb-4"&gt;
            Here you can explore my portfolio of projects. Feel free to ask
            ProjectAgent for more details about any project.
          &lt;/p&gt;
        &lt;/div&gt;
        &lt;div className="md:w-2/3"&gt;
          &lt;div className="bg-white rounded-lg shadow-md overflow-hidden"&gt;
            &lt;div className="p-6"&gt;
              &lt;h5 className="text-xl font-semibold mb-2"&gt;
                Chat with ProjectAgent
              &lt;/h5&gt;
              &lt;p className="text-gray-600 mb-4"&gt;
                Our project specialist can provide detailed information about
                projects, technologies, and challenges.
              &lt;/p&gt;
              &lt;Chat
                agentType="project"
                initialMessage={initialMessage}
                agentInitials="PA"
                directQuestion={currentQuestion}
              /&gt;
            &lt;/div&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div className="mb-12"&gt;
        &lt;div className="mb-6"&gt;
          &lt;h2 className="text-2xl font-bold mb-4"&gt;Featured Projects&lt;/h2&gt;
        &lt;/div&gt;
        &lt;div className="grid grid-cols-1 md:grid-cols-3 gap-6"&gt;
          &lt;div className="bg-white rounded-lg shadow-md overflow-hidden"&gt;
            &lt;div className="project-image-placeholder"&gt;E-commerce Platform&lt;/div&gt;
            &lt;div className="p-6"&gt;
              &lt;h5 className="text-xl font-semibold mb-2"&gt;
                E-commerce Platform
              &lt;/h5&gt;
              &lt;p className="text-gray-600 mb-4"&gt;
                A full-featured e-commerce platform with product management,
                shopping cart, and payment processing.
              &lt;/p&gt;
              &lt;div className="flex justify-between items-center"&gt;
                &lt;div className="flex space-x-2"&gt;
                  &lt;button
                    type="button"
                    className="py-1.5 px-3 text-sm border border-blue-500 text-blue-500 rounded-md hover:bg-blue-50 transition-colors"
                    onClick={() =&gt;
                      askProjectQuestion(
                        "Tell me more about the E-commerce Platform project"
                      )
                    }
                  &gt;
                    View Details
                  &lt;/button&gt;
                  &lt;button
                    type="button"
                    className="py-1.5 px-3 text-sm border border-gray-500 text-gray-500 rounded-md hover:bg-gray-50 transition-colors"
                    onClick={() =&gt;
                      askProjectQuestion(
                        "What technologies were used in the E-commerce Platform project?"
                      )
                    }
                  &gt;
                    Technologies
                  &lt;/button&gt;
                &lt;/div&gt;
                &lt;span className="text-sm text-gray-500"&gt;2023&lt;/span&gt;
              &lt;/div&gt;
            &lt;/div&gt;
          &lt;/div&gt;
          &lt;div className="bg-white rounded-lg shadow-md overflow-hidden"&gt;
            &lt;div className="project-image-placeholder"&gt;Task Management App&lt;/div&gt;
            &lt;div className="p-6"&gt;
              &lt;h5 className="text-xl font-semibold mb-2"&gt;
                Task Management Application
              &lt;/h5&gt;
              &lt;p className="text-gray-600 mb-4"&gt;
                A collaborative task management application with real-time
                updates and team collaboration features.
              &lt;/p&gt;
              &lt;div className="flex justify-between items-center"&gt;
                &lt;div className="flex space-x-2"&gt;
                  &lt;button
                    type="button"
                    className="py-1.5 px-3 text-sm border border-blue-500 text-blue-500 rounded-md hover:bg-blue-50 transition-colors"
                    onClick={() =&gt;
                      askProjectQuestion(
                        "Tell me more about the Task Management Application project"
                      )
                    }
                  &gt;
                    View Details
                  &lt;/button&gt;
</code></pre>
<p>As previously mentioned, we have our imports, functions, and some components. Complete the page with the second part of the code here:</p>
<pre><code class="lang-javascript"> &lt;button
                    type=<span class="hljs-string">"button"</span>
                    className=<span class="hljs-string">"py-1.5 px-3 text-sm border border-gray-500 text-gray-500 rounded-md hover:bg-gray-50 transition-colors"</span>
                    onClick={<span class="hljs-function">() =&gt;</span>
                      askProjectQuestion(
                        <span class="hljs-string">"What technologies were used in the Task Management Application project?"</span>
                      )
                    }
                  &gt;
                    Technologies
                  &lt;/button&gt;
                &lt;/div&gt;
                <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-sm text-gray-500"</span>&gt;</span>2022<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span></span>
              &lt;/div&gt;
            &lt;/div&gt;
          &lt;/div&gt;
          <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-white rounded-lg shadow-md overflow-hidden"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"project-image-placeholder"</span>&gt;</span>Data Visualization<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-6"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h5</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl font-semibold mb-2"</span>&gt;</span>
                Data Visualization Dashboard
              <span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-600 mb-4"</span>&gt;</span>
                An interactive dashboard for visualizing complex datasets with
                customizable charts and filters.
              <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex justify-between items-center"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex space-x-2"</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                    <span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span>
                    <span class="hljs-attr">className</span>=<span class="hljs-string">"py-1.5 px-3 text-sm border border-blue-500 text-blue-500 rounded-md hover:bg-blue-50 transition-colors"</span>
                    <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span>
                      askProjectQuestion(
                        "Tell me more about the Data Visualization Dashboard project"
                      )
                    }
                  &gt;
                    View Details
                  <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                    <span class="hljs-attr">type</span>=<span class="hljs-string">"button"</span>
                    <span class="hljs-attr">className</span>=<span class="hljs-string">"py-1.5 px-3 text-sm border border-gray-500 text-gray-500 rounded-md hover:bg-gray-50 transition-colors"</span>
                    <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span>
                      askProjectQuestion(
                        "What technologies were used in the Data Visualization Dashboard project?"
                      )
                    }
                  &gt;
                    Technologies
                  <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">span</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-sm text-gray-500"</span>&gt;</span>2021<span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
        &lt;/div&gt;
      &lt;/div&gt;

      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"grid grid-cols-1 md:grid-cols-2 gap-6"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-white rounded-lg shadow-md overflow-hidden"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-6"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h5</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl font-semibold mb-2"</span>&gt;</span>
              Technical Skills Showcase
            <span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-600 mb-4"</span>&gt;</span>
              These projects demonstrate proficiency in the following
              technologies:
            <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"grid grid-cols-1 md:grid-cols-2 gap-4"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">h6</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-semibold mb-2"</span>&gt;</span>Frontend<span class="hljs-tag">&lt;/<span class="hljs-name">h6</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"list-disc pl-5 space-y-1"</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>React<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>Vue.js<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>Angular<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>TypeScript<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>CSS/SASS<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">h6</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-semibold mb-2"</span>&gt;</span>Backend<span class="hljs-tag">&lt;/<span class="hljs-name">h6</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"list-disc pl-5 space-y-1"</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>Node.js<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>Python<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>Django<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>Flask<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>MongoDB<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"mt-4 py-1.5 px-3 text-sm border border-blue-500 text-blue-500 rounded-md hover:bg-blue-50 transition-colors"</span>
              <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span>
                askProjectQuestion(
                  "What other technologies are you proficient in?"
                )
              }
            &gt;
              Ask About Other Skills
            <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-white rounded-lg shadow-md overflow-hidden"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-6"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h5</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl font-semibold mb-2"</span>&gt;</span>Project Inquiry<span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-600 mb-4"</span>&gt;</span>
              Interested in a specific type of project or technology? Ask
              ProjectAgent for more information.
            <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col space-y-3"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">"py-2 px-4 border border-blue-500 text-blue-500 rounded-md hover:bg-blue-50 transition-colors"</span>
                <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span>
                  askProjectQuestion(
                    "Do you have any projects involving machine learning or AI?"
                  )
                }
              &gt;
                Ask About AI Projects
              <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">"py-2 px-4 border border-blue-500 text-blue-500 rounded-md hover:bg-blue-50 transition-colors"</span>
                <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span>
                  askProjectQuestion("What are your most challenging projects?")
                }
              &gt;
                Ask About Challenging Projects
              <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">"py-2 px-4 border border-blue-500 text-blue-500 rounded-md hover:bg-blue-50 transition-colors"</span>
                <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span>
                  askProjectQuestion(
                    "Can you show me examples of your UI/UX work?"
                  )
                }
              &gt;
                Ask About UI/UX Work
              <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    &lt;/div&gt;
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Projects;
</code></pre>
<p>With the remaining components added, this page is now complete.</p>
<p>Its time to do the <code>Research.jsx</code> page, starting with the first half of the codebase:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> Chat <span class="hljs-keyword">from</span> <span class="hljs-string">"../components/Chat"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Research</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> initialMessage =
    <span class="hljs-string">"Hello! I'm ResearchAgent, the research specialist. I can provide information about technologies, trends, and industry news. What would you like to know?"</span>;
  <span class="hljs-keyword">const</span> [searchQuery, setSearchQuery] = useState(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [tech1, setTech1] = useState(<span class="hljs-string">""</span>);
  <span class="hljs-keyword">const</span> [tech2, setTech2] = useState(<span class="hljs-string">""</span>);

  <span class="hljs-keyword">const</span> [currentQuestion, setCurrentQuestion] = useState(<span class="hljs-string">""</span>);

  <span class="hljs-keyword">const</span> askResearchQuestion = <span class="hljs-function">(<span class="hljs-params">question</span>) =&gt;</span> {
    setCurrentQuestion(<span class="hljs-string">`<span class="hljs-subst">${question}</span> [<span class="hljs-subst">${<span class="hljs-built_in">Date</span>.now()}</span>]`</span>);

    <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
      setCurrentQuestion(<span class="hljs-string">""</span>);
    }, <span class="hljs-number">500</span>);
  };

  <span class="hljs-keyword">const</span> handleSearch = <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
    e.preventDefault();
    <span class="hljs-keyword">if</span> (searchQuery.trim()) {
      askResearchQuestion(<span class="hljs-string">`Search for information about: <span class="hljs-subst">${searchQuery}</span>`</span>);
      setSearchQuery(<span class="hljs-string">""</span>);
    }
  };

  <span class="hljs-keyword">const</span> handleCompare = <span class="hljs-function">(<span class="hljs-params">e</span>) =&gt;</span> {
    e.preventDefault();
    <span class="hljs-keyword">if</span> (tech1.trim() &amp;&amp; tech2.trim()) {
      askResearchQuestion(<span class="hljs-string">`Compare <span class="hljs-subst">${tech1}</span> vs <span class="hljs-subst">${tech2}</span>`</span>);
      setTech1(<span class="hljs-string">""</span>);
      setTech2(<span class="hljs-string">""</span>);
    }
  };

  <span class="hljs-keyword">return</span> (
    &lt;div&gt;
      &lt;div className="flex flex-col md:flex-row gap-8 mb-12"&gt;
        &lt;div className="md:w-1/3"&gt;
          &lt;h1 className="text-3xl font-bold mb-4"&gt;Research &amp; Insights&lt;/h1&gt;
          &lt;p className="text-lg mb-4"&gt;
            Here you can explore research on technologies, trends, and industry
            news. Feel free to ask ResearchAgent for more information.
          &lt;/p&gt;
        &lt;/div&gt;
        &lt;div className="md:w-2/3"&gt;
          &lt;div className="bg-white rounded-lg shadow-md overflow-hidden"&gt;
            &lt;div className="p-6"&gt;
              &lt;h5 className="text-xl font-semibold mb-2"&gt;
                Chat with ResearchAgent
              &lt;/h5&gt;
              &lt;p className="text-gray-600 mb-4"&gt;
                Our research specialist can provide information about
                technologies, trends, and industry news.
              &lt;/p&gt;
              &lt;Chat
                agentType="research"
                initialMessage={initialMessage}
                agentInitials="RA"
                directQuestion={currentQuestion}
              /&gt;
            &lt;/div&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div className="grid grid-cols-1 md:grid-cols-2 gap-6 mb-12"&gt;
        &lt;div className="bg-white rounded-lg shadow-md overflow-hidden"&gt;
          &lt;div className="p-6"&gt;
            &lt;h5 className="text-xl font-semibold mb-3"&gt;
              Search for Information
            &lt;/h5&gt;
            &lt;p className="text-gray-600 mb-4"&gt;
              Enter a topic to search for the latest information and insights.
            &lt;/p&gt;
            &lt;form onSubmit={handleSearch}&gt;
              &lt;div className="flex mb-4"&gt;
                &lt;input
                  type="text"
                  className="flex-grow px-3 py-2 border border-gray-300 rounded-l-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
                  placeholder="e.g., WebAssembly, Edge Computing, etc."
                  value={searchQuery}
                  onChange={(e) =&gt; setSearchQuery(e.target.value)}
                /&gt;
                &lt;button
                  className="px-4 py-2 bg-blue-500 text-white rounded-r-md hover:bg-blue-600 transition-colors"
                  type="submit"
                &gt;
                  Search
                &lt;/button&gt;
              &lt;/div&gt;
            &lt;/form&gt;
          &lt;/div&gt;
        &lt;/div&gt;
        &lt;div className="bg-white rounded-lg shadow-md overflow-hidden"&gt;
          &lt;div className="p-6"&gt;
            &lt;h5 className="text-xl font-semibold mb-3"&gt;Compare Technologies&lt;/h5&gt;
            &lt;p className="text-gray-600 mb-4"&gt;
              Compare two technologies to understand their pros, cons, and use
              cases.
            &lt;/p&gt;
            &lt;form onSubmit={handleCompare}&gt;
              &lt;div className="grid grid-cols-1 sm:grid-cols-2 gap-3 mb-4"&gt;
                &lt;div&gt;
                  &lt;input
                    type="text"
                    className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
                    placeholder="First technology"
                    value={tech1}
                    onChange={(e) =&gt; setTech1(e.target.value)}
                  /&gt;
                &lt;/div&gt;
</code></pre>
<p>We have our imports, state, functions, and some components for the ResearchAgent, so it's pretty straightforward. Now, we can complete the page by finishing it with the rest of the code:</p>
<pre><code class="lang-javascript">&lt;div&gt;
                  <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">input</span>
                    <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
                    <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"</span>
                    <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Second technology"</span>
                    <span class="hljs-attr">value</span>=<span class="hljs-string">{tech2}</span>
                    <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setTech2(e.target.value)}
                  /&gt;</span>
                &lt;/div&gt;
              &lt;/div&gt;
              <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">"px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 transition-colors"</span>
                <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>
              &gt;</span>
                Compare
              <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
            &lt;/form&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mb-12"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mb-6"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-2xl font-bold mb-4"</span>&gt;</span>Current Tech Trends<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"grid grid-cols-1 md:grid-cols-3 gap-6"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-white rounded-lg shadow-md overflow-hidden h-full"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-6 flex flex-col h-full"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h5</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl font-semibold mb-3"</span>&gt;</span>
                AI in Web Development
              <span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-600 mb-4 flex-grow"</span>&gt;</span>
                Exploring how artificial intelligence is transforming web
                development practices and tools.
              <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">"py-1.5 px-3 text-sm border border-blue-500 text-blue-500 rounded-md hover:bg-blue-50 transition-colors self-start"</span>
                <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span>
                  askResearchQuestion("Tell me about AI in web development")
                }
              &gt;
                Learn More
              <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-white rounded-lg shadow-md overflow-hidden h-full"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-6 flex flex-col h-full"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h5</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl font-semibold mb-3"</span>&gt;</span>
                Modern Frontend Frameworks
              <span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-600 mb-4 flex-grow"</span>&gt;</span>
                Analysis of current frontend frameworks, their strengths, and
                ideal use cases.
              <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">"py-1.5 px-3 text-sm border border-blue-500 text-blue-500 rounded-md hover:bg-blue-50 transition-colors self-start"</span>
                <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span>
                  askResearchQuestion("Compare modern frontend frameworks")
                }
              &gt;
                Learn More
              <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-white rounded-lg shadow-md overflow-hidden h-full"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-6 flex flex-col h-full"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h5</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl font-semibold mb-3"</span>&gt;</span>
                Cloud Architecture Patterns
              <span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-600 mb-4 flex-grow"</span>&gt;</span>
                Best practices and patterns for designing scalable cloud-based
                applications.
              <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">"py-1.5 px-3 text-sm border border-blue-500 text-blue-500 rounded-md hover:bg-blue-50 transition-colors self-start"</span>
                <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span>
                  askResearchQuestion("Explain cloud architecture patterns")
                }
              &gt;
                Learn More
              <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>

      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mb-6"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-white rounded-lg shadow-md overflow-hidden"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-6"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h5</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl font-semibold mb-3"</span>&gt;</span>Industry Trends<span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-600 mb-4"</span>&gt;</span>
              Stay updated on the latest trends in software development and
              technology.
            <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"py-2 px-4 border border-blue-500 text-blue-500 rounded-md hover:bg-blue-50 transition-colors"</span>
              <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span>
                askResearchQuestion(
                  "What are the current trends in software development and technology?"
                )
              }
            &gt;
              Get Industry Trends
            <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    &lt;/div&gt;
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Research;
</code></pre>
<p>The second half of the code has the remaining components, which complete the page.</p>
<p>Now for the final page which is for <code>Services.jsx</code>. The codebase is quite large so we will break it down.</p>
<p>And here's the first part of the codebase to add:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;
<span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">"axios"</span>;
<span class="hljs-keyword">import</span> Chat <span class="hljs-keyword">from</span> <span class="hljs-string">"../components/Chat"</span>;

<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Services</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> initialMessage =
    <span class="hljs-string">"Hello! I'm BusinessAdvisor, the client specialist. I can provide information about services, pricing, and project details. What would you like to know?"</span>;
  <span class="hljs-keyword">const</span> [projectDescription, setProjectDescription] = useState(<span class="hljs-string">""</span>);

  <span class="hljs-keyword">const</span> [currentQuestion, setCurrentQuestion] = useState(<span class="hljs-string">""</span>);

  <span class="hljs-keyword">const</span> askClientQuestion = <span class="hljs-function">(<span class="hljs-params">question</span>) =&gt;</span> {
    setCurrentQuestion(<span class="hljs-string">`<span class="hljs-subst">${question}</span> [<span class="hljs-subst">${<span class="hljs-built_in">Date</span>.now()}</span>]`</span>);

    <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
      setCurrentQuestion(<span class="hljs-string">""</span>);
    }, <span class="hljs-number">500</span>);
  };

  <span class="hljs-keyword">const</span> generateProposal = <span class="hljs-keyword">async</span> () =&gt; {
    <span class="hljs-keyword">if</span> (!projectDescription.trim()) <span class="hljs-keyword">return</span>;

    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> axios.post(<span class="hljs-string">"/api/client/proposal"</span>, {
        <span class="hljs-attr">project_description</span>: projectDescription,
      });

      <span class="hljs-keyword">if</span> (response.data &amp;&amp; response.data.proposal) {
        askClientQuestion(
          <span class="hljs-string">`Can you provide a proposal for this project: <span class="hljs-subst">${projectDescription}</span>`</span>
        );
      }
    } <span class="hljs-keyword">catch</span> (error) {
      <span class="hljs-built_in">console</span>.error(<span class="hljs-string">"Error generating proposal:"</span>, error);
    }
  };

  <span class="hljs-keyword">return</span> (
    &lt;div&gt;
      &lt;div className="flex flex-col md:flex-row gap-8 mb-12"&gt;
        &lt;div className="md:w-1/3"&gt;
          &lt;h1 className="text-3xl font-bold mb-4"&gt;Services&lt;/h1&gt;
          &lt;p className="text-lg mb-4"&gt;
            Here you can find information about the services I offer. Feel free
            to ask BusinessAdvisor for more details about pricing, timelines,
            and project specifics.
          &lt;/p&gt;
        &lt;/div&gt;
        &lt;div className="md:w-2/3"&gt;
          &lt;div className="bg-white rounded-lg shadow-md overflow-hidden"&gt;
            &lt;div className="p-6"&gt;
              &lt;h5 className="text-xl font-semibold mb-2"&gt;
                Chat with BusinessAdvisor
              &lt;/h5&gt;
              &lt;p className="text-gray-600 mb-4"&gt;
                Our client specialist can provide information about services,
                pricing, and project details.
              &lt;/p&gt;
              &lt;Chat
                agentType="client"
                initialMessage={initialMessage}
                agentInitials="BA"
                directQuestion={currentQuestion}
              /&gt;
            &lt;/div&gt;
          &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div className="mb-12"&gt;
        &lt;div className="mb-6"&gt;
          &lt;h2 className="text-2xl font-bold mb-4"&gt;Services Offered&lt;/h2&gt;
        &lt;/div&gt;
        &lt;div className="grid grid-cols-1 md:grid-cols-3 gap-6"&gt;
          &lt;div className="bg-white rounded-lg shadow-md overflow-hidden h-full"&gt;
            &lt;div className="p-6"&gt;
              &lt;h5 className="text-xl font-semibold mb-2"&gt;Web Development&lt;/h5&gt;
              &lt;p className="text-gray-600 mb-4"&gt;
                Custom web application development using modern frameworks and
                best practices.
              &lt;/p&gt;
              &lt;h6 className="font-semibold mb-2"&gt;Technologies&lt;/h6&gt;
              &lt;ul className="list-disc pl-5 space-y-1 mb-4"&gt;
                &lt;li&gt;React&lt;/li&gt;
                &lt;li&gt;Vue.js&lt;/li&gt;
                &lt;li&gt;Node.js&lt;/li&gt;
                &lt;li&gt;Django&lt;/li&gt;
                &lt;li&gt;Flask&lt;/li&gt;
              &lt;/ul&gt;
              &lt;h6 className="font-semibold mb-2"&gt;Details&lt;/h6&gt;
              &lt;ul className="space-y-2 mb-4"&gt;
                &lt;li&gt;
                  &lt;strong&gt;Pricing Model:&lt;/strong&gt; Project-based or hourly
                &lt;/li&gt;
                &lt;li&gt;
                  &lt;strong&gt;Price Range:&lt;/strong&gt; $5,000 - $50,000 depending on
                  complexity
                &lt;/li&gt;
                &lt;li&gt;
                  &lt;strong&gt;Timeline:&lt;/strong&gt; 4-12 weeks depending on scope
                &lt;/li&gt;
              &lt;/ul&gt;
              &lt;button
                className="mt-2 py-1.5 px-3 text-sm border border-blue-500 text-blue-500 rounded-md hover:bg-blue-50 transition-colors"
                onClick={() =&gt;
                  askClientQuestion(
                    "Tell me more about your web development services"
                  )
                }
              &gt;
                Ask about Web Development
              &lt;/button&gt;
            &lt;/div&gt;
          &lt;/div&gt;
</code></pre>
<p>We have more import statements, state, and components for our BusinessAdvisor AI agent. Onto the next part of this codebase here:</p>
<pre><code class="lang-javascript"> &lt;div className=<span class="hljs-string">"bg-white rounded-lg shadow-md overflow-hidden h-full"</span>&gt;
            <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-6"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h5</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl font-semibold mb-2"</span>&gt;</span>
                Mobile App Development
              <span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-600 mb-4"</span>&gt;</span>
                Native and cross-platform mobile application development for iOS
                and Android.
              <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h6</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-semibold mb-2"</span>&gt;</span>Technologies<span class="hljs-tag">&lt;/<span class="hljs-name">h6</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"list-disc pl-5 space-y-1 mb-4"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>React Native<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>Flutter<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>Swift<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>Kotlin<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h6</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-semibold mb-2"</span>&gt;</span>Details<span class="hljs-tag">&lt;/<span class="hljs-name">h6</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"space-y-2 mb-4"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">strong</span>&gt;</span>Pricing Model:<span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span> Project-based
                <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">strong</span>&gt;</span>Price Range:<span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span> $8,000 - $60,000 depending on
                  complexity
                <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">strong</span>&gt;</span>Timeline:<span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span> 6-16 weeks depending on scope
                <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">"mt-2 py-1.5 px-3 text-sm border border-blue-500 text-blue-500 rounded-md hover:bg-blue-50 transition-colors"</span>
                <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span>
                  askClientQuestion(
                    "Tell me more about your mobile app development services"
                  )
                }
              &gt;
                Ask about Mobile Development
              <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
          &lt;/div&gt;
          <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-white rounded-lg shadow-md overflow-hidden h-full"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-6"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h5</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl font-semibold mb-2"</span>&gt;</span>
                Technical Consulting
              <span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-600 mb-4"</span>&gt;</span>
                Expert advice on architecture, technology stack, and development
                practices.
              <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h6</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-semibold mb-2"</span>&gt;</span>Areas of Expertise<span class="hljs-tag">&lt;/<span class="hljs-name">h6</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"list-disc pl-5 space-y-1 mb-4"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>System Architecture<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>Database Design<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>Performance Optimization<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>Security Best Practices<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>DevOps Implementation<span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">h6</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"font-semibold mb-2"</span>&gt;</span>Details<span class="hljs-tag">&lt;/<span class="hljs-name">h6</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">ul</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"space-y-2 mb-4"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">strong</span>&gt;</span>Pricing Model:<span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span> Hourly
                <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">strong</span>&gt;</span>Price Range:<span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span> $150 - $250 per hour
                <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">li</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">strong</span>&gt;</span>Timeline:<span class="hljs-tag">&lt;/<span class="hljs-name">strong</span>&gt;</span> Ongoing or as needed
                <span class="hljs-tag">&lt;/<span class="hljs-name">li</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">ul</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">"mt-2 py-1.5 px-3 text-sm border border-blue-500 text-blue-500 rounded-md hover:bg-blue-50 transition-colors"</span>
                <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span>
                  askClientQuestion(
                    "Tell me more about your technical consulting services"
                  )
                }
              &gt;
                Ask about Consulting
              <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
        &lt;/div&gt;
      &lt;/div&gt;
</code></pre>
<p>We can expect to see lots of component code here for the page, so lets finish it off with the final part now:</p>
<pre><code class="lang-javascript"> &lt;div className=<span class="hljs-string">"mb-12"</span>&gt;
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mb-6"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">h2</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-2xl font-bold mb-4"</span>&gt;</span>Client Engagement Process<span class="hljs-tag">&lt;/<span class="hljs-name">h2</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
        <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-white rounded-lg shadow-md overflow-hidden"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-6"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"grid grid-cols-1 md:grid-cols-4 gap-6"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mb-6 md:mb-0"</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col items-center"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-12 h-12 rounded-full bg-blue-500 text-white flex items-center justify-center text-xl font-bold mb-4"</span>&gt;</span>
                      1
                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">h5</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-lg font-semibold mt-2 mb-1"</span>&gt;</span>
                      Initial Consultation
                    <span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-600 text-center"</span>&gt;</span>
                      Understanding your requirements and project goals
                    <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mb-6 md:mb-0"</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col items-center"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-12 h-12 rounded-full bg-blue-500 text-white flex items-center justify-center text-xl font-bold mb-4"</span>&gt;</span>
                      2
                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">h5</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-lg font-semibold mt-2 mb-1"</span>&gt;</span>
                      Proposal
                    <span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-600 text-center"</span>&gt;</span>
                      Detailed quote and project plan preparation
                    <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mb-6 md:mb-0"</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col items-center"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-12 h-12 rounded-full bg-blue-500 text-white flex items-center justify-center text-xl font-bold mb-4"</span>&gt;</span>
                      3
                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">h5</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-lg font-semibold mt-2 mb-1"</span>&gt;</span>
                      Development
                    <span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-600 text-center"</span>&gt;</span>
                      Regular sprints with client feedback
                    <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mb-6 md:mb-0"</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-col items-center"</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-12 h-12 rounded-full bg-blue-500 text-white flex items-center justify-center text-xl font-bold mb-4"</span>&gt;</span>
                      4
                    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">h5</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-lg font-semibold mt-2 mb-1"</span>&gt;</span>
                      Delivery
                    <span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
                    <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-600 text-center"</span>&gt;</span>
                      Testing, deployment, and ongoing support
                    <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
                  <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex justify-center mt-8"</span>&gt;</span>
                <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
                  <span class="hljs-attr">className</span>=<span class="hljs-string">"py-2 px-4 border border-blue-500 text-blue-500 rounded-md hover:bg-blue-50 transition-colors"</span>
                  <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span>
                    askClientQuestion(
                      "Explain your client engagement process in detail"
                    )
                  }
                &gt;
                  Learn More About the Process
                <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
              <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
      &lt;/div&gt;

      <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-white rounded-lg shadow-md overflow-hidden"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-6"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">h5</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-xl font-semibold mb-2"</span>&gt;</span>Request a Proposal<span class="hljs-tag">&lt;/<span class="hljs-name">h5</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-gray-600 mb-4"</span>&gt;</span>
              Interested in working together? Describe your project below and
              BusinessAdvisor will generate a custom proposal for you.
            <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"mb-4"</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">label</span>
                <span class="hljs-attr">htmlFor</span>=<span class="hljs-string">"project-description"</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">"block text-sm font-medium text-gray-700 mb-1"</span>
              &gt;</span>
                Describe your project:
              <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">textarea</span>
                <span class="hljs-attr">id</span>=<span class="hljs-string">"project-description"</span>
                <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"</span>
                <span class="hljs-attr">rows</span>=<span class="hljs-string">"5"</span>
                <span class="hljs-attr">placeholder</span>=<span class="hljs-string">"Enter project description..."</span>
                <span class="hljs-attr">value</span>=<span class="hljs-string">{projectDescription}</span>
                <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setProjectDescription(e.target.value)}
              &gt;<span class="hljs-tag">&lt;/<span class="hljs-name">textarea</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
              <span class="hljs-attr">className</span>=<span class="hljs-string">"py-2 px-4 bg-blue-500 text-white rounded-md hover:bg-blue-600 transition-colors"</span>
              <span class="hljs-attr">onClick</span>=<span class="hljs-string">{generateProposal}</span>
            &gt;</span>
              Generate Proposal
            <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
    &lt;/div&gt;
  );
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Services;
</code></pre>
<p>Our services page is complete, and so is the application!</p>
<p>Make sure that the Python backend server is running, and then start your React frontend with the usual Vite run script here inside the <code>frontend</code> folder:</p>
<pre><code class="lang-shell">npm run dev
</code></pre>
<p>You should see the website up and running on <a target="_blank" href="http://localhost:5173/">http://localhost:5173/</a> with working AI agents on all pages (apart from the contact page, which does not have one). Remember that every time you use one of the AI agents to ask a question, it will use 1 API call on Groq Cloud, so check the <a target="_blank" href="https://console.groq.com/docs/rate-limits">Rate Limits</a> for the different LLMs.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Building a squad of AI agents for your website using platforms like Agno and Groq is a powerful way to showcase how innovative automated workflows can enhance user experience without spending a lot of money.</p>
<p>The combination of Agno and Groq provides a free route for exploring AI agents, which can be very beneficial. With Agno's no-code agent orchestration and Groq's super-fast inference, you can deploy AI-powered features that engage with visitors and make interactions easier.</p>
<p>So, whether it's a chatbot, content generator, or intelligent assistant, these tools are making it easier than ever to integrate AI into your brand. With the advancements that AI technology is making, being able to try out these free solutions will definitely keep you ahead and make your website truly shine as you continue to modernise your brand.</p>
<h3 id="heading-stay-up-to-date-with-tech-programming-productivity-and-ai">Stay up to date with tech, programming, productivity, and AI</h3>
<p>If you enjoyed these articles, connect and follow me across <a target="_blank" href="https://limey.io/andrewbaisden">social media</a>, where I share content related to all of these topics 🔥</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1741977770238/3766c236-f276-4939-996e-61ab1306cc26.png" alt="Andrew Baisden Software Developer and Technical Writer Social Media Banner" class="image--center mx-auto" width="1500" height="500" loading="lazy"></p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Build a Realtime Chart with React, HighCharts, and Pusher ]]>
                </title>
                <description>
                    <![CDATA[ In today's tutorial, you are going to learn about WebSockets and how you can use them to create interactive realtime data applications.  To illustrate just how innovative real time technologies are, we will build a chart application which automatical... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-build-a-realtime-chart-with-react-and-pusher/</link>
                <guid isPermaLink="false">66be00bfb51b2616d39af871</guid>
                
                    <category>
                        <![CDATA[ charts ]]>
                    </category>
                
                    <category>
                        <![CDATA[ React ]]>
                    </category>
                
                    <category>
                        <![CDATA[ websocket ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Andrew Baisden ]]>
                </dc:creator>
                <pubDate>Thu, 02 May 2024 00:07:30 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/05/pusher-banner.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In today's tutorial, you are going to learn about WebSockets and how you can use them to create interactive realtime data applications. </p>
<p>To illustrate just how innovative real time technologies are, we will build a chart application which automatically updates with new dynamic online data.</p>
<p>This is going to be a really good example of how we can implement user data into useful commercial products such as sports leaderboards, social media analytics, financial money trackers, medical instruments, games and many more.</p>
<p>As you can see, there are numerous use cases for this technology. So let's get under way and first learn about the main platform that we will be using to create our realtime data: <a target="_blank" href="https://pusher.com/">Pusher</a>.</p>
<p>You can find the <a target="_blank" href="https://github.com/andrewbaisden/realtime-chart-pusher">codebase online here</a>.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li>An IDE/code editor installed</li>
<li>Basic knowledge of JavaScript and React</li>
<li>An understanding of Node.js and npm</li>
<li>An account on <a target="_blank" href="https://pusher.com/">Pusher</a></li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><a class="post-section-overview" href="#heading-what-are-websockets">What are WebSockets?</a></li>
<li><a class="post-section-overview" href="#heading-what-is-pusher">What is Pusher?</a></li>
<li><a class="post-section-overview" href="#heading-what-we-will-be-building">What we will be building</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-an-account-on-pusher">How to Create an Account on Pusher</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-company-annual-income-application">How to Build the Company Annual Income Application</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ol>
<h2 id="heading-what-are-websockets">What are WebSockets?</h2>
<p>WebSockets are a communications protocol which allows for data to travel bidirectionally between a client and a server. This essentially means that it's possible for a client and server to simultaneously send data back and forth independently of each other. </p>
<p>Being able to send data this way brings many advantages, such as preventing blocking. This can occur if the system is only capable of either sending or receiving and not both at the same time.</p>
<p>Another advantage is the fact that the Transmission Control Protocol (TCP) connection remains long-lived and connected. This is a drawback of traditional Hypertext Transfer Protocol (HTTP) connections, as they are regularly opening and closing each time there is a request or a response. This prevents communication from being instant and realtime. </p>
<p>Not having to worry about the opening and closing of connections results in dramatically less network traffic – so in terms of speed and resources, this is a welcome bonus.</p>
<p>Now that you know why WebSockets are helpful, let's take a look at Pusher and see what the platform is capable of.</p>
<h2 id="heading-what-is-pusher">What is Pusher?</h2>
<p>Pusher is an online platform you can use to build and develop applications that require real-time communication. These communications typically take place between web browsers, mobile phones, and other internet connected devices. </p>
<p>The platform is designed to make it easy to implement real-time communication systems which are nowhere near as complicated as raw WebSocket connections in terms of setup and management. This allows for better scaling and even a way to handle fallbacks for legacy environments that do not support WebSockets.</p>
<p>Pusher is capable of using numerous technologies such as WebSockets, HTTP fallbacks, and even its own propriety protocol called <code>ws-longpolling</code>. This enables the platform to work in places where WebSockets is not supported.</p>
<p>Pusher also has a lot of other useful features. For example, it fully supports WebSockets, so bidirectional communication works as you would expect. The platform also follows the publish-subscribe (Pub/Sub) pattern which is a popular messaging pattern used in the industry. It pretty much guarantees that anyone subscribed to any of the channels can receive the messages in realtime.</p>
<p>A good example of this would be everyone getting the latest news notification who is subscribed to that social media channel. Subscriptions can be public and private, which requires authentication to gain access to those messages. </p>
<p>There's also wide platform support, as the Pusher SDK is available in different programming languages like JavaScript, Python, iOS, Android and many others. It's available as a service online, so all of the scaling and communications infrastructure is already handled. This means one less system for developers to handle when building an application.</p>
<p>Pusher is a great option, as I hope you can tell. But there are other tools out there that do similar things:</p>
<ul>
<li><a target="_blank" href="https://www.pubnub.com/">PubNub</a></li>
<li><a target="_blank" href="https://deepstreamhub.com/">deepstreamHub</a></li>
<li><a target="_blank" href="https://aws.amazon.com/iot/">AWS IoT</a></li>
<li><a target="_blank" href="https://aws.amazon.com/sns/">AWS SNS</a></li>
<li><a target="_blank" href="https://cloud.google.com/pubsub">Google Cloud Pub/Sub</a></li>
<li><a target="_blank" href="https://fanout.io/">Fanout</a></li>
</ul>
<p>Ok, you've learned quite a lot about WebSockets and Pusher. Now, we'll take a look at the application that we are going to build.</p>
<h2 id="heading-what-we-will-be-building">What We Will Be Building</h2>
<p>To help you learn how Pusher works, we are going to build a Company Annual Income dashboard application. The application has a graph, a 3D Mode toggle, and three buttons.</p>
<p>We'll build our application using <a target="_blank" href="https://nextjs.org/">Next.js</a>, <a target="_blank" href="https://pusher.com/">Pusher</a>, and <a target="_blank" href="https://www.highcharts.com/docs/index">HighCharts</a> (the tool that we'll use to create our graph component). This will be a full stack application with one API endpoint for our Pusher REST API. We just need one GET endpoint which will connect to the Pusher API.</p>
<p>The graph data will come directly from Pusher, and the 3D Mode toggle essentially lets us switch our chart between 2D and 3D mode. The buttons let us change the type of chart that is displayed on screen, and all 3 charts can work in 2D and 3D mode. The chart has some starting data, and new data is automatically added to the end for each new year.</p>
<p>Here you can see our Company Annual Income Application in 2D Graph Mode.</p>
<p><img src="https://res.cloudinary.com/d74fh3kw/image/upload/v1714407050/company-annual-income_ww5d4g.png" alt="Company Annual Income Application in 2D Graph Mode" width="2754" height="1204" loading="lazy">
<em>Company Annual Income Application 2D Mode</em></p>
<p>And here you can see our Company Annual Income Application in 3D Graph Mode.</p>
<p><img src="https://res.cloudinary.com/d74fh3kw/image/upload/v1714407667/company-annual-income-3d_fxjm1p.png" alt="Company Annual Income Application in 3D Graph Mode" width="2754" height="1194" loading="lazy">
<em>Company Annual Income Application 3D Mode</em></p>
<p>As you can see, the HighCharts library is very good when it comes to building applications that require data visualisation. If you are interested in other libraries which work just as well, I have put together a list of some of them here. Each one has its own unique benefits and it's possible to use more than one library if you want to:</p>
<ul>
<li><a target="_blank" href="https://www.chartjs.org/">Chart.js</a></li>
<li><a target="_blank" href="https://d3js.org/">D3</a></li>
<li><a target="_blank" href="https://recharts.org/en-US">Recharts</a></li>
<li><a target="_blank" href="https://developers.google.com/chart">Google Charts</a></li>
<li><a target="_blank" href="https://apexcharts.com/">Apex Charts</a></li>
<li><a target="_blank" href="https://dygraphs.com/">dyagraphs</a></li>
<li><a target="_blank" href="https://commerce.nearform.com/open-source/victory/">Victory</a></li>
</ul>
<p>Alright, we know what we are going to build. In the next section, we will quickly go through how to create an account on Pusher followed by actually building our application afterwards.</p>
<h2 id="heading-how-to-create-an-account-on-pusher">How to Create an Account on Pusher</h2>
<p>Creating an account on Pusher only requires a few steps. Depending on when you follow this tutorial, the website might look a bit different – but the sign up process should be the same.</p>
<p>Start by going to the <a target="_blank" href="https://pusher.com/">Pusher</a> website as shown below:</p>
<p><img src="https://res.cloudinary.com/d74fh3kw/image/upload/v1714409546/pusher-website-home_fvngyq.png" alt="Pusher website home page" width="600" height="400" loading="lazy">
<em>Pusher website home page</em></p>
<p>Click on either the Get your free account button in the middle of the page, or the Sign up button in the top right hand corner.</p>
<p>You should now be on the Sign up page as shown here:</p>
<p><img src="https://res.cloudinary.com/d74fh3kw/image/upload/v1714409766/pusher-website-signup_zpihwa.png" alt="Pusher website sign up page" width="600" height="400" loading="lazy">
<em>Pusher website sign up for account page</em></p>
<p>Either use your GitHub, Google, or email to create an account.</p>
<p>The next page to load should be your main dashboard after you have completed the sign up as shown in the image below:</p>
<p><img src="https://res.cloudinary.com/d74fh3kw/image/upload/v1714410240/pusher-channels_wqpt2l.png" alt="Pusher channels page" width="1305" height="535" loading="lazy">
<em>Pusher website main dashboard page</em></p>
<p>Click on the Manage button in the Channels section in the image shown above to get to the Channels page.</p>
<p>The Channels page can be seen below which displays all of the data for our app.</p>
<p><img src="https://res.cloudinary.com/d74fh3kw/image/upload/v1714410477/pusher-website-create-app_iorisl.png" alt="Pusher website Channels page" width="2454" height="1940" loading="lazy">
<em>Pusher website Channels page</em></p>
<p>We need to create an app, so click on the Create app button in the top right hand corner.</p>
<p>Clicking on the button should give you this Create your Channels app box shown here:</p>
<p><img src="https://res.cloudinary.com/d74fh3kw/image/upload/v1714410628/pusher-website-create-channels-app_gsyb8z.png" alt="Pusher website Create your Channels app screen" width="533" height="627" loading="lazy">
<em>Pusher website Create your channels app page</em></p>
<p>Give your app a name and then choose React for the front end and Node.js for the back end. Then click the Create app button at the bottom of the form.</p>
<p>That's it – you should now see the main screen for your app as shown in the example image below (with data for connections and messages shown in the middle). The sidebar on the left has the features and App keys which we need to use later.</p>
<p><img src="https://res.cloudinary.com/d74fh3kw/image/upload/v1714411607/pusher-website-app-overview_lsrhxl.png" alt="Pusher website app overview page" width="1260" height="892" loading="lazy">
<em>Pusher website app page</em></p>
<p>Great! Up next we will start building our app...so lets get to it.</p>
<h2 id="heading-how-to-build-the-company-annual-income-application">How to Build the Company Annual Income Application</h2>
<p>The first thing we have to do is setup our project architecture and the folder structure so let's start with that. Create a folder on your computer called <code>realtime-chart-pusher</code> and then <code>cd</code> into it.</p>
<p>Create a Next.js project by running the usual install and setup command here:</p>
<pre><code class="lang-shell">npx create-next-app .
</code></pre>
<p>On the configuration screen, our project needs to use Tailwind CSS for styling and its recommended that you use the App Router. The other defaults should be fine.</p>
<p>Now we have to install our dependencies for the project. Go ahead and do that with this command:</p>
<pre><code class="lang-shell">npm i axios highcharts-react-official pusher pusher-js
</code></pre>
<p>Here is a quick breakdown of the purpose of each package we are installing:</p>
<ul>
<li>Axios: for making API requests to our backend API which is connected to the Pusher API</li>
<li>highcharts-react-official: for building our realtime chart</li>
<li>pusher and pusher-js: for connecting to our Pusher account and the Pusher API</li>
</ul>
<p>Right, with that done, we just have to setup our project files. You can use this run script in your terminal while still in the project root folder:</p>
<pre><code class="lang-shell">touch .env.local
cd src/app
mkdir -p api/pusher components/ChartButton components/CompanyIncomeChart components/Toggle3DButton
touch api/pusher/route.js
touch components/ChartButton/ChartButton.js components/CompanyIncomeChart/CompanyIncomeChart.js components/Toggle3DButton/Toggle3DButton.js
</code></pre>
<p>This script creates a <code>.env.local</code> file for our Pusher App keys. It also creates files and folders for our backend and frontend.</p>
<p>Before we begin adding the code to our codebase, we need to get our Pusher App Keys. You can find them on your Pusher account as shown in this example below:</p>
<p><img src="https://res.cloudinary.com/d74fh3kw/image/upload/v1714580536/pusher-app-keys_rihysv.png" alt="Pusher App Keys Screen" width="810" height="537" loading="lazy">
<em>Pusher website App keys page</em></p>
<p>Add your app keys to the <code>.env.local</code> file like in this code example here:</p>
<pre><code class="lang-shell">NEXT_SECRET_PUSHER_APP_ID = "yourid"
NEXT_SECRET_PUSHER_KEY = "yourkey"
NEXT_SECRET_PUSHER_SECRET = "yoursecret"
NEXT_SECRET_PUSHER_CLUSTER = "yourlocation"
</code></pre>
<p>We just added <code>NEXT_SECRET</code> at the start of all of the variables because it is a Next.js convention and ensures that the variables work properly.</p>
<p>Now we can start with the bulk of our codebase. First we will do the <code>route.js</code> file inside of the <code>api/route</code> folder. This is the code needed for the file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> Pusher <span class="hljs-keyword">from</span> <span class="hljs-string">'pusher'</span>;

<span class="hljs-keyword">const</span> pusher = <span class="hljs-keyword">new</span> Pusher({
  <span class="hljs-attr">appId</span>: process.env.NEXT_SECRET_PUSHER_APP_ID,
  <span class="hljs-attr">key</span>: process.env.NEXT_SECRET_PUSHER_KEY,
  <span class="hljs-attr">secret</span>: process.env.NEXT_SECRET_PUSHER_SECRET,
  <span class="hljs-attr">cluster</span>: process.env.NEXT_SECRET_PUSHER_CLUSTER,
  <span class="hljs-attr">useTLS</span>: <span class="hljs-literal">true</span>,
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">GET</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-comment">// Define the initial value</span>
  <span class="hljs-keyword">const</span> value = <span class="hljs-built_in">Math</span>.random() * <span class="hljs-number">800000</span> + <span class="hljs-number">200000</span>;

  <span class="hljs-built_in">setInterval</span>(<span class="hljs-function">() =&gt;</span> {
    pusher.trigger(<span class="hljs-string">'company-income'</span>, <span class="hljs-string">'new-price'</span>, {
      <span class="hljs-attr">value</span>: value,
    });

    <span class="hljs-keyword">return</span> Response.json({ <span class="hljs-attr">value</span>: value }, { <span class="hljs-attr">status</span>: <span class="hljs-number">200</span> });
    <span class="hljs-comment">// Every ten seconds, the setInterval method will get data from the API because its value is set to ten seconds. Pusher's free plans are limited to 200,000 messages a day, so be careful while reducing the interval or you risk exceeding your limit too soon.</span>
  }, <span class="hljs-number">10000</span>);
  <span class="hljs-keyword">return</span> Response.json({ <span class="hljs-attr">value</span>: value }, { <span class="hljs-attr">status</span>: <span class="hljs-number">200</span> });
}
</code></pre>
<p>The code in this file lets us connect to Pusher using our App Keys. The <code>value</code> is a randomly generated number that gets sent as JSON to our Pusher channel. This number is what we use to show the company income in each new year which is automatically generated in our chart.</p>
<p>This update occurs every 10 seconds and can be changed in the <code>setInterval</code> function. Pusher's free plans are limited to 200,000 messages a day, so be careful while reducing the interval or you risk exceeding your limit too soon.</p>
<p>The next file we will work on is the <code>ChartButton.js</code> file which you can find inside its folder <code>ChartButton</code>.</p>
<p>Add this code to the file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ChartButton</span>(<span class="hljs-params">{ chartRef, type, name }</span>) </span>{
  <span class="hljs-keyword">const</span> switchToLineChart = <span class="hljs-function">() =&gt;</span> {
    chartRef.current.update({
      <span class="hljs-attr">chart</span>: {
        <span class="hljs-attr">type</span>: type,
      },
    });
  };

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">button</span>
      <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =&gt;</span> switchToLineChart()}
      className="bg-indigo-500 hover:bg-indigo-700 p-2 rounded text-white shadow-md"
    &gt;
      {name}
    <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span></span>
  );
}
</code></pre>
<p>Our chart application has three buttons for Area Chart, Bar Chart, and Line Chart. This is basically just the component that creates the buttons.</p>
<p>Good, up next will be our <code>CompanyIncomeChart.js</code> file which you can find in the <code>CompanyIncomeChart</code> folder. This file will be taking this code here:</p>
<pre><code class="lang-javascript"><span class="hljs-string">'use client'</span>;

<span class="hljs-keyword">import</span> { useEffect, useRef, useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> axios <span class="hljs-keyword">from</span> <span class="hljs-string">'axios'</span>;
<span class="hljs-keyword">import</span> ChartButton <span class="hljs-keyword">from</span> <span class="hljs-string">'../ChartButton/ChartButton'</span>;
<span class="hljs-keyword">import</span> Toggle3DButton <span class="hljs-keyword">from</span> <span class="hljs-string">'../Toggle3DButton/Toggle3DButton'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">CompanyIncome</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> initialData = [
    [<span class="hljs-number">1965</span>, <span class="hljs-number">360202</span>],
    [<span class="hljs-number">1966</span>, <span class="hljs-number">400123</span>],
    [<span class="hljs-number">1967</span>, <span class="hljs-number">460331</span>],
    [<span class="hljs-number">1968</span>, <span class="hljs-number">460346</span>],
    [<span class="hljs-number">1969</span>, <span class="hljs-number">460339</span>],
    [<span class="hljs-number">1970</span>, <span class="hljs-number">460370</span>],
  ];

  <span class="hljs-keyword">const</span> chartRef = useRef(<span class="hljs-literal">null</span>);
  <span class="hljs-keyword">const</span> [chartData, setChartData] = useState([...initialData]);
  <span class="hljs-keyword">const</span> [toggle3D, setToggle3D] = useState(<span class="hljs-literal">false</span>);

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-comment">// Dynamically import Highcharts and its 3D module</span>
    <span class="hljs-keyword">import</span>(<span class="hljs-string">'highcharts/highcharts-3d'</span>).then(<span class="hljs-function">(<span class="hljs-params">Highcharts3D</span>) =&gt;</span> {
      <span class="hljs-keyword">import</span>(<span class="hljs-string">'highcharts'</span>).then(<span class="hljs-function">(<span class="hljs-params">Highcharts</span>) =&gt;</span> {
        Highcharts3D.default(Highcharts.default);

        chartRef.current = Highcharts.default.chart(<span class="hljs-string">'chart-container'</span>, {
          <span class="hljs-attr">colors</span>: [<span class="hljs-string">'#F3F7FB'</span>, <span class="hljs-string">'#F3F7FB'</span>],
          <span class="hljs-attr">chart</span>: {
            <span class="hljs-attr">style</span>: {
              <span class="hljs-attr">fontFamily</span>: [<span class="hljs-string">'Prompt'</span>, <span class="hljs-string">'sans-serif'</span>],
              <span class="hljs-attr">fontSize</span>: <span class="hljs-string">'16px'</span>,
            },
            <span class="hljs-attr">type</span>: <span class="hljs-string">'column'</span>,
            <span class="hljs-attr">options3d</span>: {
              <span class="hljs-attr">enabled</span>: toggle3D,
              <span class="hljs-attr">alpha</span>: <span class="hljs-number">10</span>,
              <span class="hljs-attr">beta</span>: <span class="hljs-number">25</span>,
              <span class="hljs-attr">depth</span>: <span class="hljs-number">70</span>,
              <span class="hljs-attr">viewDistance</span>: <span class="hljs-number">25</span>,
            },
            <span class="hljs-attr">backgroundColor</span>: {
              <span class="hljs-attr">linearGradient</span>: [<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">500</span>, <span class="hljs-number">500</span>],
              <span class="hljs-attr">stops</span>: [
                [<span class="hljs-number">0</span>, <span class="hljs-string">'rgb(128, 130, 221)'</span>],
                [<span class="hljs-number">1</span>, <span class="hljs-string">'rgb(128, 130, 221)'</span>],
              ],
            },
          },
          <span class="hljs-attr">title</span>: {
            <span class="hljs-attr">text</span>: <span class="hljs-string">'COMPANY ANNUAL INCOME'</span>,
            <span class="hljs-attr">style</span>: {
              <span class="hljs-attr">fontSize</span>: <span class="hljs-string">'27px'</span>,
            },
          },
          <span class="hljs-attr">xAxis</span>: {
            <span class="hljs-attr">title</span>: {
              <span class="hljs-attr">text</span>: <span class="hljs-string">'Year'</span>,
              <span class="hljs-attr">style</span>: {
                <span class="hljs-attr">color</span>: <span class="hljs-string">'white'</span>,
              },
            },
            <span class="hljs-attr">labels</span>: {
              <span class="hljs-attr">style</span>: {
                <span class="hljs-attr">color</span>: <span class="hljs-string">'white'</span>,
              },
            },
            <span class="hljs-attr">type</span>: <span class="hljs-string">'category'</span>,
          },
          <span class="hljs-attr">yAxis</span>: {
            <span class="hljs-attr">title</span>: {
              <span class="hljs-attr">text</span>: <span class="hljs-string">'Income'</span>,
              <span class="hljs-attr">style</span>: {
                <span class="hljs-attr">color</span>: <span class="hljs-string">'white'</span>,
              },
            },
            <span class="hljs-attr">labels</span>: {
              <span class="hljs-attr">style</span>: {
                <span class="hljs-attr">color</span>: <span class="hljs-string">'white'</span>,
              },
            },
            <span class="hljs-attr">min</span>: <span class="hljs-number">0</span>,
          },
          <span class="hljs-attr">credits</span>: {
            <span class="hljs-attr">text</span>: <span class="hljs-string">''</span>,
          },
          <span class="hljs-attr">series</span>: [
            {
              <span class="hljs-attr">name</span>: <span class="hljs-string">'Income'</span>,
              <span class="hljs-attr">data</span>: chartData,
            },
          ],
          <span class="hljs-attr">animation</span>: {
            <span class="hljs-attr">duration</span>: <span class="hljs-number">100</span>,
          },
        });
      });
    });

    <span class="hljs-keyword">const</span> interval = <span class="hljs-built_in">setInterval</span>(<span class="hljs-keyword">async</span> () =&gt; {
      <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> axios.get(<span class="hljs-string">'/api/pusher'</span>);
        <span class="hljs-keyword">const</span> newDataPoint = [
          chartData[chartData.length - <span class="hljs-number">1</span>][<span class="hljs-number">0</span>] + <span class="hljs-number">1</span>,
          response.data.value,
        ];
        chartRef.current.series[<span class="hljs-number">0</span>].addPoint(newDataPoint, <span class="hljs-literal">true</span>, <span class="hljs-literal">true</span>);
        setChartData(<span class="hljs-function">(<span class="hljs-params">prevData</span>) =&gt;</span> [...prevData, newDataPoint]);
      } <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error fetching data:'</span>, error);
      }
      <span class="hljs-comment">// Every ten seconds, the setInterval method will get data from the API because its value is set to ten seconds. Pusher's free plans are limited to 200,000 messages a day, so be careful while reducing the interval or you risk exceeding your limit too soon.</span>
    }, <span class="hljs-number">10000</span>);
    <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> {
      <span class="hljs-built_in">clearInterval</span>(interval);
    };
  }, [toggle3D]);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"chart-container"</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-full h-96"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"border-solid border-2 border-indigo-500 m-10"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"grid justify-center mt-5 text-center"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span>&gt;</span>3D Mode<span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"w-20"</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">Toggle3DButton</span> <span class="hljs-attr">toggle3D</span>=<span class="hljs-string">{toggle3D}</span> <span class="hljs-attr">setToggle3D</span>=<span class="hljs-string">{setToggle3D}</span> /&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"grid gap-4 justify-center mt-5 xl:grid-cols-3 p-4"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">ChartButton</span> <span class="hljs-attr">chartRef</span>=<span class="hljs-string">{chartRef}</span> <span class="hljs-attr">type</span>=<span class="hljs-string">{</span>'<span class="hljs-attr">area</span>'} <span class="hljs-attr">name</span>=<span class="hljs-string">{</span>'<span class="hljs-attr">Area</span> <span class="hljs-attr">Chart</span>'} /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">ChartButton</span> <span class="hljs-attr">chartRef</span>=<span class="hljs-string">{chartRef}</span> <span class="hljs-attr">type</span>=<span class="hljs-string">{</span>'<span class="hljs-attr">bar</span>'} <span class="hljs-attr">name</span>=<span class="hljs-string">{</span>'<span class="hljs-attr">Bar</span> <span class="hljs-attr">Chart</span>'} /&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">ChartButton</span> <span class="hljs-attr">chartRef</span>=<span class="hljs-string">{chartRef}</span> <span class="hljs-attr">type</span>=<span class="hljs-string">{</span>'<span class="hljs-attr">line</span>'} <span class="hljs-attr">name</span>=<span class="hljs-string">{</span>'<span class="hljs-attr">Line</span> <span class="hljs-attr">Chart</span>'} /&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p>This component creates our Company Income Chart and is already set with some initial loading data so that the chart loads some data when it first starts. </p>
<p>The file contains our chart configuration settings. There is a function that does a GET request to our Next.js backend which then connects to our Pusher account. This is how the data is retrieved for our chart file. Like on the backend, there is a <code>setInterval</code> function that runs every 10 seconds to get the latest data from our backend (we we can increase or decrease).</p>
<p>Next is the <code>Toggle3DButton.js</code> file in the <code>Toggle3DButton</code> folder. Here is the code for the file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Toggle3DButton</span>(<span class="hljs-params">{ toggle3D, setToggle3D }</span>) </span>{
  <span class="hljs-keyword">const</span> toggle3DMode = <span class="hljs-function">() =&gt;</span> {
    setToggle3D(!toggle3D);
  };

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex items-center cursor-pointer border border-gray-400 rounded-full p-1 relative"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
        <span class="hljs-attr">type</span>=<span class="hljs-string">"checkbox"</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">"hidden"</span>
        <span class="hljs-attr">checked</span>=<span class="hljs-string">{toggle3D}</span>
        <span class="hljs-attr">onChange</span>=<span class="hljs-string">{toggle3DMode}</span>
      /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">span</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">toggle__line</span> <span class="hljs-attr">w-full</span> <span class="hljs-attr">h-4</span> <span class="hljs-attr">bg-gray-400</span> <span class="hljs-attr">rounded-full</span> <span class="hljs-attr">shadow-inner</span> ${
          <span class="hljs-attr">toggle3D</span> ? '<span class="hljs-attr">bg-green-500</span>' <span class="hljs-attr">:</span> '<span class="hljs-attr">bg-gray-400</span>'
        }`}
      &gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">span</span>
        <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">toggle__dot</span> <span class="hljs-attr">absolute</span> <span class="hljs-attr">w-6</span> <span class="hljs-attr">h-6</span> <span class="hljs-attr">bg-white</span> <span class="hljs-attr">rounded-full</span> <span class="hljs-attr">shadow</span> <span class="hljs-attr">inset-y-0</span> ${
          <span class="hljs-attr">toggle3D</span> ? '<span class="hljs-attr">right-0</span>' <span class="hljs-attr">:</span> '<span class="hljs-attr">left-0</span>'
        }`}
      &gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">span</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span></span>
  );
}
</code></pre>
<p>Our application has a 3D Mode toggle and this component is used to create it.</p>
<p>The main files are completed – we just have three left and then we're done. Next is the <code>globals.css</code> file which needs this code to replace the existing code:</p>
<pre><code class="lang-css"><span class="hljs-keyword">@import</span> url(<span class="hljs-string">'https://fonts.googleapis.com/css2?family=Bebas+Neue&amp;family=Prompt:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&amp;display=swap'</span>);

<span class="hljs-keyword">@tailwind</span> base;
<span class="hljs-keyword">@tailwind</span> components;
<span class="hljs-keyword">@tailwind</span> utilities;

<span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">font-family</span>: <span class="hljs-string">'Prompt'</span>, sans-serif;
  <span class="hljs-attribute">font-weight</span>: <span class="hljs-number">400</span>;
  <span class="hljs-attribute">font-style</span>: normal;
}
</code></pre>
<p>We just imported the <code>Prompt</code> font to use in our application.</p>
<p>Continuing on from before, now we have to update the <code>layout.js</code> file so that it also uses the <code>Prompt</code> font. Add this code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { Prompt } <span class="hljs-keyword">from</span> <span class="hljs-string">'next/font/google'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'./globals.css'</span>;

<span class="hljs-keyword">const</span> prompt = Prompt({ <span class="hljs-attr">subsets</span>: [<span class="hljs-string">'latin'</span>], <span class="hljs-attr">weight</span>: <span class="hljs-string">'400'</span> });

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> metadata = {
  <span class="hljs-attr">title</span>: <span class="hljs-string">'Create Next App'</span>,
  <span class="hljs-attr">description</span>: <span class="hljs-string">'Generated by create next app'</span>,
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">RootLayout</span>(<span class="hljs-params">{ children }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">body</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{prompt.className}</span>&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span></span>
  );
}
</code></pre>
<p>Now we can access the <code>Prompt</code> font throughout our application with this import and update.</p>
<p>All that remains is our <code>page.js</code> file which is the main entry point for all of our components. Replace all the code in that file with this final code shown here:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> CompanyIncomeChart <span class="hljs-keyword">from</span> <span class="hljs-string">'./components/CompanyIncomeChart/CompanyIncomeChart'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Home</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">CompanyIncomeChart</span> /&gt;</span></span>;
}
</code></pre>
<p>Alright all done – our project codebase is complete!</p>
<p>Just return to the root project folder and run the command below. Your application should be up and running in a web browser.</p>
<pre><code class="lang-shell">npm run dev
</code></pre>
<p>Everything should be working, and now you should have a realtime chart with live random dates.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Realtime communication platforms are essential for all of the tech that we use. Today you learned about Pusher and how it can help you build realtime data applications that can integrate with a chart library like HighCharts.</p>
<p>There are lots of potential applications you can build with these tools. Today's introduction should open the door and give you many ideas for future projects.</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ How to Deploy a Next.js App Using Fly.io and Tigris ]]>
                </title>
                <description>
                    <![CDATA[ In this tutorial, you're going to learn about the app deployment platform Fly.io and the globally distributed S3-compatible object storage service Tigris.  Both platforms are deeply connected, which makes them a great choice for your projects. You ge... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-deploy-a-nextjs-app-with-fly-io-and-tigris/</link>
                <guid isPermaLink="false">66be00c2779f84626329b874</guid>
                
                    <category>
                        <![CDATA[ AWS ]]>
                    </category>
                
                    <category>
                        <![CDATA[ deployment ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Next.js ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Andrew Baisden ]]>
                </dc:creator>
                <pubDate>Wed, 17 Apr 2024 21:24:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/04/fly-tigris-banner.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this tutorial, you're going to learn about the app deployment platform Fly.io and the globally distributed S3-compatible object storage service Tigris. </p>
<p>Both platforms are deeply connected, which makes them a great choice for your projects. You get the app deployment experience from Fly.io and the object storage features from Tigris. </p>
<p>App deployment is pretty self-explanatory, so let's first get a quick introduction into bucket storage, which Tigris uses.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<ul>
<li>An IDE/Code editor installed like <a target="_blank" href="https://code.visualstudio.com/">Visual Studio Code</a></li>
<li><a target="_blank" href="https://nodejs.org/en">Node.js and npm</a> installed</li>
<li><a target="_blank" href="https://nextjs.org/">Next.js</a> installed or setup</li>
<li>A free account on <a target="_blank" href="https://fly.io/">Fly.io</a></li>
<li>A free account on <a target="_blank" href="https://www.tigrisdata.com/">Tigris</a></li>
</ul>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><a class="post-section-overview" href="#heading-what-is-bucket-storage">What is Bucket Storage?</a></li>
<li><a class="post-section-overview" href="#heading-what-well-be-building">What We'll Be Building</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-an-account-on-flyio-and-tigris">How to Create an Account on Fly.io and Tigris</a></li>
<li><a class="post-section-overview" href="#heading-how-to-set-up-the-user-database-project">How to Set Up the User Database Project</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-user-database-application">How to Build the User Database Application</a></li>
<li><a class="post-section-overview" href="#heading-how-to-create-the-user-database-ui">How to Create the User Database UI</a></li>
<li><a class="post-section-overview" href="#how-to-deploy-our-app-to-fly-io">How to Deploy Your App to Fly.io</a></li>
<li><a class="post-section-overview" href="#heading-conclusion">Conclusion</a></li>
</ol>
<h2 id="heading-what-is-bucket-storage">What is Bucket Storage?</h2>
<p>An Amazon S3 bucket is a resource for public cloud storage that is accessible via the Simple Storage Service (S3) platform of Amazon Web Services (AWS). </p>
<p>Low-latency storage is a feature that the globally distributed, S3-compatible object storage service Tigris uses. This means that you can access Amazon's S3 buckets on Tigris for your storage needs. </p>
<p>Tigris has also been fully integrated directly with Fly.io and is also completely integrated with flyctl which operates on hardware from Fly.io. Fly.io's command-line interface, flyctl, allows you to deal with the platform from account creation to application deployment.</p>
<h2 id="heading-what-well-be-building">What We'll Be Building</h2>
<p>To learn the fundamentals of these platforms, we'll build a user database application. It's pretty straightforward: essentially we can perform full CRUD requests which means being able to read, add, update, and delete our user data. </p>
<p>Next.js will be our main framework because it allows us to build full-stack apps without having to create a separate server.</p>
<p><img src="https://res.cloudinary.com/d74fh3kw/image/upload/v1710878134/user-database_h2gfd8.png" alt="User Database App" width="3134" height="2648" loading="lazy">
<em>User Database application home screen</em></p>
<p>You can learn more about <a target="_blank" href="https://fly.io/">Fly.io</a> and <a target="_blank" href="https://www.tigrisdata.com/">Tigris</a> from their documentation. We will need to create an account on both platforms for this project, which I'll walk you through in a minute. </p>
<p>So now, with the theory out of the way, let's get started.</p>
<p>For this project, you can find the codebase <a target="_blank" href="https://github.com/andrewbaisden/fly-tigris-user-database">on my GitHub here</a>.</p>
<h2 id="heading-how-to-create-an-account-on-flyio-and-tigris">How to Create an Account on Fly.io and Tigris</h2>
<p>Just follow these steps to get up and running on both platforms:</p>
<ol>
<li>Firstly you need to create an account on <a target="_blank" href="https://fly.io/">Fly.io</a>, because to utilise Tigris you'll need a Fly.io account.</li>
<li>Next, install the <a target="_blank" href="https://fly.io/docs/hands-on/install-flyctl/">flyctl</a> command line tool on your computer which is essential for setting up your account to deploy your applications.</li>
</ol>
<p>Ok, let's move on to the next stage which is where we will set up our project as well as our Tigris bucket storage.</p>
<h2 id="heading-how-to-set-up-the-user-database-project">How to Set Up the User Database Project</h2>
<p>Start by navigating to a directory on your computer where you plan to create the project. Then create a folder called <code>fly-tigris-user-database</code> and <code>cd</code> into it. Now run the command to setup a Next.js project inside of that folder:</p>
<pre><code class="lang-shell">npx create-next-app .
</code></pre>
<p>All we are doing is setting up our Next.js project and it's important that for the configuration you select yes for Tailwind CSS and the App router because we will need them in this project.</p>
<p>Now run this command to install the AWS SDK:</p>
<pre><code class="lang-shell">npm install @aws-sdk/client-s3
</code></pre>
<p>We just have one package to install (<code>@aws-sdk/client-s3</code>) which we need for connecting to our bucket.  </p>
<p>Ok, good – now it is time to create a bucket for the project we just created. You can refer to their <a target="_blank" href="https://www.tigrisdata.com/docs/get-started/">official documentation here</a>.</p>
<p>Just run this command to create a bucket:</p>
<pre><code class="lang-shell">fly storage create
</code></pre>
<p>Now on the setup screen, choose a name for your bucket. The name should be unique, so you can't use a name that someone else has chosen. </p>
<p>Alright now for the most important stage: you should have your AWS and bucket secrets like the example here:</p>
<pre><code class="lang-shell">AWS_ACCESS_KEY_ID: yourkey
AWS_ENDPOINT_URL_S3: https://fly.storage.tigris.dev
AWS_REGION: auto
AWS_SECRET_ACCESS_KEY: your secret access
BUCKET_NAME: your bucket name
</code></pre>
<p>Create a <code>.env.local</code> file inside the root of your Next.js project and copy and paste all of those secret environment variables. </p>
<p>To get these environment variables to work properly inside our Next.js application, we need to adjust their names by making them public. See the example below and make the change to your <code>.env.local</code> file. </p>
<pre><code class="lang-shell">NEXT_PUBLIC_SECRET_AWS_ACCESS_KEY_ID: yourkey
NEXT_PUBLIC_SECRET_AWS_ENDPOINT_URL_S3: https://fly.storage.tigris.dev
NEXT_PUBLIC_SECRET_AWS_REGION: auto
NEXT_PUBLIC_SECRET_AWS_SECRET_ACCESS_KEY: your secret access
NEXT_PUBLIC_SECRET_BUCKET_NAME: your bucket name
</code></pre>
<p>Right now on the Tigris documentation page, if you click on the dashboard button and sign into your account you should see your newly created bucket like in my example shown here:</p>
<p><img src="https://res.cloudinary.com/d74fh3kw/image/upload/v1710881025/tigris_w3n8zm.jpg" alt="Tigris buckets" width="1724" height="577" loading="lazy">
<em>Tigris website buckets management page</em></p>
<p>Great! That's the first phase done. We now have a bucket to store our app data online, so we can get started creating our application in the next section.</p>
<h2 id="heading-how-to-build-the-user-database-application">How to Build the User Database Application</h2>
<p>I'll split this part into two sections. First, we'll get our server built and running so that we can test out the CRUD endpoints. Then we'll finish off by building our front end.</p>
<h3 id="heading-how-to-create-the-user-database-server">How to Create the User Database Server</h3>
<p>To begin with, let's create our backend architecture. We are going to be creating four endpoints, so one for each CRUD request. We also need a helper file which will have some functions for getting users from our object storage. </p>
<p>If you have not already done so, <code>cd</code> into the root of the project and run the commands below. They will set up all of our files and folders quickly:</p>
<pre><code class="lang-shell">cd src/app
mkdir api
mkdir api/deleteuser api/getusers api/postuser api/updateuser
touch api/deleteuser/route.js
touch api/getusers/route.js
touch api/postuser/route.js
touch api/updateuser/route.js
mkdir helpers
touch helpers/getUsers.js
</code></pre>
<p>Good, that was quick. Now we just have to add the code to our five files and our backend API will be ready to test.</p>
<p>Let's do the helpers file first. The code in this file allows us to collect and access the user data stored in our S3 bucket on Tigris.   </p>
<p>Put the below code inside of <code>helpers/getUsers.js</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> {
  S3Client,
  ListObjectsV2Command,
  GetObjectCommand,
} <span class="hljs-keyword">from</span> <span class="hljs-string">'@aws-sdk/client-s3'</span>;

<span class="hljs-keyword">const</span> streamToString = <span class="hljs-function">(<span class="hljs-params">stream</span>) =&gt;</span>
  <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> chunks = [];
    stream.on(<span class="hljs-string">'data'</span>, <span class="hljs-function">(<span class="hljs-params">chunk</span>) =&gt;</span> chunks.push(chunk));
    stream.on(<span class="hljs-string">'error'</span>, reject);
    stream.on(<span class="hljs-string">'end'</span>, <span class="hljs-function">() =&gt;</span> resolve(Buffer.concat(chunks).toString(<span class="hljs-string">'utf8'</span>)));
  });

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fetchAllUsersFromS3</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> s3 = <span class="hljs-keyword">new</span> S3Client({
      <span class="hljs-attr">region</span>: process.env.NEXT_PUBLIC_SECRET_AWS_REGION,
      <span class="hljs-attr">endpoint</span>: process.env.NEXT_PUBLIC_SECRET_AWS_ENDPOINT_URL_S3,
      <span class="hljs-attr">credentials</span>: {
        <span class="hljs-attr">accessKeyId</span>: process.env.NEXT_PUBLIC_SECRET_AWS_ACCESS_KEY_ID,
        <span class="hljs-attr">secretAccessKey</span>: process.env.NEXT_PUBLIC_SECRET_AWS_SECRET_ACCESS_KEY,
      },
    });

    <span class="hljs-keyword">const</span> commandDetails = <span class="hljs-keyword">new</span> ListObjectsV2Command({
      <span class="hljs-attr">Bucket</span>: process.env.NEXT_PUBLIC_SECRET_BUCKET_NAME,
      <span class="hljs-attr">MaxKeys</span>: <span class="hljs-number">10</span>,
    });
    <span class="hljs-keyword">const</span> { Contents } = <span class="hljs-keyword">await</span> s3.send(commandDetails);
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'List Result'</span>, Contents);
    <span class="hljs-keyword">if</span> (!Contents) {
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'no users'</span>);
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">const</span> users = <span class="hljs-keyword">await</span> <span class="hljs-built_in">Promise</span>.all(
        Contents.map(<span class="hljs-keyword">async</span> (item) =&gt; {
          <span class="hljs-keyword">const</span> getObject = <span class="hljs-keyword">new</span> GetObjectCommand({
            <span class="hljs-attr">Bucket</span>: process.env.NEXT_PUBLIC_SECRET_BUCKET_NAME,
            <span class="hljs-attr">Key</span>: item.Key,
          });

          <span class="hljs-keyword">const</span> { Body } = <span class="hljs-keyword">await</span> s3.send(getObject);
          <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> streamToString(Body);
          <span class="hljs-keyword">const</span> userObject = <span class="hljs-built_in">JSON</span>.parse(data);
          <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Data'</span>, data);
          <span class="hljs-keyword">return</span> userObject;
        })
      );
      <span class="hljs-keyword">return</span> users;
    }
  } <span class="hljs-keyword">catch</span> (e) {
    <span class="hljs-built_in">console</span>.error(e);
    <span class="hljs-keyword">throw</span> e;
  }
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getUserById</span>(<span class="hljs-params">users, userId</span>) </span>{
  <span class="hljs-keyword">if</span> (!users) {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'no users'</span>);
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">return</span> users.find(<span class="hljs-function">(<span class="hljs-params">user</span>) =&gt;</span> user.id === userId);
  }
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getUserByIdEmail</span>(<span class="hljs-params">users, email</span>) </span>{
  <span class="hljs-keyword">if</span> (!users) {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'no users'</span>);
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">return</span> users.find(
      <span class="hljs-function">(<span class="hljs-params">user</span>) =&gt;</span> user.email.toLowerCase() === email.toLowerCase()
    );
  }
}
</code></pre>
<p>The main function is <code>fetchAllUsersFromS3()</code>, which basically creates the S3 client with the credentials and configuration that we need. It uses the <code>GetObjectCommand</code> to get the objects contents which are then converted from a stream to a string using the <code>streamToString</code> function. The JSON data is then parsed with the user objects returned.</p>
<p>The other two function calls, <code>getUserById(users, userId)</code> and <code>getUserByIdEmail(users, email)</code>, are helper functions that allow us to search for people in our S3 bucket based on their ID or email address. The code stores the essential AWS configuration parameters in environment variables, including the region, endpoint URL, access key, and secret access key, as well as the S3 bucket name.  </p>
<p>Alright, just the routes left now.  </p>
<p>Start by putting this code into <code>getusers/route.js</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> {
S3Client,
ListObjectsV2Command,
GetObjectCommand,
} <span class="hljs-keyword">from</span> <span class="hljs-string">'@aws-sdk/client-s3'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">GET</span>(<span class="hljs-params"></span>) </span>{
<span class="hljs-keyword">const</span> streamToString = <span class="hljs-function">(<span class="hljs-params">stream</span>) =&gt;</span>
<span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =&gt;</span> {
<span class="hljs-keyword">const</span> chunks = [];
stream.on(<span class="hljs-string">'data'</span>, <span class="hljs-function">(<span class="hljs-params">chunk</span>) =&gt;</span> chunks.push(chunk));
stream.on(<span class="hljs-string">'error'</span>, reject);
stream.on(<span class="hljs-string">'end'</span>, <span class="hljs-function">() =&gt;</span> resolve(Buffer.concat(chunks).toString(<span class="hljs-string">'utf8'</span>)));
});

<span class="hljs-keyword">try</span> {
<span class="hljs-keyword">const</span> s3 = <span class="hljs-keyword">new</span> S3Client({
<span class="hljs-attr">region</span>: process.env.NEXT_PUBLIC_SECRET_AWS_REGION,
<span class="hljs-attr">endpoint</span>: process.env.NEXT_PUBLIC_SECRET_AWS_ENDPOINT_URL_S3,
<span class="hljs-attr">credentials</span>: {
<span class="hljs-attr">accessKeyId</span>: process.env.NEXT_PUBLIC_SECRET_AWS_ACCESS_KEY_ID,
<span class="hljs-attr">secretAccessKey</span>: process.env.NEXT_PUBLIC_SECRET_AWS_SECRET_ACCESS_KEY,
},
});

<span class="hljs-keyword">const</span> listParams = {
<span class="hljs-attr">Bucket</span>: process.env.NEXT_PUBLIC_SECRET_BUCKET_NAME,
<span class="hljs-attr">MaxKeys</span>: <span class="hljs-number">10</span>,
};

<span class="hljs-keyword">const</span> list = <span class="hljs-keyword">new</span> ListObjectsV2Command(listParams);
<span class="hljs-keyword">const</span> { Contents } = <span class="hljs-keyword">await</span> s3.send(list);

<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'List Result'</span>, Contents);

<span class="hljs-keyword">if</span> (!Contents || Contents.length === <span class="hljs-number">0</span>) {
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'No users found'</span>);
<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Response(<span class="hljs-built_in">JSON</span>.stringify({ <span class="hljs-attr">error</span>: <span class="hljs-string">'No users found'</span> }), {
<span class="hljs-attr">status</span>: <span class="hljs-number">404</span>,
});
}

<span class="hljs-keyword">const</span> users = <span class="hljs-keyword">await</span> <span class="hljs-built_in">Promise</span>.all(
Contents.map(<span class="hljs-keyword">async</span> (item) =&gt; {
<span class="hljs-keyword">const</span> getObjectParams = {
<span class="hljs-attr">Bucket</span>: process.env.NEXT_PUBLIC_SECRET_BUCKET_NAME,
<span class="hljs-attr">Key</span>: item.Key,
};

<span class="hljs-keyword">const</span> getObject = <span class="hljs-keyword">new</span> GetObjectCommand(getObjectParams);
<span class="hljs-keyword">const</span> { Body } = <span class="hljs-keyword">await</span> s3.send(getObject);
<span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> streamToString(Body);
<span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Backend API GET Data:'</span>, data);
<span class="hljs-keyword">return</span> <span class="hljs-built_in">JSON</span>.parse(data);
})
);

<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Response(<span class="hljs-built_in">JSON</span>.stringify(users), { <span class="hljs-attr">status</span>: <span class="hljs-number">200</span> });
} <span class="hljs-keyword">catch</span> (e) {
<span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error:'</span>, e);
<span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Response(
<span class="hljs-built_in">JSON</span>.stringify({ <span class="hljs-attr">error</span>: e.message || <span class="hljs-string">'Unknown error'</span> }),
{ <span class="hljs-attr">status</span>: <span class="hljs-number">500</span> }
);
}
}
</code></pre>
<p>In the code for this file, we retrieve the user data from our Tigris S3 bucket and send it back as a JSON response via the Next.js API route handler function.</p>
<p>The function also imports the AWS SDK clients required to communicate with our S3 bucket on Tigris. When the API route is requested, the main entry point, the <code>GET</code> function, is called. Using environment variables, the <code>GET</code> method first establishes an S3 client with the required setup for region, endpoint, and credentials. </p>
<p>After that, a <code>ListObjectsV2Command</code> is created to retrieve the user data list of items from the designated S3 bucket. The method then iterates through the object list, retrieving each object's data with the <code>GetObjectCommand</code>. </p>
<p>The <code>streamToString</code> method is used to convert each object's contents from a stream to a string. </p>
<p>Following parsing of the JSON input, user objects are sent out as a JSON response. A 404 error response is returned if no users are detected, and the function provides a 500 error response with the error message if there are any issues during the process.  </p>
<p>Up next is the POST route, so put this code in <code>postuser/route.js</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { fetchAllUsersFromS3, getUserByIdEmail } <span class="hljs-keyword">from</span> <span class="hljs-string">'../../helpers/getUsers'</span>;

<span class="hljs-keyword">import</span> { S3Client, PutObjectCommand } <span class="hljs-keyword">from</span> <span class="hljs-string">'@aws-sdk/client-s3'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">POST</span>(<span class="hljs-params">req</span>) </span>{
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> { firstname, lastname, email, password } = <span class="hljs-keyword">await</span> req.json();
    <span class="hljs-keyword">const</span> id = crypto.randomUUID();
    <span class="hljs-keyword">const</span> data = { firstname, lastname, email, password, id };
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Request body data'</span>, data);
    <span class="hljs-keyword">const</span> allUsers = <span class="hljs-keyword">await</span> fetchAllUsersFromS3();
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'all users'</span>, allUsers);
    <span class="hljs-keyword">const</span> existingUser = <span class="hljs-keyword">await</span> getUserByIdEmail(allUsers, email);
    <span class="hljs-built_in">console</span>.log(existingUser, email);
    <span class="hljs-keyword">if</span> (existingUser) {
      <span class="hljs-keyword">return</span> Response.json({
        <span class="hljs-attr">error</span>: <span class="hljs-string">'Email address already in use'</span>,
      });
    }

    <span class="hljs-keyword">const</span> s3 = <span class="hljs-keyword">new</span> S3Client({
      <span class="hljs-attr">region</span>: process.env.NEXT_PUBLIC_SECRET_AWS_REGION,
      <span class="hljs-attr">endpoint</span>: process.env.NEXT_PUBLIC_SECRET_AWS_ENDPOINT_URL_S3,
      <span class="hljs-attr">credentials</span>: {
        <span class="hljs-attr">accessKeyId</span>: process.env.NEXT_PUBLIC_SECRET_AWS_ACCESS_KEY_ID,
        <span class="hljs-attr">secretAccessKey</span>: process.env.NEXT_PUBLIC_SECRET_AWS_SECRET_ACCESS_KEY,
      },
    });

    <span class="hljs-keyword">const</span> commandDetails = <span class="hljs-keyword">new</span> PutObjectCommand({
      <span class="hljs-attr">Body</span>: <span class="hljs-built_in">JSON</span>.stringify(data),
      <span class="hljs-attr">Bucket</span>: process.env.NEXT_PUBLIC_SECRET_BUCKET_NAME,
      <span class="hljs-attr">Key</span>: email,
    });

    <span class="hljs-keyword">await</span> s3.send(commandDetails);

    <span class="hljs-keyword">return</span> Response.json({ <span class="hljs-attr">message</span>: <span class="hljs-string">'User added'</span> });
  } <span class="hljs-keyword">catch</span> (e) {
    <span class="hljs-built_in">console</span>.error(e);
    <span class="hljs-keyword">return</span> Response.json({ <span class="hljs-attr">error</span>: <span class="hljs-string">'Failed to create user'</span> });
  }
}
</code></pre>
<p>This code manages user registration and saves user information in our Tigris S3 bucket. The two helper functions which are needed to communicate with the S3 bucket and collect user data are imported by the function <code>fetchAllUsersFromS3</code> and <code>getUserByIdEmail</code>. The initial entry point, which is triggered when a <code>POST</code> request is made to access the API route, is the <code>POST</code> function. </p>
<p>The user's information (first and last name, email address, and password) is initially taken from the request body via the <code>POST</code> function. It then uses <code>crypto.randomUUID()</code> to create a unique user ID. To obtain all of the current user data from the S3 bucket, the method then invokes the <code>fetchAllUsersFromS3</code> helper function.  </p>
<p>The code uses the <code>getUserByIdEmail</code> helper function to determine whether the email address supplied in the request already exists in the user data. If the email address already exists, the method provides a JSON response including an error message. If the email address is unique, the method uses environment variables to generate an S3 client with the required setup with the region, endpoint, and credentials. </p>
<p>It then generates a <code>PutObjectCommand</code> that uploads the new user data (as a JSON string) to the S3 bucket, with the email address as the key. Finally, the method produces a JSON response confirming that the user has been successfully added. If an error occurs during the process, the function provides a JSON response including an error message.  </p>
<p>Follow that up with our UPDATE route and this is the code which goes into <code>updateuser/route.js</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { getUserById, fetchAllUsersFromS3 } <span class="hljs-keyword">from</span> <span class="hljs-string">'../../helpers/getUsers'</span>;

<span class="hljs-keyword">import</span> {
  S3Client,
  DeleteObjectCommand,
  PutObjectCommand,
} <span class="hljs-keyword">from</span> <span class="hljs-string">'@aws-sdk/client-s3'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">PUT</span>(<span class="hljs-params">req</span>) </span>{
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> { firstname, lastname, email, originalEmail, id } = <span class="hljs-keyword">await</span> req.json();
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'request data'</span>, firstname, lastname, email, originalEmail, id);
    <span class="hljs-keyword">const</span> allUsers = <span class="hljs-keyword">await</span> fetchAllUsersFromS3();
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'all users'</span>, allUsers);
    <span class="hljs-keyword">const</span> userToUpdate = <span class="hljs-keyword">await</span> getUserById(allUsers, id);
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'user to update'</span>, userToUpdate);
    <span class="hljs-keyword">const</span> user = allUsers.find(<span class="hljs-function">(<span class="hljs-params">user</span>) =&gt;</span> user.id === id);
    <span class="hljs-keyword">const</span> userEmail = user ? user.email : <span class="hljs-literal">null</span>;
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'User Email'</span>, userEmail);
    <span class="hljs-keyword">if</span> (!userToUpdate) {
      <span class="hljs-keyword">return</span> Response.json({ <span class="hljs-attr">error</span>: <span class="hljs-string">'User not found'</span> });
    }

    <span class="hljs-keyword">if</span> (!originalEmail || !email) {
      <span class="hljs-keyword">return</span> Response.json({
        <span class="hljs-attr">error</span>: <span class="hljs-string">'Both originalEmail and email are required for update'</span>,
      });
    }

    <span class="hljs-keyword">const</span> data = { firstname, lastname, email, id };

    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Updated data'</span>, data);

    <span class="hljs-keyword">const</span> s3 = <span class="hljs-keyword">new</span> S3Client({
      <span class="hljs-attr">region</span>: process.env.NEXT_PUBLIC_SECRET_AWS_REGION,
      <span class="hljs-attr">endpoint</span>: process.env.NEXT_PUBLIC_SECRET_AWS_ENDPOINT_URL_S3,
      <span class="hljs-attr">credentials</span>: {
        <span class="hljs-attr">accessKeyId</span>: process.env.NEXT_PUBLIC_SECRET_AWS_ACCESS_KEY_ID,
        <span class="hljs-attr">secretAccessKey</span>: process.env.NEXT_PUBLIC_SECRET_AWS_SECRET_ACCESS_KEY,
      },
    });

    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Original email'</span>, originalEmail);
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'New email'</span>, email);

    <span class="hljs-keyword">if</span> (userEmail === originalEmail) {
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'The emails are the same so its a match'</span>);
      <span class="hljs-keyword">const</span> deleteCommand = <span class="hljs-keyword">new</span> DeleteObjectCommand({
        <span class="hljs-attr">Bucket</span>: process.env.NEXT_PUBLIC_SECRET_BUCKET_NAME,
        <span class="hljs-attr">Key</span>: originalEmail,
      });

      <span class="hljs-keyword">await</span> s3.send(deleteCommand);
      <span class="hljs-keyword">const</span> putCommand = <span class="hljs-keyword">new</span> PutObjectCommand({
        <span class="hljs-attr">Body</span>: <span class="hljs-built_in">JSON</span>.stringify(data),
        <span class="hljs-attr">Bucket</span>: process.env.NEXT_PUBLIC_SECRET_BUCKET_NAME,
        <span class="hljs-attr">Key</span>: email,
      });

      <span class="hljs-keyword">await</span> s3.send(putCommand);

      <span class="hljs-keyword">return</span> Response.json({ <span class="hljs-attr">message</span>: <span class="hljs-string">'User updated successfully'</span> });
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Error: The emails do not match'</span>);
      <span class="hljs-keyword">return</span> Response.json({ <span class="hljs-attr">error</span>: <span class="hljs-string">'Failed to update user'</span> });
    }
  } <span class="hljs-keyword">catch</span> (e) {
    <span class="hljs-built_in">console</span>.error(e);
  }
}
</code></pre>
<p>The code includes the same helper functions from earlier, <code>getUserById</code> and <code>fetchAllUsersFromS3</code>, that are used to connect with the S3 bucket and obtain user data. The primary entry point is the <code>PUT</code> function, which is invoked anytime a <code>PUT</code> request is made to the API route. The <code>PUT</code> function first pulls user data (firstname, lastname, email, originalEmail, and id) from the request body. </p>
<p>It then invokes the <code>fetchAllUsersFromS3</code> helper function, which retrieves all existing user data from the S3 bucket. The code then locates the user to be updated by invoking the <code>getUserById</code> helper function with the specified user ID. If the user cannot be found, the method provides a JSON response with an error message.  </p>
<p>If the originalEmail or email is absent, the function provides a JSON response including an error message. The method then builds an updated user data object based on the information given. In addition the code can use environment variables to generate an S3 client with the required settings (region, endpoint, and credentials).   </p>
<p>Assuming the initial email address matches the current user's, the function updates the user's information, but if the original email and the current user's email address do not match, the method provides a JSON response including an error message. Like before if an error is detected during the process, then the function will create a log without sending a response.  </p>
<p>All thats left is our DELETE route. Add this code to <code>deleteuser/route.js</code>:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { S3Client, DeleteObjectCommand } <span class="hljs-keyword">from</span> <span class="hljs-string">'@aws-sdk/client-s3'</span>;

<span class="hljs-keyword">import</span> { fetchAllUsersFromS3, getUserById } <span class="hljs-keyword">from</span> <span class="hljs-string">'../../helpers/getUsers'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">DELETE</span>(<span class="hljs-params">req</span>) </span>{
  <span class="hljs-keyword">try</span> {
    <span class="hljs-keyword">const</span> id = <span class="hljs-keyword">await</span> req.json();
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Id'</span>, id.id);
    <span class="hljs-keyword">const</span> allUsers = <span class="hljs-keyword">await</span> fetchAllUsersFromS3();
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'all users'</span>, allUsers);
    <span class="hljs-keyword">const</span> userToDelete = <span class="hljs-keyword">await</span> getUserById(allUsers, id.id);
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'user to delete'</span>, userToDelete);

    <span class="hljs-keyword">if</span> (!userToDelete) {
      <span class="hljs-keyword">return</span> Response.json({ <span class="hljs-attr">error</span>: <span class="hljs-string">'User not found'</span> });
    }

    <span class="hljs-keyword">const</span> userEmail = userToDelete.email;
    <span class="hljs-keyword">const</span> s3 = <span class="hljs-keyword">new</span> S3Client({
      <span class="hljs-attr">region</span>: process.env.NEXT_PUBLIC_SECRET_AWS_REGION,
      <span class="hljs-attr">endpoint</span>: process.env.NEXT_PUBLIC_SECRET_AWS_ENDPOINT_URL_S3,
      <span class="hljs-attr">credentials</span>: {
        <span class="hljs-attr">accessKeyId</span>: process.env.NEXT_PUBLIC_SECRET_AWS_ACCESS_KEY_ID,
        <span class="hljs-attr">secretAccessKey</span>: process.env.NEXT_PUBLIC_SECRET_AWS_SECRET_ACCESS_KEY,
      },
    });

    <span class="hljs-keyword">const</span> deleteCommand = <span class="hljs-keyword">new</span> DeleteObjectCommand({
      <span class="hljs-attr">Bucket</span>: process.env.NEXT_PUBLIC_SECRET_BUCKET_NAME,
      <span class="hljs-attr">Key</span>: userEmail,
    });

    <span class="hljs-keyword">await</span> s3.send(deleteCommand);
    <span class="hljs-keyword">return</span> Response.json({ <span class="hljs-attr">message</span>: <span class="hljs-string">'User deleted successfully'</span> });
  } <span class="hljs-keyword">catch</span> (e) {
    <span class="hljs-built_in">console</span>.error(e);
    <span class="hljs-keyword">return</span> Response.json({ <span class="hljs-attr">error</span>: <span class="hljs-string">'Failed to delete user'</span> });
  }
}
</code></pre>
<p>This is used for removing data from our bucket. The code includes the same AWS SDK clients required for communicating with S3, as well as the two helper functions <code>fetchAllUsersFromS3</code> and <code>getUserById</code> (which are used for getting user data from the S3 bucket). The key entry point is the <code>DELETE</code> function, which is called when a <code>DELETE</code> request is made to the API route. </p>
<p>In the <code>DELETE</code> function, the user ID is initially extracted from the request body. It then invokes the <code>fetchAllUsersFromS3</code> helper method, which retrieves all existing user data from the S3 bucket. The code then locates the user to be removed by invoking the <code>getUserById</code> helper function with the specified user ID. If the client cannot be found, the method provides a JSON response with an error message.  </p>
<p>It then generates a <code>DeleteObjectCommand</code> that deletes the item from the S3 bucket, with the user's email address as the key. Finally, the method produces a JSON response confirming that the user has been successfully erased. Once again errors are logged to the console.  </p>
<p>Ok, good that's it – we're done with the backend. Start the server with the usual run code and test out those routes to make sure that you can connect to your bucket and use all of the CRUD requests:</p>
<pre><code>npm run dev
</code></pre><p>To test the backend, you can use an API testing tool like Postman. Take a look at the example screenshots for reference:</p>
<h5 id="heading-doing-get-requests">Doing GET Requests</h5>
<p><img src="https://res.cloudinary.com/d74fh3kw/image/upload/v1710885503/get-user-dashboard_od5nyy.png" alt="GET Requests" width="1628" height="548" loading="lazy">
<em>Using the Postman API app to do GET requests for our Tigris bucket</em></p>
<p>GET requests are pretty easy. Just go to <a target="_blank" href="http://localhost:3000/api/getusers">http://localhost:3000/api/getusers</a>.</p>
<h5 id="heading-doing-post-requests">Doing POST Requests</h5>
<p><img src="https://res.cloudinary.com/d74fh3kw/image/upload/v1710885503/post-user-dashboard_n01a4q.png" alt="POST Requests" width="1636" height="604" loading="lazy">
<em>Using the Postman API app to do POST requests for our Tigris bucket</em></p>
<p>POST requests can be done here: <a target="_blank" href="http://localhost:3000/api/postuser">http://localhost:3000/api/postuser</a>.</p>
<h5 id="heading-doing-put-requests">Doing PUT Requests</h5>
<p><img src="https://res.cloudinary.com/d74fh3kw/image/upload/v1710885503/put-user-dashboard_n2evps.png" alt="PUT Requests" width="1640" height="686" loading="lazy">
<em>Using the Postman API app to do PUT requests for our Tigris bucket</em></p>
<p>For PUT requests, go to this route: <a target="_blank" href="http://localhost:3000/api/updateuser">http://localhost:3000/api/updateuser</a>. It's important to note that you MUST put the original email address for that ID, otherwise it's not going to work. And remember this for the front end, too, because we've only implemented basic error handling.</p>
<h5 id="heading-doing-delete-requests">Doing DELETE Requests</h5>
<p><img src="https://res.cloudinary.com/d74fh3kw/image/upload/v1710885503/delete-user-dashboard_ebniqk.png" alt="DELETE Requests" width="1638" height="510" loading="lazy">
<em>Using the Postman API app to do DELETE requests for our Tigris bucket</em></p>
<p>DELETE requests can be done here: <a target="_blank" href="http://localhost:3000/api/deleteuser">http://localhost:3000/api/deleteuser</a>.</p>
<p>Great, our backend should be fully working. Now we just have the frontend left. Then we can deploy our app online to fly.io.</p>
<h2 id="heading-how-to-create-the-user-database-ui">How to Create the User Database UI</h2>
<p>Now for the front end, we need to create four components and four custom hooks to integrate with them, and each one is self-explanatory. The component files hold our form and table data whereas the hook files perform our CRUD requests and that's all.</p>
<p>Other than that we need to modify a few files so that we get our Tailwind CSS styles working – then we can finish with building our frontend components.</p>
<p>Before we start, run this script from the root project folder so we can set the project folder structure for our components and custom hooks:</p>
<pre><code class="lang-shell">cd src/app
mkdir -p components/AddUserForm
touch components/AddUserForm/AddUserForm.js
mkdir -p components/DeleteUserForm
touch components/DeleteUserForm/DeleteUserForm.js
mkdir -p components/UpdateUserForm
touch components/UpdateUserForm/UpdateUserForm.js
mkdir -p components/UserDatabaseTable
touch components/UserDatabaseTable/UserDatabaseTable.js
mkdir -p hooks
touch hooks/useDelete.js
touch hooks/useFetch.js
touch hooks/usePost.js
touch hooks/useUpdate.js
</code></pre>
<p>Now that the folders are done, let's quickly do some setup for Tailwind CSS and styling before we complete our codebase.</p>
<p>Replace all of the code in the <code>globals.css</code> file with this code which just sets a background colour for our app:</p>
<pre><code class="lang-css"><span class="hljs-keyword">@tailwind</span> base;
<span class="hljs-keyword">@tailwind</span> components;
<span class="hljs-keyword">@tailwind</span> utilities;

<span class="hljs-selector-tag">body</span> {
  <span class="hljs-attribute">background</span>: <span class="hljs-number">#eeeff1</span>;
  <span class="hljs-attribute">font-size</span>: <span class="hljs-number">16px</span>;
  <span class="hljs-attribute">color</span>: <span class="hljs-number">#0e0e0e</span>;
}
</code></pre>
<p>Now do the same for <code>layout.js</code> – we are just adding the Arsenal font to our project:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { Arsenal } <span class="hljs-keyword">from</span> <span class="hljs-string">'next/font/google'</span>;
<span class="hljs-keyword">import</span> <span class="hljs-string">'./globals.css'</span>;

<span class="hljs-keyword">const</span> arsenal = Arsenal({
  <span class="hljs-attr">weight</span>: <span class="hljs-string">'400'</span>,
  <span class="hljs-attr">subsets</span>: [<span class="hljs-string">'latin'</span>],
});

<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> metadata = {
  <span class="hljs-attr">title</span>: <span class="hljs-string">'Create Next App'</span>,
  <span class="hljs-attr">description</span>: <span class="hljs-string">'Generated by create next app'</span>,
};

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">RootLayout</span>(<span class="hljs-params">{ children }</span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">html</span> <span class="hljs-attr">lang</span>=<span class="hljs-string">"en"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">body</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{arsenal.className}</span>&gt;</span>{children}<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span></span>
  );
}
</code></pre>
<p>Ok moving on, let's get these hooks done. </p>
<p>Up first is <code>useFetch.js</code> which we use for getting data out of our S3 bucket. Give the <code>useFetch.js</code> file this code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useState, useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">useFetch</span>(<span class="hljs-params">url</span>) </span>{
  <span class="hljs-keyword">const</span> [data, setData] = useState([]);
  <span class="hljs-keyword">const</span> [error, setError] = useState(<span class="hljs-literal">null</span>);
  <span class="hljs-keyword">const</span> [isLoading, setIsLoading] = useState(<span class="hljs-literal">null</span>);

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-keyword">const</span> fetchData = <span class="hljs-keyword">async</span> () =&gt; {
      <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> json = <span class="hljs-keyword">await</span> fetch(url).then(<span class="hljs-function">(<span class="hljs-params">r</span>) =&gt;</span> r.json());
        setIsLoading(<span class="hljs-literal">false</span>);
        setData(json);
      } <span class="hljs-keyword">catch</span> (error) {
        setError(error);
        setIsLoading(<span class="hljs-literal">false</span>);
      }
    };

    fetchData();

    <span class="hljs-keyword">const</span> pollInterval = <span class="hljs-built_in">setInterval</span>(<span class="hljs-function">() =&gt;</span> {
      fetchData();
    }, <span class="hljs-number">5000</span>);

    <span class="hljs-keyword">return</span> <span class="hljs-function">() =&gt;</span> {
      <span class="hljs-built_in">clearInterval</span>(pollInterval);
    };
  }, [url]);

  <span class="hljs-keyword">return</span> { data, error, isLoading };
}
</code></pre>
<p>Let's take a look at this file and see how it works. </p>
<p>We have three state variables: one for our fetched data, another to take care of errors during the fetch process, and our loading state which indicates if our data is being fetched or not. The <code>fetchData</code> method is asynchronous and utilises the fetch API to retrieve data from the specified URL. It then adjusts the state variables accordingly.  </p>
<p>If the fetch is successful, <code>isLoading</code> is changed to <code>false</code>, and the data state is updated with the retrieved JSON data. If an error occurs, it assigns error to the error object and sets <code>isLoading</code> to <code>false</code>.  </p>
<p>There is also a function for polling set up at the end which is set to 5 seconds. Basically all it does is run the fetch function every 5 seconds, so if there is any new data in the API then the page will automatically be updated. </p>
<p>You can customise this so that it does it more frequently, less frequently, or not at all. If you disable this code, then you have to manually refresh your page to see new changes to the API.  </p>
<p>Lastly, the <code>useFetch</code> function returns an object with the <code>data</code>, <code>error</code>, and <code>isLoading</code> state variables, which we can use in the component that calls this custom hook. </p>
<p>Now for the <code>usePost.js</code> file, add this code to the <code>usePost.js</code> file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">usePost</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [isLoading, setIsLoading] = useState(<span class="hljs-literal">false</span>);
  <span class="hljs-keyword">const</span> [error, setError] = useState(<span class="hljs-literal">null</span>);
  <span class="hljs-keyword">const</span> [response, setResponse] = useState(<span class="hljs-literal">null</span>);

  <span class="hljs-keyword">const</span> postRequest = <span class="hljs-keyword">async</span> (url, formData) =&gt; {
    setIsLoading(<span class="hljs-literal">true</span>);
    setError(<span class="hljs-literal">null</span>);
    setResponse(<span class="hljs-literal">null</span>);

    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(url, {
        <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>,
        <span class="hljs-attr">headers</span>: {
          <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>,
        },

        <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify(formData),
      });

      <span class="hljs-keyword">const</span> responseData = <span class="hljs-keyword">await</span> response.json();

      <span class="hljs-keyword">if</span> (response.ok) {
        setResponse(responseData);
      } <span class="hljs-keyword">else</span> {
        setError(responseData);
      }
    } <span class="hljs-keyword">catch</span> (error) {
      setError(error);
    } <span class="hljs-keyword">finally</span> {
      setIsLoading(<span class="hljs-literal">false</span>);
    }
  };

  <span class="hljs-keyword">return</span> { isLoading, error, response, postRequest };
}
</code></pre>
<p>Let's run through the code to see how this custom hook works. Like our previous file, this file has state variables setup to check loading and error states. But this time, we also have a <code>response</code> state used to save the return data from the successful POST request.  </p>
<p>The <code>postRequest</code> method is asynchronous and accepts a URL and <code>formData</code> as inputs. This function is responsible for performing the POST request and changing the state variables. It collects the form data from the frontend which would be a new entry for our API in the S3 bucket on Tigris. This is sent as JSON and saved in our S3 bucket.  </p>
<p>Next is <code>useUpdate.js</code>, so now you can add this code to the <code>useUpdate.js</code> file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">useUpdate</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [isLoading, setIsLoading] = useState(<span class="hljs-literal">false</span>);
  <span class="hljs-keyword">const</span> [error, setError] = useState(<span class="hljs-literal">null</span>);
  <span class="hljs-keyword">const</span> [response, setResponse] = useState(<span class="hljs-literal">null</span>);
  <span class="hljs-keyword">const</span> updateRequest = <span class="hljs-keyword">async</span> (url, formData) =&gt; {
    setIsLoading(<span class="hljs-literal">true</span>);
    setError(<span class="hljs-literal">null</span>);
    setResponse(<span class="hljs-literal">null</span>);

    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(url, {
        <span class="hljs-attr">method</span>: <span class="hljs-string">'PUT'</span>,
        <span class="hljs-attr">headers</span>: {
          <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>,
        },

        <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify(formData),
      });

      <span class="hljs-keyword">const</span> responseData = <span class="hljs-keyword">await</span> response.json();

      <span class="hljs-keyword">if</span> (response.ok) {
        setResponse(responseData);
      } <span class="hljs-keyword">else</span> {
        setError(responseData);
      }
    } <span class="hljs-keyword">catch</span> (error) {
      setError(error);
    } <span class="hljs-keyword">finally</span> {
      setIsLoading(<span class="hljs-literal">false</span>);
    }
  };

  <span class="hljs-keyword">return</span> { isLoading, error, response, updateRequest };
}
</code></pre>
<p>Just like our previous custom hooks, this file can track our loading and error states. It's quite similar to our <code>POST</code> custom hook, but it's now doing a <code>PUT</code> request and updating existing data in our S3 bucket as opposed to creating brand new entries.  </p>
<p>And lastly it's time for the <code>useDelete.js</code> file. Add this code to our <code>useDelete.js</code> file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">useDelete</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> [isLoading, setIsLoading] = useState(<span class="hljs-literal">false</span>);
  <span class="hljs-keyword">const</span> [error, setError] = useState(<span class="hljs-literal">null</span>);
  <span class="hljs-keyword">const</span> [response, setResponse] = useState(<span class="hljs-literal">null</span>);
  <span class="hljs-keyword">const</span> deleteRequest = <span class="hljs-keyword">async</span> (url, formData) =&gt; {
    setIsLoading(<span class="hljs-literal">true</span>);
    setError(<span class="hljs-literal">null</span>);
    setResponse(<span class="hljs-literal">null</span>);

    <span class="hljs-keyword">try</span> {
      <span class="hljs-keyword">const</span> response = <span class="hljs-keyword">await</span> fetch(url, {
        <span class="hljs-attr">method</span>: <span class="hljs-string">'DELETE'</span>,
        <span class="hljs-attr">headers</span>: {
          <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>,
        },

        <span class="hljs-attr">body</span>: <span class="hljs-built_in">JSON</span>.stringify(formData),
      });

      <span class="hljs-keyword">const</span> responseData = <span class="hljs-keyword">await</span> response.json();

      <span class="hljs-keyword">if</span> (response.ok) {
        setResponse(responseData);
      } <span class="hljs-keyword">else</span> {
        setError(responseData);
      }
    } <span class="hljs-keyword">catch</span> (error) {
      setError(error);
    } <span class="hljs-keyword">finally</span> {
      setIsLoading(<span class="hljs-literal">false</span>);
    }
  };

  <span class="hljs-keyword">return</span> { isLoading, error, response, deleteRequest };
}
</code></pre>
<p>First, we will have an overview of how this custom hook works. We use this file for making delete requests, so essentially removing data from our S3 bucket. </p>
<p>Like our other custom hooks, it contains the same state for checking loading and errors. The response state gives us the data from a successful or bad <code>DELETE</code> request.  </p>
<p>The file uses the fetch API to send a <code>DELETE</code> request to the specified URL. The request body contains the formData object with its response.  </p>
<p>Our hook files are done, so we just need to finish with our component and page file and our application is good to go!</p>
<p>Ok our component files have the same name as their folder, which makes them easy to find.   </p>
<p>First up is our <code>AddUserForm.js</code> file, and here is the code for our file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> { usePost } <span class="hljs-keyword">from</span> <span class="hljs-string">'../../hooks/usePost'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">AddUserForm</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> API = <span class="hljs-string">'http://localhost:3000/'</span>;

  <span class="hljs-comment">// POST form input state</span>
  <span class="hljs-keyword">const</span> [firstname, setFirstname] = useState(<span class="hljs-string">''</span>);
  <span class="hljs-keyword">const</span> [lastname, setlastname] = useState(<span class="hljs-string">''</span>);
  <span class="hljs-keyword">const</span> [email, setEmail] = useState(<span class="hljs-string">''</span>);
  <span class="hljs-keyword">const</span> [password, setPassword] = useState(<span class="hljs-string">''</span>);

  <span class="hljs-keyword">const</span> handlePostForm = <span class="hljs-keyword">async</span> (e) =&gt; {
    e.preventDefault();

    <span class="hljs-keyword">if</span> (
      firstname === <span class="hljs-string">''</span> ||
      lastname === <span class="hljs-string">''</span> ||
      email === <span class="hljs-string">''</span> ||
      password === <span class="hljs-string">''</span>
    ) {
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'The form needs all fields to be filled in'</span>);
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> user = {
          <span class="hljs-attr">firstname</span>: firstname,
          <span class="hljs-attr">lastname</span>: lastname,
          <span class="hljs-attr">email</span>: email,
          <span class="hljs-attr">password</span>: password,
        };
        <span class="hljs-comment">// POST Route</span>
        postRequest(<span class="hljs-string">`<span class="hljs-subst">${API}</span>/api/postuser`</span>, user);
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`User <span class="hljs-subst">${user}</span>`</span>);
        setFirstname(<span class="hljs-string">''</span>);
        setlastname(<span class="hljs-string">''</span>);
        setEmail(<span class="hljs-string">''</span>);
        setPassword(<span class="hljs-string">''</span>);

        setAddUserMessage();
      } <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-built_in">console</span>.log(error);
      }
    }
  };

  <span class="hljs-comment">// CRUD message box state</span>
  <span class="hljs-keyword">const</span> useToggleMessage = <span class="hljs-function">(<span class="hljs-params">initialState = <span class="hljs-string">'hidden'</span></span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> [message, setMessage] = useState(initialState);

    <span class="hljs-keyword">const</span> toggleMessage = <span class="hljs-function">() =&gt;</span> {
      setMessage(<span class="hljs-string">''</span>);

      <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
        setMessage(<span class="hljs-string">'hidden'</span>);
      }, <span class="hljs-number">3000</span>);
    };

    <span class="hljs-keyword">return</span> [message, toggleMessage];
  };

  <span class="hljs-keyword">const</span> [addUserMessage, setAddUserMessage] = useToggleMessage();
  <span class="hljs-keyword">const</span> { postRequest } = usePost();

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-white p-4 rounded drop-shadow-md"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-2xl mb-4"</span>&gt;</span>ADD User<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{(e)</span> =&gt;</span> handlePostForm(e)}&gt;
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-wrap items-center mb-2"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-2 w-36 border-solid border-2"</span>&gt;</span>Firstname<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
            <span class="hljs-attr">value</span>=<span class="hljs-string">{firstname}</span>
            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setFirstname(e.target.value)}
            className="grow p-2 border border-2"
            required
          /&gt;
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-wrap items-center mb-2"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-2 w-36 border-solid border-2"</span>&gt;</span>Lastname<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
            <span class="hljs-attr">value</span>=<span class="hljs-string">{lastname}</span>
            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setlastname(e.target.value)}
            className="grow p-2 border border-2"
            required
          /&gt;
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-wrap items-center mb-2"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-2 w-36 border-solid border-2"</span>&gt;</span>Email<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span>
            <span class="hljs-attr">value</span>=<span class="hljs-string">{email}</span>
            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setEmail(e.target.value)}
            className="grow p-2 border border-2"
            required
          /&gt;
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-wrap items-center mb-2"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-2 w-36 border-solid border-2"</span>&gt;</span>Password<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"password"</span>
            <span class="hljs-attr">value</span>=<span class="hljs-string">{password}</span>
            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setPassword(e.target.value)}
            className="grow p-2 border border-2"
            required
          /&gt;
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-slate-600 hover:bg-slate-400 p-2 text-white cursor-pointer font-bold rounded-lg"</span>
          &gt;</span>
            Add User
          <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">bg-amber-100</span> <span class="hljs-attr">p-2</span> <span class="hljs-attr">mt-4</span> <span class="hljs-attr">rounded</span> ${<span class="hljs-attr">addUserMessage</span>}`}&gt;</span>
            User added
          <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p>This component has a form which sends a POST request to our server. We are adding new users by sending the request body data for the state variables (Firstname, Lastname, Email and Password) to our backend server which then saves them to our S3 bucket on Tigris. The form uses the <code>usePost</code> hook we created to send the <code>POST</code> request to our server.  </p>
<p>Alright next is the <code>DeleteUserForm.js</code> file. Here is the code needed for the file:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> { useDelete } <span class="hljs-keyword">from</span> <span class="hljs-string">'../../hooks/useDelete'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">DeleteUserForm</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> API = <span class="hljs-string">'http://localhost:3000/'</span>;

  <span class="hljs-comment">// DELETE form input state</span>
  <span class="hljs-keyword">const</span> [deleteId, setDeleteId] = useState(<span class="hljs-string">''</span>);
  <span class="hljs-keyword">const</span> { deleteRequest } = useDelete();

  <span class="hljs-comment">// CRUD message box state</span>
  <span class="hljs-keyword">const</span> useToggleMessage = <span class="hljs-function">(<span class="hljs-params">initialState = <span class="hljs-string">'hidden'</span></span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> [message, setMessage] = useState(initialState);

    <span class="hljs-keyword">const</span> toggleMessage = <span class="hljs-function">() =&gt;</span> {
      setMessage(<span class="hljs-string">''</span>);

      <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
        setMessage(<span class="hljs-string">'hidden'</span>);
      }, <span class="hljs-number">3000</span>);
    };

    <span class="hljs-keyword">return</span> [message, toggleMessage];
  };

  <span class="hljs-keyword">const</span> [deleteUserMessage, setDeleteUserMessage] = useToggleMessage();

  <span class="hljs-keyword">const</span> handleDeleteForm = <span class="hljs-keyword">async</span> (e) =&gt; {
    e.preventDefault();
    <span class="hljs-keyword">if</span> (deleteId === <span class="hljs-string">''</span>) {
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Form needs an id to be submitted'</span>);
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> userId = {
          <span class="hljs-attr">id</span>: deleteId,
        };

        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'User ID'</span>, userId);
        <span class="hljs-comment">// DELETE Route</span>
        deleteRequest(<span class="hljs-string">`<span class="hljs-subst">${API}</span>/api/deleteuser`</span>, userId);
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`User <span class="hljs-subst">${deleteId}</span> deleted`</span>);
        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`UserId <span class="hljs-subst">${userId}</span>`</span>);
        setDeleteId(<span class="hljs-string">''</span>);

        setDeleteUserMessage();
      } <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-built_in">console</span>.log(error);
      }
    }
  };

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-white p-4 rounded drop-shadow-md mb-4 mt-4"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-2xl mb-4"</span>&gt;</span>DELETE User<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{(e)</span> =&gt;</span> handleDeleteForm(e)}&gt;
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-wrap items-center mb-2"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-2 w-36 border-solid border-2"</span>&gt;</span>ID<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
            <span class="hljs-attr">value</span>=<span class="hljs-string">{deleteId}</span>
            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setDeleteId(e.target.value)}
            className="grow p-2 border border-2"
            required
          /&gt;
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-slate-600 hover:bg-slate-400 p-2 text-white cursor-pointer font-bold rounded-lg"</span>
          &gt;</span>
            Delete User
          <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">bg-amber-100</span> <span class="hljs-attr">p-2</span> <span class="hljs-attr">mt-4</span> <span class="hljs-attr">rounded</span> ${<span class="hljs-attr">deleteUserMessage</span>}`}&gt;</span>
            User deleted
          <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p>This one is pretty straightforward: it just has a form for deleting users from the database. Essentially, the logic searches for a user by its ID in the database. If it finds a match, then it sends a <code>DELETE</code> request to the server which removes that entry from our S3 bucket. Like our previous component, this uses a <code>useDelete</code>  hook to perform the action.  </p>
<p>Right, on to the <code>UpdateUserForm.js</code> file now. This is the code for our form, so go ahead and add it in:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useState } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> { useUpdate } <span class="hljs-keyword">from</span> <span class="hljs-string">'../../hooks/useUpdate'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">UpdateUserForm</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> API = <span class="hljs-string">'http://localhost:3000/'</span>;

  <span class="hljs-comment">// UPDATE/PUT form input state</span>
  <span class="hljs-keyword">const</span> [updateId, setUpdateId] = useState(<span class="hljs-string">''</span>);
  <span class="hljs-keyword">const</span> [updateFirstname, setUpdateFirstname] = useState(<span class="hljs-string">''</span>);
  <span class="hljs-keyword">const</span> [updateLastname, setUpdateLastname] = useState(<span class="hljs-string">''</span>);
  <span class="hljs-keyword">const</span> [updateEmail, setUpdateEmail] = useState(<span class="hljs-string">''</span>);
  <span class="hljs-keyword">const</span> [originalemail, setOriginalemail] = useState(<span class="hljs-string">''</span>);
  <span class="hljs-keyword">const</span> [updatePassword, setUpdatePassword] = useState(<span class="hljs-string">''</span>);

  <span class="hljs-comment">// CRUD message box state</span>
  <span class="hljs-keyword">const</span> useToggleMessage = <span class="hljs-function">(<span class="hljs-params">initialState = <span class="hljs-string">'hidden'</span></span>) =&gt;</span> {
    <span class="hljs-keyword">const</span> [message, setMessage] = useState(initialState);

    <span class="hljs-keyword">const</span> toggleMessage = <span class="hljs-function">() =&gt;</span> {
      setMessage(<span class="hljs-string">''</span>);

      <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =&gt;</span> {
        setMessage(<span class="hljs-string">'hidden'</span>);
      }, <span class="hljs-number">3000</span>);
    };

    <span class="hljs-keyword">return</span> [message, toggleMessage];
  };

  <span class="hljs-keyword">const</span> [updateUserMessage, setUpdateUserMessage] = useToggleMessage();
  <span class="hljs-keyword">const</span> { updateRequest } = useUpdate();

  <span class="hljs-keyword">const</span> handleUpdateForm = <span class="hljs-keyword">async</span> (e) =&gt; {
    e.preventDefault();

    <span class="hljs-keyword">if</span> (
      updateId === <span class="hljs-string">''</span> ||
      updateFirstname === <span class="hljs-string">''</span> ||
      updateLastname === <span class="hljs-string">''</span> ||
      originalemail === <span class="hljs-string">''</span> ||
      updateEmail === <span class="hljs-string">''</span> ||
      updatePassword === <span class="hljs-string">''</span>
    ) {
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'The form needs all fields to be filled in'</span>);
    } <span class="hljs-keyword">else</span> {
      <span class="hljs-keyword">try</span> {
        <span class="hljs-keyword">const</span> user = {
          <span class="hljs-attr">id</span>: updateId,
          <span class="hljs-attr">firstname</span>: updateFirstname,
          <span class="hljs-attr">lastname</span>: updateLastname,
          <span class="hljs-attr">originalEmail</span>: originalemail,
          <span class="hljs-attr">email</span>: updateEmail,
          <span class="hljs-attr">password</span>: updatePassword,
        };

        <span class="hljs-built_in">console</span>.log(<span class="hljs-string">`User: <span class="hljs-subst">${user}</span>`</span>);
        <span class="hljs-comment">// UPDATE Route</span>
        updateRequest(<span class="hljs-string">`<span class="hljs-subst">${API}</span>/api/updateuser`</span>, user);

        setUpdateId(<span class="hljs-string">''</span>);
        setUpdateFirstname(<span class="hljs-string">''</span>);
        setUpdateLastname(<span class="hljs-string">''</span>);
        setOriginalemail(<span class="hljs-string">''</span>);
        setUpdateEmail(<span class="hljs-string">''</span>);
        setUpdatePassword(<span class="hljs-string">''</span>);

        setUpdateUserMessage();
      } <span class="hljs-keyword">catch</span> (error) {
        <span class="hljs-built_in">console</span>.log(error);
      }
    }
  };

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-white p-4 rounded drop-shadow-md mb-4 mt-4"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-2xl mb-4"</span>&gt;</span>UPDATE User<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">form</span> <span class="hljs-attr">onSubmit</span>=<span class="hljs-string">{(e)</span> =&gt;</span> handleUpdateForm(e)}&gt;
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-wrap items-center mb-2"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-2 w-36 border-solid border-2"</span>&gt;</span>ID<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
            <span class="hljs-attr">value</span>=<span class="hljs-string">{updateId}</span>
            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setUpdateId(e.target.value)}
            className="grow p-2 border border-2"
            required
          /&gt;
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-wrap items-center mb-2"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-2 w-36 border-solid border-2"</span>&gt;</span>Firstname<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
            <span class="hljs-attr">value</span>=<span class="hljs-string">{updateFirstname}</span>
            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setUpdateFirstname(e.target.value)}
            className="grow p-2 border border-2"
            required
          /&gt;
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>

        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-wrap items-center mb-2"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-2 w-36 border-solid border-2"</span>&gt;</span>Lastname<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"text"</span>
            <span class="hljs-attr">value</span>=<span class="hljs-string">{updateLastname}</span>
            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setUpdateLastname(e.target.value)}
            className="grow p-2 border border-2"
            required
          /&gt;
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-wrap items-center mb-2"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-2 w-36 border-solid border-2"</span>&gt;</span>
            Original Email
          <span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span>
            <span class="hljs-attr">value</span>=<span class="hljs-string">{originalemail}</span>
            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setOriginalemail(e.target.value)}
            className="grow p-2 border border-2"
            required
          /&gt;
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-wrap items-center mb-2"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-2 w-36 border-solid border-2"</span>&gt;</span>Email<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"email"</span>
            <span class="hljs-attr">value</span>=<span class="hljs-string">{updateEmail}</span>
            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setUpdateEmail(e.target.value)}
            className="grow p-2 border border-2"
            required
          /&gt;
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"flex flex-wrap items-center mb-2"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">label</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"p-2 w-36 border-solid border-2"</span>&gt;</span>Password<span class="hljs-tag">&lt;/<span class="hljs-name">label</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">input</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"password"</span>
            <span class="hljs-attr">value</span>=<span class="hljs-string">{updatePassword}</span>
            <span class="hljs-attr">onChange</span>=<span class="hljs-string">{(e)</span> =&gt;</span> setUpdatePassword(e.target.value)}
            className="grow p-2 border border-2"
            required
          /&gt;
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">button</span>
            <span class="hljs-attr">type</span>=<span class="hljs-string">"submit"</span>
            <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-slate-600 hover:bg-slate-400 p-2 text-white cursor-pointer font-bold rounded-lg"</span>
          &gt;</span>
            Update User
          <span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">p</span> <span class="hljs-attr">className</span>=<span class="hljs-string">{</span>`<span class="hljs-attr">bg-amber-100</span> <span class="hljs-attr">p-2</span> <span class="hljs-attr">mt-4</span> <span class="hljs-attr">rounded</span> ${<span class="hljs-attr">updateUserMessage</span>}`}&gt;</span>
            User updated
          <span class="hljs-tag">&lt;/<span class="hljs-name">p</span>&gt;</span>
        <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">form</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p>The form in this component uses the <code>useUpdate</code> hook to send a <code>PUT</code> request to our backend server. The form takes state variables for ID, Firstname, Lastname, Original Email, Email and Password. </p>
<p>Most of the fields are easy to interpret – let's take note of the Original Email form field. You have to put in the current email address for the user, otherwise you will be unable to update the user because it will fail the check. The ID also needs to match, otherwise the form won't work.</p>
<p>Good, now we just have two files remaining – starting with the <code>UserDatabaseTable.js</code> file which requires this code:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">import</span> { useEffect } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span>;
<span class="hljs-keyword">import</span> { useFetch } <span class="hljs-keyword">from</span> <span class="hljs-string">'../../hooks/useFetch'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">UserDatabaseTable</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> API = <span class="hljs-string">'http://localhost:3000/'</span>;

  <span class="hljs-keyword">const</span> { data, error, isLoading } = useFetch(<span class="hljs-string">`<span class="hljs-subst">${API}</span>/api/getusers`</span>);
  <span class="hljs-keyword">if</span> (error) <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>An error has occurred.<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>;
  <span class="hljs-keyword">if</span> (isLoading) <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>Loading...<span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>;

  useEffect(<span class="hljs-function">() =&gt;</span> {
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Client API GET Data:'</span>, data);
  }, [data]);

  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">h1</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"text-4xl mb-2 text-center uppercase"</span>&gt;</span>User Database<span class="hljs-tag">&lt;/<span class="hljs-name">h1</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-gray-900 text-white p-4 rounded flex justify-center"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">table</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"table-auto border border-slate-500"</span>&gt;</span>
          <span class="hljs-tag">&lt;<span class="hljs-name">thead</span>&gt;</span>
            <span class="hljs-tag">&lt;<span class="hljs-name">tr</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">th</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"border border-slate-600 p-2 text-2xl"</span>&gt;</span>ID<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">th</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"border border-slate-600 p-2 text-2xl"</span>&gt;</span>
                Firstname
              <span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">th</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"border border-slate-600 p-2 text-2xl"</span>&gt;</span>Lastname<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
              <span class="hljs-tag">&lt;<span class="hljs-name">th</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"border border-slate-600 p-2 text-2xl"</span>&gt;</span>Email<span class="hljs-tag">&lt;/<span class="hljs-name">th</span>&gt;</span>
            <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
          <span class="hljs-tag">&lt;/<span class="hljs-name">thead</span>&gt;</span>

          {data === 0 ? (
            <span class="hljs-tag">&lt;<span class="hljs-name">tbody</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">tbody</span>&gt;</span>
          ) : (
            <span class="hljs-tag">&lt;<span class="hljs-name">tbody</span>&gt;</span>
              {data.map((user) =&gt; (
                <span class="hljs-tag">&lt;<span class="hljs-name">tr</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{user.id}</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"border border-slate-600 p-2 bg-gray-800 hover:bg-gray-600"</span>&gt;</span>
                    {user.id}
                  <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"border border-slate-600 p-2 bg-gray-800 hover:bg-gray-600"</span>&gt;</span>
                    {user.firstname}
                  <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"border border-slate-600 p-2 bg-gray-800 hover:bg-gray-600"</span>&gt;</span>
                    {user.lastname}
                  <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
                  <span class="hljs-tag">&lt;<span class="hljs-name">td</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"border border-slate-600 p-2 bg-gray-800 hover:bg-gray-600"</span>&gt;</span>
                    {user.email}
                  <span class="hljs-tag">&lt;/<span class="hljs-name">td</span>&gt;</span>
                <span class="hljs-tag">&lt;/<span class="hljs-name">tr</span>&gt;</span>
              ))}
            <span class="hljs-tag">&lt;/<span class="hljs-name">tbody</span>&gt;</span>
          )}
        <span class="hljs-tag">&lt;/<span class="hljs-name">table</span>&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p>In this file, we are using the hook <code>useFetch</code> to do a <code>GET</code> request to our S3 bucket. That's how we retrieve the data that is inside of it. Then all we are doing is <code>data.map</code> to loop through the array of data and render it in a table on our screen.  </p>
<p>Almost there – we have one file left: our <code>page.js</code> file in the root folder. Just add this code and let's get it completed:</p>
<pre><code class="lang-javascript"><span class="hljs-string">'use client'</span>;
<span class="hljs-keyword">import</span> UserDatabaseTable <span class="hljs-keyword">from</span> <span class="hljs-string">'./components/UserDatabaseTable/UserDatabaseTable'</span>;
<span class="hljs-keyword">import</span> AddUserForm <span class="hljs-keyword">from</span> <span class="hljs-string">'./components/AddUserForm/AddUserForm'</span>;
<span class="hljs-keyword">import</span> UpdateUserForm <span class="hljs-keyword">from</span> <span class="hljs-string">'./components/UpdateUserForm/UpdateUserForm'</span>;
<span class="hljs-keyword">import</span> DeleteUserForm <span class="hljs-keyword">from</span> <span class="hljs-string">'./components/DeleteUserForm/DeleteUserForm'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Home</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">return</span> (
    <span class="xml"><span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"container mx-auto mt-4"</span>&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">UserDatabaseTable</span> /&gt;</span>
      <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"bg-slate-100 rounded p-10 drop-shadow-lg"</span>&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">AddUserForm</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">UpdateUserForm</span> /&gt;</span>
        <span class="hljs-tag">&lt;<span class="hljs-name">DeleteUserForm</span> /&gt;</span>
      <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
    <span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span></span>
  );
}
</code></pre>
<p>All this file does is act as a main entry point for all of the components we created so that they render on our screen and complete the front end.  </p>
<p>And we are done! Now you can run the app with <code>npm run dev</code> (if it's not running already) and give it a try.</p>
<p>Just a quick reminder: when using the UPDATE User form, you need to be sure that you are using the Original Email, otherwise it won't update. </p>
<p>Also, be careful of the white space when copying the ID because it will also stop the updates from going through. Feel free to implement better error handling and checking if you want to ;)</p>
<p>Our app should be fully working so now. We just need to deploy it online in the final section.</p>
<h2 id="heading-how-to-deploy-your-app-to-flyio">How to Deploy Your App to Fly.io</h2>
<p>Deployment is the final part of this process, and it only takes a few simple steps to deploy your application online. </p>
<p>Firstly, you should be in the root of the <code>fly-tigris-user-database</code> project folder. Run the following command from your project's source directory to start a new Fly.io app:</p>
<pre><code class="lang-shell">fly launch
</code></pre>
<p>This command will create a <code>fly.toml</code> file and configure our project by setting up our project name, deployment region and other settings.  </p>
<p>Now run the command below to deploy your application to Fly.io:</p>
<pre><code class="lang-shell">fly deploy
</code></pre>
<p>The <code>fly deploy</code> flyctl command creates your Fly App and launches it on one or more Fly Machines, using the parameters supplied in the local <code>fly.toml</code> file.  </p>
<p>Now when you go to the dashboard on your Fly.io account, you will see your app like in the screenshot here:</p>
<p><img src="https://res.cloudinary.com/d74fh3kw/image/upload/v1712949553/fly-io-dashboard_m1egpx.jpg" alt="fly.io website apps dashboard screen" width="1476" height="825" loading="lazy">
<em>Fly.io website apps dashboard screen</em></p>
<p>Our app is now online but it won't work until we have added our environment variables from the <code>.env.local</code> file to the Secrets page for our app on Fly.io. We will also need to update our API route variable next so that it is using our Fly.io route and not localhost.   </p>
<p>First lets do the secrets so use this example secrets page as a reference for your own app:</p>
<p><img src="https://res.cloudinary.com/d74fh3kw/image/upload/v1712950078/fly-io-secrets_s6ysyz.jpg" alt="fly.io website secrets screen" width="1466" height="696" loading="lazy">
<em>Fly.io website secrets screen</em></p>
<p>Secrets are staged for the next release. To trigger a deploy, run <a target="_blank" href="https://fly.io/docs/flyctl/deploy/"><code>fly deploy</code></a> from a terminal. Remember that your app won't work online until you update the API variable for the components inside of the components folder. Locally we use <a target="_blank" href="http://localhost:3000/">http://localhost:3000</a> and online we will ue the url that is automatically generated by fly.io.</p>
<p>The API variable is near the top inside of these files which you will need to update:</p>
<ul>
<li>components/AddUserForm/AddUserForm.js</li>
<li>components/DeleteUserForm/DeleteUserForm.js</li>
<li>components/UpdateUserForm/UpdateUserForm.js</li>
<li>components/UserDatabaseTable/UserDatabaseTable.js</li>
</ul>
<p>See the example code here for the API variable:</p>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> API = <span class="hljs-string">'https://your-online-url.fly.dev/'</span>;
</code></pre>
<p>When your app is deployed online, you will get a URL. Just replace the value for the variable <code>API</code> with your Fly.io online URL. The routes in our application are now coming from our app on Fly.io and not our localhost app in the development test environment.</p>
<p>Don't forget to run the command <code>fly deploy</code> again (and every time you make a change to your local codebase or the secrets) so that your app on Fly.io gets the latest changes.  </p>
<p>You can <a target="_blank" href="https://fly.io/docs/apps/launch/">read the documentation here</a> for more info on how deployment its done.</p>
<p>That's it! Deployment should be done as well, and we can access our application online.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>So today we learned how to build a full-stack application using Next.js and deploy it online to the app hosting platform Fly.io. We also utilised Tigris for storing our user data in an AWS bucket online. </p>
<p>The combination of both platforms makes them a very useful and powerful tool for getting our apps online. Both platforms offer many different features so it's worth playing around with them and seeing how they can be beneficial for your projects.</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
