Created
February 12, 2024 10:38
-
-
Save itsanishjain/c52ab14823f8b7f1fc643406dc38df50 to your computer and use it in GitHub Desktop.
Search with pagincation with Nextjs14
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// utils.ts | |
export const generatePagination = (currentPage: number, totalPages: number) => { | |
// If the total number of pages is 7 or less, | |
// display all pages without any ellipsis. | |
if (totalPages <= 7) { | |
return Array.from({ length: totalPages }, (_, i) => i + 1); | |
} | |
// If the current page is among the first 3 pages, | |
// show the first 3, an ellipsis, and the last 2 pages. | |
if (currentPage <= 3) { | |
return [1, 2, 3, "...", totalPages - 1, totalPages]; | |
} | |
// If the current page is among the last 3 pages, | |
// show the first 2, an ellipsis, and the last 3 pages. | |
if (currentPage >= totalPages - 2) { | |
return [1, 2, "...", totalPages - 2, totalPages - 1, totalPages]; | |
} | |
// If the current page is somewhere in the middle, | |
// show the first page, an ellipsis, the current page and its neighbors, | |
// another ellipsis, and the last page. | |
return [ | |
1, | |
"...", | |
currentPage - 1, | |
currentPage, | |
currentPage + 1, | |
"...", | |
totalPages, | |
]; | |
}; | |
// Search component | |
("use client"); | |
import { usePathname, useRouter, useSearchParams } from "next/navigation"; | |
import { useDebouncedCallback } from "use-debounce"; | |
function Search({ placeholder }: { placeholder: string }) { | |
const searchParams = useSearchParams(); | |
const { replace } = useRouter(); | |
const pathname = usePathname(); | |
const handleSearch = useDebouncedCallback((term) => { | |
const params = new URLSearchParams(searchParams); | |
params.set("page", "1"); | |
if (term) { | |
params.set("query", term); | |
} else { | |
params.delete("query"); | |
} | |
replace(`${pathname}?${params.toString()}`); | |
}, 300); | |
return ( | |
<div className="relative flex flex-1 flex-shrink-0"> | |
<label htmlFor="search" className="sr-only"> | |
Search | |
</label> | |
<input | |
className="peer block w-full rounded-md border border-gray-200 py-[9px] pl-10 text-sm outline-2 placeholder:text-gray-500" | |
placeholder={placeholder} | |
onChange={(e) => { | |
handleSearch(e.target.value); | |
}} | |
defaultValue={searchParams.get("query")?.toString()} | |
/> | |
</div> | |
); | |
} | |
// Table Component | |
function MembersTable({ | |
query, | |
currentPage, | |
}: { | |
query: string; | |
currentPage: number; | |
}) { | |
return <div></div>; | |
} | |
// Pagination | |
function Pagination({ totalPages }: { totalPages: number }) { | |
const pathname = usePathname(); | |
const searchParams = useSearchParams(); | |
const currentPage = Number(searchParams.get("page")) || 1; | |
const createPageURL = (pageNumber: number | string) => { | |
const params = new URLSearchParams(searchParams); | |
params.set("page", pageNumber.toString()); | |
return `${pathname}?${params.toString()}`; | |
}; | |
const allPages = generatePagination(currentPage, totalPages); | |
return ( | |
// Some tailwind pagination Ui | |
<div> | |
{allPages.map((page, index) => { | |
return <div key={index}>{page}</div>; | |
})} | |
</div> | |
); | |
} | |
// Server Component | |
import { Suspense } from "react"; | |
export default async function members({ | |
searchParams, | |
}: { | |
searchParams?: { | |
query?: string; | |
page?: string; | |
}; | |
}) { | |
const fetchTotalMembers = async ({ query }: { query: string }) => { | |
// Call Database to get members with matched query | |
return 5; | |
}; | |
const query = searchParams?.query || ""; | |
const currentPage = Number(searchParams?.page) || 1; | |
const totalPages = await fetchTotalMembers({ query }); | |
return ( | |
<div> | |
<div className="flex w-full items-center justify-between"> | |
<h1 className="text-2xl">Members</h1> | |
</div> | |
<div className="mt-4 flex items-center justify-between gap-2 md:mt-8"> | |
<Search placeholder="Search invoices..." /> | |
{/* Show fetched members */} | |
<Suspense key={query + currentPage} fallback={"loading...."}> | |
<MembersTable currentPage={currentPage} query={query} /> | |
</Suspense> | |
{/* Show pagination ui */} | |
<Pagination totalPages={totalPages} /> | |
</div> | |
</div> | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment