January 14, 2024
O. Wolfson
This article will guide you through setting up the project, creating the necessary components, and implementing search functionality with URL parameters.
Here is a deployed version of the blog application: https://next-search-params-cyan.vercel.app/blog
Code on GitHub: https://github.com/owolfdev/next-search-params
Create a New Next.js App:
bashnpx create-next-app@latest nextjs-blog-app --typescript
cd nextjs-blog-app
Create a posts.ts file inside the data directory with your blog posts.
typescript// data/posts.ts
export const blogPosts = [
  {
    slug: "post-one",
    title: "First Blog Post",
    content: "This is the content of the first post.",
  },
  // ... other posts
];
Create the Blog component in app/blog/page.tsx. This component will display all blog posts and include a search bar.
typescript// app/blog/page.tsx
import React from "react";
import Link from "next/link";
import { blogPosts } from "@/data/posts/posts.js";
import SearchBar from "@/components/SearchBar";
const BlogPage = ({
  searchParams,
}: {
  searchParams: { [key: string]: string | string[] | undefined };
}) => {
  const value = searchParams.slug;
  const searchQuery = searchParams.search?.toString().toLowerCase() || "";
  const filteredPosts = blogPosts.filter((post) =>
    post.title.toLowerCase().includes(searchQuery)
  );
  return (
    <div className="flex flex-col gap-8">
      <div>Blog Page</div>
      <div>
        <SearchBar />
      </div>
      <div>
        {filteredPosts.map((post) => {
          return (
            <div key={post.slug}>
              < =`//${}`}>{post.title}
            
          );
        })}
      
    
  );
};
  ;
Props Structure and Data Import:
BlogPage component accepts searchParams as a prop. This object contains URL search parameters, key-value pairs corresponding to the query parameters in the URL.@/data/posts/posts.js.Search Functionality:
searchQuery is extracted from searchParams. If the search parameter exists, it is converted to a string and made lowercase. If it doesn't exist, it defaults to an empty string.blogPosts array is filtered to include only those posts whose titles contain the searchQuery. This filtering is case-insensitive, as both the titles and the search query are converted to lowercase.Rendering:
SearchBar component, which handles the updating of the search parameters in the URL.SearchBar, it maps over filteredPosts to display each post. Each post is wrapped in a Link component to create a clickable element that navigates to the individual blog post page (/blog/${post.slug}).Create the SearchBar component in app/components/SearchBar.tsx.
typescript// app/components/SearchBar.tsx
"use client";
import { useState, useEffect } from "react";
import { useSearchParams } from "next/navigation";
import { useRouter, usePathname } from "next/navigation";
export default function SearchBar() {
  const searchParams = useSearchParams();
  const [inputValue, setInputValue] = useState("");
  const router = useRouter();
  const pathname = usePathname();
  // Update the input value when it changes
  const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setInputValue(event.target.value);
  };
  // Optionally, submit the form and update the URL
  const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
    event.preventDefault();
    if (inputValue === "") {
      router.push(pathname);
      return;
    }
    router.push(`${pathname}?search=${encodeURIComponent(inputValue)}`);
  };
  
  ( {
     searchQuery = searchParams.();
     (searchQuery) {
      (searchQuery);
    }
  }, [searchParams]);
   (
    
  );
}
Imports and Client-Side Directive:
useState, useEffect, useSearchParams, useRouter, and usePathname are imported from React and next/navigation.State and Hooks:
useSearchParams is used to access the current URL's search parameters.useState is used to maintain inputValue, the current state of the search input field.useRouter provides navigation functionality, and usePathname gives access to the current pathname of the URL.Form Submission:
handleSubmit prevents the default form submission and handles the search functionality.
If inputValue is not empty, it navigates to the current pathname with inputValue as a query parameter (?search=).
If inputValue is empty, it navigates back to the current pathname without any search parameters.
Enhanced Navigation and URL Management:
SearchBar component demonstrates an effective way of managing URL search parameters in a Next.js 14 application. By using useSearchParams for reading and useRouter for updating the URL, the component can dynamically control the browser's URL based on user interaction.Create a component for individual blog posts in app/blog/[slug]/page.tsx.
typescript// app/blog/[slug]/page.tsx
import React from "react";
import path from "path";
import { blogPosts } from "@/data/posts/posts.js";
export async function generateStaticParams() {
  const posts = blogPosts;
  return posts.map((post: any) => ({
    slug: post.slug,
  }));
}
export default async function BlogPostPage({
  params,
}: {
  params: { slug: string };
}) {
  const post = blogPosts.find((p) => p.slug === params.slug);
  return (
    <div>
      <div>{post?.title}</div>
      <div>{post?.content}</div>
    </div>
  );
}
This function and the component together handle the rendering and static generation of individual blog post pages based on dynamic URL segments.
generateStaticParams Function
[slug] in this case) to statically generate routes at build time.blogPosts data (imported from @/data/posts/posts.ts) and maps over it to return an array of objects, each containing a slug property. These objects representthe dynamic segments needed for each route.
generateStaticParams and uses its return value to statically generate a page for each slug in the blogPosts array.BlogPostPage Component
params as a prop, which includes the slug of the current blog post.slug to find the corresponding post from the blogPosts data. It then renders the title and content of the post.slug does not exist (e.g., when accessing a URL with a slug that hasn't been generated), the component will safely render nothing for the title and content, avoiding any errors.This article covers setting up a blog application using Next.js 14 with TypeScript, focusing on search functionality. We've implemented a blog page with a search bar and individual blog post pages, utilizing the new App Router API in Next.js 14. The useRouter hook is used to manage URL parameters for search functionality, demonstrating a modern approach to web application development with Next.js.