Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Stephen Gbolagade
Stephen Gbolagade

Posted on • Edited on

     

Build a Functional Search Bar in Next.js

The search bar is one of the most important components to integrate for mobile or web applications, especially the ones that deal with users consuming a lot of data from the website such as an e-commerce website, a blog, a job listing platform, etc.

If you're working on a flow that requires you to integrate a search feature in Nextjs, this blog post will help you out. You will not just build a functional search bar, in the consequent posts, you'll learn how to handle pagination and filter the search results based on the data structure.

To have a clear understanding of what we'll be building, we will use the Google search website as an example. And model it with Nextjs, Tailwind, and Typescript.

If you go towww.google.com, it will load up the homepage. From the homepage, you're presented with an input field where you can type in whatever you want to search, if you hit enter, you'll be shown the search result page for that keyword(s) you've searched.

When you search for keyword(s) on Google search, the keywords (what you want to enter in the search bar) are called "Search Parameters", these params are sent to the backend to get the result that fits the entered keywords from the database and it's finally shown to the user.

That's what we will do.

In summary, we will:

  • Push the search query to the URL - using theuseRouter hook

  • Get the search query and use it to find related data -using theuseSearchParams hook

But in this article, we are not using any backend or database, instead, we will use raw data. Also, we will handle everything on a page. Don't worry, it's still the same experience.

Table Of Contents

If you prefer to just look at the code,here is the repository

Now Let's start!

Step 1 — Create Next App

Runnpx create-next-app in your terminal to bootstrap the Nextjs app.

Follow the prompts as you always do. But note that I will use the newApp router, Typescript, and Tailwindcss for styling.

cd into the project folder, and runyarn in your terminal to install all packages and dependencies.

After that, runnpm dev to start the application and checklocalhost:3000 in any of your browsers to see the website running.

If you've done that successfully, let's go to the next step.

Step 2 — Set up the Starter files

Create two folders in thesrc folder:components andservices. We will put all our reusable UIs in the component folder and we'll put all our mockup data in the services folder.

Note: if you're not using thesrc folder structure, you can still create the component and services folder.

In theservices folder:

Create adata.ts file to handle our mock API data.

Put this code here

Instead of duplicating random data, we use afor loop to generate 50 sample data.

Understand that we are just simulating how the data would come from an API response. And we defined a Typescript interface for it.

Inside thecomponents folder:

  • Create aSearchInput.tsx file to handle the search bar

  • Create aProfileCard.tsx file to handle our user profile card UI

Step 3 — Build the SearchInput UI

Starting with SearchInput, here is the code:

