<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Olaitan Odulaja </title>
    <description>The latest articles on DEV Community by Olaitan Odulaja  (@thevideopilot).</description>
    <link>https://dev.to/thevideopilot</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F551166%2F4c846c9e-98ea-4087-8211-7a494b5f7e93.jpeg</url>
      <title>DEV Community: Olaitan Odulaja </title>
      <link>https://dev.to/thevideopilot</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/thevideopilot"/>
    <language>en</language>
    <item>
      <title>Using shadcn/ui for an Autocomplete Component</title>
      <dc:creator>Olaitan Odulaja </dc:creator>
      <pubDate>Sun, 23 Mar 2025 17:16:02 +0000</pubDate>
      <link>https://dev.to/thevideopilot/using-shadcnui-for-an-autocomplete-component-4cgc</link>
      <guid>https://dev.to/thevideopilot/using-shadcnui-for-an-autocomplete-component-4cgc</guid>
      <description>&lt;p&gt;Autocomplete is one of those UI elements that feels simple until you try to build one that looks great, feels responsive, and is flexible enough to extend. That’s where &lt;strong&gt;shadcn/ui&lt;/strong&gt; comes in. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Check out the full project &lt;a href="https://github.com/roofonecapital/neighbourhood-ratings" rel="noopener noreferrer"&gt;here&lt;/a&gt; (Still a WIP)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  What is shadcn/ui?
&lt;/h4&gt;

&lt;p&gt;shadcn/ui is a component library that brings together the flexibility of headless UI, the utility of Tailwind CSS, and the aesthetics of pre-styled components—all while keeping your codebase in control.&lt;/p&gt;

&lt;h4&gt;
  
  
  Why Should You Use It?
&lt;/h4&gt;

&lt;p&gt;Here’s what makes it stand out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Open Code:&lt;/strong&gt; You own the top layer. Easily customize anything without hacking around abstraction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composition:&lt;/strong&gt; Components are built with predictable, composable patterns.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Distribution:&lt;/strong&gt; Simple CLI and flat-file system make setup and updates seamless.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Beautiful Defaults:&lt;/strong&gt; Clean, modern styles out of the box.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI-Ready:&lt;/strong&gt; Because the code is open and modular, it’s easy for LLMs to analyze and suggest improvements.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Installation (Next.js)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pnpm dlx shadcn@latest init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This sets up the base config and connects the CLI to your project.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Command Component
&lt;/h3&gt;

&lt;p&gt;This is the core of the autocomplete feature. It’s a dynamic, accessible component designed for fuzzy search and filtering.&lt;/p&gt;

&lt;h4&gt;
  
  
  Install it:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  pnpm dlx shadcn@latest add &lt;span class="nb"&gt;command&lt;/span&gt;  
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Import the component and structure your layout.
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"use client";
import { useState, useCallback } from "react";
import {
   Command,
   CommandEmpty,
   CommandGroup,
   CommandInput,
   CommandItem,
   CommandList,
   CommandSeparator,
} from "@/components/ui/command";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Create the component
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkd2xmaebhuvrhi56bumv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkd2xmaebhuvrhi56bumv.png" alt="Neighborhood Rating" width="800" height="311"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;&amp;gt;
         &amp;lt;div className="flex flex-col items-center justify-center relative"&amp;gt;
            &amp;lt;div className="flex justify-center mt-8"&amp;gt;
               &amp;lt;Command
                  className="rounded-lg border shadow-md w-[540px] bg-white"
                  shouldFilter={false}
               &amp;gt;
                  &amp;lt;CommandInput
                     placeholder="Search by address or postcode"
                     defaultValue={userInput}
                     onValueChange={(v) =&amp;gt; handleUserInput(v)}
                     className="block w-[480px] h-[48px] -mr-16 text-base text-gray-900"
                  /&amp;gt;

                  &amp;lt;CommandList&amp;gt;
                     &amp;lt;CommandEmpty&amp;gt;No results found.&amp;lt;/CommandEmpty&amp;gt;
                     &amp;lt;CommandGroup&amp;gt;
                        {predictions.map((prediction) =&amp;gt; (
                           &amp;lt;CommandItem
                              key={prediction.placePrediction?.placeId}
                              value={prediction.placePrediction?.placeId}
                              onSelect={(value) =&amp;gt; handleSelectedPlace(value)}
                           &amp;gt;
                              {prediction.placePrediction?.text.text}
                           &amp;lt;/CommandItem&amp;gt;
                        ))}
                     &amp;lt;/CommandGroup&amp;gt;
                     &amp;lt;CommandSeparator /&amp;gt;
                  &amp;lt;/CommandList&amp;gt;
               &amp;lt;/Command&amp;gt;
               &amp;lt;button
                  type="button"
                  className="rounded-r-lg h-[48px] -ml-48 w-48 bg-roofone-green-primary mt-[0.75px] px-4 py-2.5 text-sm font-semibold text-white shadow-sm focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2"
               &amp;gt;
                  Calculate rating
               &amp;lt;/button&amp;gt;
            &amp;lt;/div&amp;gt;
         &amp;lt;/div&amp;gt;
      &amp;lt;/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I used the &lt;code&gt;CommandInput&lt;/code&gt; to handle user input and the &lt;code&gt;CommandList&lt;/code&gt; to render results from the API call. It is important to not that Shadcn\ui handles state changes for you.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Tip: Use the &lt;code&gt;shouldFilter&lt;/code&gt; prop to control search behavior—great for more advanced use cases like server-side filtering or custom ranking.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Create event handlers to handle user input and rendering the results.
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;   const [predictions, setPredictions] = useState&amp;lt;
      google.maps.places.AutocompleteSuggestion[]
   &amp;gt;([]);

   const handleUserInput = useCallback(
      debounce((input: string) =&amp;gt; {
         setUserInput(input);
         fetchPredictions(input);
      }, 1000),
      []
   );

   async function fetchPredictions(input: string) {
      const queryBody = {
         input: input,
         includedRegionCodes: ["uk"],
         includeQueryPredictions: true,
      };
      try {
         const res = await PlacesApi.post("places:autocomplete", queryBody);
         const data = await res.json();
         if (!res.ok) throw new Error("Failed to fetch predictions");
         console.log("received suggestings -&amp;gt;", data.suggestions);
         setPredictions(data.suggestions ?? []);
      } catch (error) {
         console.log(error);
      }
   }

   const handleSelectedPlace = (placeId: string) =&amp;gt; {
      const selectedInput = predictions.filter(
         (prediction) =&amp;gt; prediction.placePrediction?.placeId === placeId
      );
      setUserInput(String(selectedInput[0].placePrediction?.text.text));
      handleGetSelectedPlaceRating(placeId);
   };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's a simple explanation of my code.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;prediction&lt;/code&gt; state stores the response from the Google Maps API&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;handleUserInput&lt;/code&gt; helps rate limit the user input by using the debounce helper function. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fetchPredictions&lt;/code&gt; takes a string and uses the PlacesApi helper function to GET the predictions and sets the array of predictions into state.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;handleSelectedPlace&lt;/code&gt; uses the &lt;code&gt;CommandItem&lt;/code&gt; value which is the &lt;code&gt;place Id&lt;/code&gt; to get the text the user selected and then calls a function prop from the parent component. &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Key challenged faced: Google Autocomplete Types
