Next.js Discord

Discord Forum

I'm trying to create a search bar that adds a query parameter when submitted.

Answered
berkserbet posted this in #help-forum
Open in Discord
I'm a newby so I assume I am missing something here. I have a search bar and submit button created. I just can't figure out the query param part. It should be something OnSubmit, but I haven't been able to get anywhere.

'use client';
import React from 'react'

interface Company {
    rank: number;
    name: string;
    total: number;
    applicationStage: number;
    postInterview: number;
}

interface Props {
  search: string;
  companies: Company[];
}

const CompanyTable = ({ search, companies }: Props) => {

    const searchFilter = (companies: Company[]) => {
        if (!search) { return companies } else { return companies.filter(el => el.name.toLowerCase().includes(search.toLowerCase()))} }
    const filtered = searchFilter(companies)

    return (
        <>
            <form className='flex justify-center'>
                <input className='border border-gray-400 rounded-md p-2 m-2' placeholder='Search' value={search}/>
                <button className='border border-gray-400 rounded-md p-2 m-2' type='submit'>Search</button> {/* I want to use the text input and add a query param */}
            </form>
            <table className='table table-zebra table-pin-rows'>
                <thead>
                    <tr>
                        <th>Rank</th>
                        <th>Name</th>
                        <th>Total Reports</th>
                        <th>Application Stage</th>
                        <th>Post Interview</th>
                    </tr>
                </thead>
                <tbody>
                    {filtered.map((company: Company) => (<tr className="hover" key={company.rank}>
                        <td className='w-0 text-center'>{company.rank}</td>
                        <td>{company.name}</td>
                        <td className='w-0 text-center'>{company.total}</td>
                        <td className='w-0 text-center'>{company.applicationStage}</td>
                        <td className='w-0 text-center'>{company.postInterview}</td>
                    </tr>))}
                </tbody>
            </table> 
        </>
    )
}

export default CompanyTable
Answered by Wuchang bream
'use client'

import { usePathname, useRouter, useSearchParams } from 'next/navigation'
import { useState } from 'react'

const SearchContainer = () => {

    // NextJs Navigation
    const router = useRouter()
    const searchParams = useSearchParams()
    const pathname = usePathname()

    // React States
    const [search, setSearch] = useState(searchParams.get('search') || '')

    // Handle Submitting
    const handleSubmit = (e) => {
        e.preventDefault()
        router.push(`${pathname}/?search=${search}`)
    }

    return (
        <form className='flex justify-center'>
            <input className='border border-gray-400 rounded-md p-2 m-2' placeholder='Search' onChange={(e) => setSearch(e.target.value)} defaultValue={search}/>
            <button className='border border-gray-400 rounded-md p-2 m-2' type='submit' onClick={handleSubmit}>Search</button> {/* I want to use the text input and add a query param */}
        </form>
    )
}

export default SearchContainer
View full answer

9 Replies

Wuchang bream
You use to be able to set search params, but now I believe you can just get search params with useSearchParams. This is what I came up with to add search params. Import useRouter from next/navigation and assign it to router.

// Change Search Params
    const changeRoute = (newParams) => {
        const params = new URLSearchParams(searchParams)
        for (const property in newParams) {
            if (newParams[property] === undefined) params.delete(property)
            else params.set(property, newParams[property])
        }
        router.replace(pathname + '?' + params.toString(), { scroll: false })
    }


Then to call to it, do changeRoute({ search: value }). To remove a param, just pass it undefined as the value.
@berkserbet Thank you, I will try to figure that out. Is there no way to do it like this example: `<Link href="/?search=bbb">BBB</Link>` I would need the button to be the link and the search field to populate based on the inputted text
Wuchang bream
You could do this as well:

const router = useRouter()

const handleSubmit = (value) => {
  router.push(`/?search=${value}`)
}


If needed, you could also do router.replace to not track it in your history.
So rather than using Link, you would have a submit button and pass the value to handleSubmit, whether throug a useState or another method.
@Wuchang bream You could do this as well: const router = useRouter() const handleSubmit = (value) => { router.push(`/?search=${value}`) } If needed, you could also do router.replace to not track it in your history.
That makes sense - I'm not quite sure how to do the part where I grab the value and run the handleSubmit command. Would you be able to show me on this code:
<form className='flex justify-center'>
    <input className='border border-gray-400 rounded-md p-2 m-2' placeholder='Search' value={search}/>
    <button className='border border-gray-400 rounded-md p-2 m-2' type='submit'>Search</button> {/* I want to use the text input and add a query param */}
</form>
This is what I tried, but nothing happened (handleSubmit worked great on it's own)
<form className='flex justify-center' onSubmit={(e) => {handleSubmit('test')}}>
    <input className='border border-gray-400 rounded-md p-2 m-2' placeholder='Search' value={search}/>
    <button className='border border-gray-400 rounded-md p-2 m-2' type='submit'>Search</button> {/* I want to use the text input and add a query param */}
</form>
Wuchang bream
'use client'

import { usePathname, useRouter, useSearchParams } from 'next/navigation'
import { useState } from 'react'

const SearchContainer = () => {

    // NextJs Navigation
    const router = useRouter()
    const searchParams = useSearchParams()
    const pathname = usePathname()

    // React States
    const [search, setSearch] = useState(searchParams.get('search') || '')

    // Handle Submitting
    const handleSubmit = (e) => {
        e.preventDefault()
        router.push(`${pathname}/?search=${search}`)
    }

    return (
        <form className='flex justify-center'>
            <input className='border border-gray-400 rounded-md p-2 m-2' placeholder='Search' onChange={(e) => setSearch(e.target.value)} defaultValue={search}/>
            <button className='border border-gray-400 rounded-md p-2 m-2' type='submit' onClick={handleSubmit}>Search</button> {/* I want to use the text input and add a query param */}
        </form>
    )
}

export default SearchContainer
Answer
Wuchang bream
I forgot to include usePathname earlier. This works to update the query. Then use a useEffect to run every time searchParams.get('search') changes if you need to run code in the same component when it updates.
Worked, thanks!