import{useRouter}from"next/navigation";import{useState,ChangeEvent}from"react";interfaceiDefault{defaultValue:string|null}exportconstSearchInput=({defaultValue}:iDefault)=>{// initiate the router from next/navigationconstrouter=useRouter()// We need to grab the current search parameters and use it as default value for the search inputconst[inputValue,setValue]=useState(defaultValue)consthandleChange=(event:ChangeEvent<HTMLInputElement>)=>{constinputValue=event.target.value;setValue(inputValue);}// If the user clicks enter on the keyboard, the input value should be submitted for search// We are now routing the search results to another page but still on the same pageconsthandleSearch=()=>{if(inputValue)returnrouter.push(`/?q=${inputValue}`);if(!inputValue)returnrouter.push("/")}consthandleKeyPress=(event:{key:any;})=>{if(event.key==="Enter")returnhandleSearch()}return(<divclassName="search__input border-[2px] border-solid border-slate-500 flex flex-row items-center gap-5 p-1 rounded-[15px]"><labelhtmlFor="inputId">searchIcon</label><inputtype="text"id="inputId"placeholder="Enter your keywords"value={inputValue??""}onChange={handleChange}onKeyDown={handleKeyPress}className="bg-[transparent] outline-none border-none w-full py-3 pl-2 pr-3"/></div>)}
Enter fullscreen modeExit fullscreen mode

Whenever we type something in the input field and hit Enter, the URL has the search query.

For instance:localhost:3000 becomeslocalhost:3000?q={query}

When we are handling the search logic, we will grab this query and use it to filter our data.

This is basically what we need for the input component but you can further customize it to your taste to handle the error state and validation.

Step 4 — Build the ProfileCard UI

The profile card also passes some props and we pass values to it when handling the logic.

Here is the code:

importImagefrom'next/image'//Import the profile interface from data.jsimport{iProfile}from"../services/data";exportconstProfileCard=(props:iProfile)=>{const{name,email,username,role,photo}=props;return(<divclassName="profile__card rounded-[15px] border border-solid"><Imagesrc={photo}alt={username}className="h-[200px]"height={1000}width={400}/><divclassName=" bg-slate-300 p-3"><h2className="">Name:{name}</h2><p>Role:{role}</p><p>Email:{email}</p><p>follow @{username}</p></div></div>)}
Enter fullscreen modeExit fullscreen mode

The profile UI is ready, now let’s go to the next step.

Step 5: Updating the UI

Create a new folder insrc called ‘pages’’

In the pages’ folder, create a new file calledHomepage.tsx. This is where we are going to join all our components together. For now, simply return this:

constHome=()=>{return(<>this is Homepage Component</>)}exportdefaultHome
Enter fullscreen modeExit fullscreen mode

If you are using the Nextjs app router, open theapp folder, locate thepage.tsx file, open it, and clear everything there. Then simply put this code there:

// import the Homepage componentconstApp=()=>{return<Homepage/>}exportdefaultApp
Enter fullscreen modeExit fullscreen mode

Check your browser now, you should see something like this:

First demo

Step 6: Handling the logic

Let’s update and handle the logic in theHomepage file. Follow along:

// change this component to client component'use client'// import the data// import the searchBar// import the profile UIimport{useState,useEffect}from"react"import{ProfileCard}from"@/components/ProfileCard"import{SearchInput}from"@/components/SearchInput"import{data,iProfile}from"@/services/data"constHome=()=>{// initialize useState for the dataconst[profileData,setProfileData]=useState<iProfile[]>([])useEffect(()=>{// will be updated soonsetProfileData(data)},[])// get total usersconsttotalUser=profileData.length;return(<sectionclassName="h-[100vh] w-screen px-[2rem] md:px-[6rem] mt-[100px]"><pclassName="mb-10 ">Showing{totalUser}{totalUser>1?"Users":"User"}</p><SearchInputdefaultValue={searchQuery}/>{/* // Conditionally render the profile cards */}<divclassName="mt-8">{totalUser===0?<p>No result returned</p>:(// return the profile cards here<divclassName="grid grid-cols-1 md:grid-cols-3 items-center gap-5">{profileData.map(({username,role,name,photo,email}:iProfile)=>{return(<divkey={username}><ProfileCardname={name}role={role}photo={photo}email={email}username={username}/></div>)})}</div>// End of profile data UI)}</div></section>)}exportdefaultHome
Enter fullscreen modeExit fullscreen mode

If you check your browser again, you can see our search input component and all the 50 users displayed on the page.

Demo 2

And if you perform a search, nothing is happening. Let's handle that.

Now that the search query is set to URL, what we need to do now is to grab the query and use it to fetch the data from the backend. In our case, we will just use it to filter our mockup data.

To grab the search query, we will use theuseSearchParams from next/navigation.

// import the useSearchParams hookimport{useSearchParams}from'next/navigation'// And replace your useEffect code with this:constsearchParams=useSearchParams()// Now get the queryconstsearchQuery=searchParams&&searchParams.get("q");// we use `q` to set the query to the browser, it could be anythinguseEffect(()=>{consthandleSearch=()=>{// Filter the data based on search queryconstfindUser=data.filter((user)=>{if(searchQuery){return(user.name.toLowerCase().includes(searchQuery.toLowerCase())||user.role.toLowerCase().includes(searchQuery.toLowerCase())||user.username.toLowerCase().includes(searchQuery.toLowerCase())||user.email.toLowerCase().includes(searchQuery.toLowerCase()));}else{// If no search query, return the original datareturntrue;}});// Update profileData based on search resultssetProfileData(findUser);};// Call handleSearch when searchQuery changeshandleSearch();},[searchQuery]);// Only rerun the effect if searchQuery changes
Enter fullscreen modeExit fullscreen mode

If you join this code with theHomepage.tsx and test your app, it should be working fine 🙂

You can search by username, email address, name, and role.

full Demo

Depending on the structure of your data and UI flow, you may need to paginate and filter the data.

I will handle that in the next post, so stay tuned.

Top comments(11)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss
CollapseExpand
 
shakilahmed007 profile image
Shakil Ahmed
MERN Stack High-Performance Applications at Your Service! React | Node | Express | MongoDB
  • Location
    Savar, Dhaka
  • Joined

Exciting tutorial! 🚀 Can't wait to implement a powerful search bar in my Next.js projects. Thanks for breaking it down step by step!

CollapseExpand
 
stephengade profile image
Stephen Gbolagade
Open to [remote] opportunities in Fullstack Software engineer, React, Nextjs, Python, Typescript, FastAPI, Postgres, SQL, WordPress Web design & Technical writing 👉👉 hello@stephengade.com
  • Email
  • Location
    Nigeria
  • Education
    Obafemi Awolowo University
  • Pronouns
    He/Him
  • Work
    Frontend Engineer
  • Joined

Thanks for reading!

CollapseExpand
 
dsaga profile image
Dusan Petkovic
Front-end software engineer with full-stack experience eager to learn new things to find material for my blog :/
  • Location
    Paracin, Serbia
  • Pronouns
    he/him
  • Joined

Thanks!

CollapseExpand
 
stephengade profile image
Stephen Gbolagade
Open to [remote] opportunities in Fullstack Software engineer, React, Nextjs, Python, Typescript, FastAPI, Postgres, SQL, WordPress Web design & Technical writing 👉👉 hello@stephengade.com
  • Email
  • Location
    Nigeria
  • Education
    Obafemi Awolowo University
  • Pronouns
    He/Him
  • Work
    Frontend Engineer
  • Joined

I'm glad you find this helpful

CollapseExpand
 
white_gui_677361f73cc80f9 profile image
White Gui
  • Joined

Thanks for your help!

CollapseExpand
 
vacilando profile image
Tomáš Fülöpp
  • Location
    Brussels, Belgium
  • Joined
• Edited on• Edited

Build fails with:

Please wait...
⨯ useSearchParams() should be wrapped in a suspense boundary at page "/search". Read more: https://nextjs.org/docs/messages/missing-suspense-with-csr-bailout

NB Instead of Homepage.tsx using:/app/search/page.tsx

CollapseExpand
 
stephengade profile image
Stephen Gbolagade
Open to [remote] opportunities in Fullstack Software engineer, React, Nextjs, Python, Typescript, FastAPI, Postgres, SQL, WordPress Web design & Technical writing 👉👉 hello@stephengade.com
  • Email
  • Location
    Nigeria
  • Education
    Obafemi Awolowo University
  • Pronouns
    He/Him
  • Work
    Frontend Engineer
  • Joined
• Edited on• Edited

Wrap your component with< Suspense /> and the error will be fixed

CollapseExpand
 
ubuntupunk profile image
ubuntupunk
hactivism, indieweb, open-source, devops, matrix
  • Location
    Muizenberg, Cape Town
  • Education
    Camps Bay High School
  • Pronouns
    Who/Where
  • Work
    Pirate at Beachhouse
  • Joined

./src/pages/Homepage.ts
Error:
× Expected '>', got 'className'
╭─[/media/afrodeity/Neptune/DRK/searchtut/mysearch/src/pages/Homepage.ts:43:1]
43 │
44 │ return (
45 │
46 │
· ─────────
47 │
48 │

Showing {totalUser} {totalUser > 1 ? "Users" : "User"}


╰────

Caused by:
Syntax Error

CollapseExpand
 
stephengade profile image
Stephen Gbolagade
Open to [remote] opportunities in Fullstack Software engineer, React, Nextjs, Python, Typescript, FastAPI, Postgres, SQL, WordPress Web design & Technical writing 👉👉 hello@stephengade.com
  • Email
  • Location
    Nigeria
  • Education
    Obafemi Awolowo University
  • Pronouns
    He/Him
  • Work
    Frontend Engineer
  • Joined

You probably mistyped something, could you please share your code snippet so I can debug it for you?

CollapseExpand
 
iammtander profile image
Mitchell Mutandah
Full stack Developer
  • Location
    Cape Town, SA
  • Work
    Developer @ Bitwise Software Solutions (Lekker Commerce)
  • Joined

Nice read!

CollapseExpand
 
stephengade profile image
Stephen Gbolagade
Open to [remote] opportunities in Fullstack Software engineer, React, Nextjs, Python, Typescript, FastAPI, Postgres, SQL, WordPress Web design & Technical writing 👉👉 hello@stephengade.com
  • Email
  • Location
    Nigeria
  • Education
    Obafemi Awolowo University
  • Pronouns
    He/Him
  • Work
    Frontend Engineer
  • Joined

Thanks for reading 🙏

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Open to [remote] opportunities in Fullstack Software engineer, React, Nextjs, Python, Typescript, FastAPI, Postgres, SQL, WordPress Web design & Technical writing 👉👉 hello@stephengade.com
  • Location
    Nigeria
  • Education
    Obafemi Awolowo University
  • Pronouns
    He/Him
  • Work
    Frontend Engineer
  • Joined

More fromStephen Gbolagade

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp