We are making amazing progress. We are now in the final stretch. In this section, we will look at Search and Pagination.
It is a Client Component hook that lets you read the current URL's query string.
We use it in our code to get our current parameters from our url.
Then, we use the new URLSearchParams to update our search parameters. You can learn more about it : Allows us to access the router object inside any function component in your app. We will use the replace method to prevent adding a new URL entry into the history stack.
usePathname : Used to prevent making an api call on every keystroke when using our Search component.
Install the use-debounce package from
Excellent. Now that our Search component shows up, let's move on to the second part. We'll pass our query search through our getSummaries function and update it accordingly to allow us to search our queries.
To make the search work, we will rely on the following parameters.
sorting: we will sort all of our summaries in descending order, ensuring that the newest summary appears first.
filters: The filters we will use
$oroperator to combine our search conditions. This means the search will return summaries based on the fields we will filter using$containsi,which will ignore case sensitivity.
Let's look inside our src/data/loaders.ts file and update the following code inside our getSummaries function.
export async function getSummaries() {
const url = new URL("/api/summaries", baseUrl);
return fetchData(url.href);
}
Here are the changes we are going to make.
export async function getSummaries(queryString: string) {
const query = qs.stringify({
sort: ["createdAt:desc"],
filters: {
$or: [
{ title: { $containsi: queryString } },
{ summary: { $containsi: queryString } },
],
},
});
const url = new URL("/api/summaries", baseUrl);
url.search = query;
return fetchData(url.href);
}
We will create a query to filter our summaries on the title and summary.
Now, we have one more step before testing our search. Let's navigate back to the src/app/dashboard/summaries folder and make the following changes inside our page.tsx file.
We cannot pass and utilize our query params since we just updated our getSummaries function.
const { data } = await getSummaries(query);
Now, let's see if our search is working.
;
So, run the following command to get all the necessary dependencies.
npx shadcn@latest add pagination
And add the following code to your components/custom folder file called pagination-component.tsx.
"use client";
import { FC } from "react";
import { usePathname, useSearchParams, useRouter } from "next/navigation";
import {
Pagination,
PaginationContent,
PaginationItem,
} from "@/components/ui/pagination";
import { Button } from "@/components/ui/button";
interface PaginationProps {
pageCount: number;
}
interface PaginationArrowProps {
direction: "left" | "right";
href: string;
isDisabled: boolean;
}
const PaginationArrow: FC<PaginationArrowProps> = ({
direction,
href,
isDisabled,
}) => {
const router = useRouter();
const isLeft = direction === "left";
const disabledClassName = isDisabled ? "opacity-50 cursor-not-allowed" : "";
return (
<Button
onClick={() => router.push(href)}
className={`bg-gray-100 text-gray-500 hover:bg-gray-200 ${disabledClassName}`}
aria-disabled={isDisabled}
disabled={isDisabled}
>
{isLeft ? "«" : "»"}
</Button>
);
};
export function PaginationComponent({ pageCount }: Readonly<PaginationProps>) {
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()}`;
};
return (
<Pagination>
<PaginationContent>
<PaginationItem>
<PaginationArrow
direction="left"
href={createPageURL(currentPage - 1)}
isDisabled={currentPage <= 1}
/>
</PaginationItem>
<PaginationItem>
<span className="p-2 font-semibold text-gray-500">
Page {currentPage}
</span>
</PaginationItem>
<PaginationItem>
<PaginationArrow
direction="right"
href={createPageURL(currentPage + 1)}
isDisabled={currentPage >= pageCount}
/>
</PaginationItem>
</PaginationContent>
</Pagination>
);
}
Again, we are using our familiar hooks from before to make this work, usePathname, searchParams, and useRouter in a similar way as we did before.
We are receiving pageCount via props to see our available pages. Inside the PaginationArrow component, we use useRouter to programmatically update our URL via the push method on click.
Let's add this component to our project. In our components/custom folder, create a PaginationComponent.tsx file and paste it into the above code.
Now that we have our component, let's navigate to src/app/dashboard/summaries/page.tsx and import it.
import { PaginationComponent } from "@/components/custom/pagination-component";
Now update our SearchParamsProps interface to the following.
interface SearchParamsProps {
searchParams?: {
page?: string;
query?: string;
};
}
Now, let's create a currentPage variable to store the current page we get from our URL parameters.
const currentPage = Number(searchParams?.page) || 1;
Now, we can pass our currentPage to our getSummaries function.
const { data } = await getSummaries(query, currentPage);
Now, let's go back to our loaders. tsx file and update the getSummaries with the following code to utilize our pagination.
export async function getSummaries(queryString: string, currentPage: number) {
const PAGE_SIZE = 4;
const query = qs.stringify({
sort: ["createdAt:desc"],
filters: {
$or: [
{ title: { $containsi: queryString } },
{ summary: { $containsi: queryString } },
],
},
pagination: {
pageSize: PAGE_SIZE,
page: currentPage,
},
});
const url = new URL("/api/summaries", baseUrl);
url.search = query;
return fetchData(url.href);
}
In the above example, we use Strapi's pagination fields and pass pageSize and page fields. You can learn more about Strapi's pagination
Excellent, it is working.
Conclusion
This Next.js with Strapi CMS tutorial covered how to implement search and pagination functionalities. Hope you are enjoying this tutorial.
We are almost done. We have two more sections to go, including deploying our project to Strapi Cloud and Vercel.
See you in the next post.
Note about this project
This project has been updated to use Next.js 15 and Strapi 5.
If you have any questions, feel free to stop by at our .
You can also find the blog post content in the Strapi Blog.
Feel free to make PRs to fix any issues you find in the project, or let me know if you have any questions.
Happy coding!
- Paul
SOCIAL SHARE CARD GENERATOR