&lt;/h3&gt;

&lt;p&gt;I initially found it tricky to properly type Google Maps results but after pair programming with my friend &lt;a class="mentioned-user" href="https://dev.to/opeadeyomoye"&gt;@opeadeyomoye&lt;/a&gt;, we were able to find the DefintelyTyped Google Maps Types and it was a live saver! &lt;/p&gt;

</description>
      <category>typescript</category>
      <category>googlemap</category>
      <category>shadcnui</category>
      <category>react</category>
    </item>
    <item>
      <title>Portfolio Website using just HTML, CSS, and Javascript.</title>
      <dc:creator>Olaitan Odulaja </dc:creator>
      <pubDate>Tue, 05 Jan 2021 10:06:43 +0000</pubDate>
      <link>https://dev.to/thevideopilot/portfolio-website-using-just-html-css-and-javascript-nmk</link>
      <guid>https://dev.to/thevideopilot/portfolio-website-using-just-html-css-and-javascript-nmk</guid>
      <description>&lt;h2&gt;
  
  
  What I built
&lt;/h2&gt;

&lt;p&gt;A static portfolio website using Html/CSS and javascript. More work to be done on the backend. &lt;/p&gt;

&lt;h3&gt;
  
  
  Category Submission:
&lt;/h3&gt;

&lt;p&gt;Personal Site/Portfolio&lt;/p&gt;

&lt;h3&gt;
  
  
  App Link
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://theodulaja-com-uh3bv.ondigitalocean.app/" rel="noopener noreferrer"&gt;https://theodulaja-com-uh3bv.ondigitalocean.app/&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Screenshots
&lt;/h3&gt;

&lt;h3&gt;
  
  
  Description
&lt;/h3&gt;

&lt;p&gt;A statice portfolio website using Html/CSS and javascript. More work to be done on the backend. You can dynamically change the theme of the website using the theme options. &lt;/p&gt;

&lt;h3&gt;
  
  
  Link to Source Code
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/thevideopilot/theodulaja.com" rel="noopener noreferrer"&gt;https://github.com/thevideopilot/theodulaja.com&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Permissive License
&lt;/h3&gt;

&lt;p&gt;MIT&lt;/p&gt;

&lt;h2&gt;
  
  
  Background
&lt;/h2&gt;

&lt;p&gt;(What made you decide to build this particular app? What inspired you?)&lt;br&gt;
Starting out in the software engineering industry and I felt the need to be able to put more skills out there and to also practice what I've learned.&lt;/p&gt;

&lt;h3&gt;
  
  
  How I built it
&lt;/h3&gt;

&lt;p&gt;(How did you utilize DigitalOcean’s App Platform? Did you learn something new along the way? Pick up a new skill?)&lt;br&gt;
I used HTML and CSS basically. In the CSS I practice grid-column and flex boxes to get a practical grasp. I also worked with javascript to dynamically change the theme of the website. Learned about deployment reading the "how to deploy an app" guide. What stood out to me is the importance of the location where you set up the computer and the idea of a CDN.&lt;/p&gt;

&lt;h3&gt;
  
  
  Additional Resources/Info&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fjjjsmus75rvew5drjtxs.png" alt="tempsnip2" width="800" height="357"&gt;
&lt;/h3&gt;

</description>
      <category>dohackathon</category>
    </item>
  </channel>
</rss>
