In the ever-evolving landscape of web development, a silent hero has emerged — the URL. As frontenders navigate the challenges of integrating a state manager into server components, the URL, provides a welcome solution in Next.js development.
Beyond its traditional role as a web address, the URL has transformed into a powerful state management tool, revolutionizing how users interact with and save their online experiences.
This method shows how the URL can do a bunch of things, giving power to both users and developers to:
- share their curated content with a simple link.
- bookmark pages for future reference
- seamlessly save search results
Let’s apply this concept with a practical example in a server component.
import Link from 'next/link';
export default function ProductPage({
searchParams,
}: {
searchParams: { [key: string]: string | string[] | undefined };
}) {
const selectedColor = (searchParams?.color || 'blue') as string;
const selectedSize = (searchParams?.size || 'M') as string;
const sizeVariants = ['S', 'M', 'L', 'XL'];
return (
// ...
<div>
{sizeVariants.map((size, index) => (
<Link
key={index}
href={`?${new URLSearchParams({
color: selectedColor,
size,
})}`}
className={`${
selectedSize === size ? 'bg-blue-500' : 'bg-gray-200'
} mr-2 inline-block h-8 w-8 text-center leading-8`}
>
{size}
</Link>
))}
</div>
// ...
);
}
This component accepts a prop named searchParams
, which is an object containing key-value pairs representing the parameters from the URL query string. The selectedColor
and selectedSize
variables are extracted from these parameters.
Suppose you have a product page for a pink shirt in size ‘XL’. The URL might look like this:
https://www.examplestore.com/product-page?color=pink&size=XL
Now, let’s break down how the code contributes to this:
1 — Default Values:
In essence, default values act as a safety net, ensuring that the component gracefully handles diverse scenarios and remains robust in the face of potential irregularities in the data it receives.
const selectedColor = (searchParams?.color || 'blue') as string;
const selectedSize = (searchParams?.size || 'M') as string;
2 — Link Generation:
This line constructs the href
attribute for each link, creating a new URLSearchParams object with the selected color and the specific size.
By incorporating new URLSearchParams
, developers can create URLs that adhere to standards, ensuring compatibility across different browsers
href={`?${new URLSearchParams({ color: selectedColor, size })}`}
3 — Styling:
The class names dynamically style the links, highlighting the selected size with a blue background and providing a visual cue.
className={`${
selectedSize === size ? 'bg-blue-500' : 'bg-gray-200'
} mr-2 inline-block h-8 w-8 text-center leading-8`}
Let’s explore an example on the client side featuring a search input.
'use client';
import { MagnifyingGlassIcon } from '@heroicons/react/24/outline';
import { useSearchParams, usePathname, useRouter } from 'next/navigation';
export default function Search() {
const searchParams = useSearchParams();
const pathname = usePathname();
const { replace } = useRouter();
function handleSearch(term: string) {
const params = new URLSearchParams(searchParams);
if (term) {
params.set('query', term);
} else {
params.delete('query');
}
replace(`${pathname}?${params.toString()}`);
}
}
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}
defaultValue={searchParams.get('query')?.toString()}
onChange={(e) => {
handleSearch(e.target.value);
}}
/>
<MagnifyingGlassIcon className="absolute left-3 top-1/2 h-[18px] w-[18px] -translate-y-1/2 text-gray-500 peer-focus:text-gray-900" />
</div>
);
}
These lines use Next.js 14 client-side navigation hooks. useSearchParams
retrieves the current URL parameters, usePathname
gets the current pathname, and useRouter
provides routing functionalities. Even if you are in client-side and can use hooks like useState, we continue in the same line of the benefits that using URL as a state manager provides.
1 —handleSearch:
The handleSearch
function updates the URL parameters based on the provided search term. It utilizes a URLSearchParams
object to manage the parameters, setting or deleting the 'query' parameter based on whether a term is provided. Finally, it uses replace
to update the URL.
function handleSearch(term: string) {
const params = new URLSearchParams(searchParams);
if (term) {
params.set('query', term);
} else {
params.delete('query');
}
replace(`${pathname}?${params.toString()}`);
}
2 — defaultValue:
searchParams.get('query')?.toString()
retrieves the value of the 'query' parameter from the URL. If the parameter exists, it returns the value; otherwise, it returns null
.
<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}
defaultValue={searchParams.get('query')?.toString()}
onChange={(e) => {
handleSearch(e.target.value);
}}
/>
Sharing Curated Experiences with Ease:
One of the most compelling aspects of using the URL as a state manager is the seamless ability to share curated experiences effortlessly. Imagine a user exploring an online store’s vibrant collection of XL-sized, pink-hued products. With the URL serving as the state manager, the experience becomes dynamic and easily shareable.
Take, for instance, the URL: https://www.examplestore.com/product-page?color=pink&size=XL
. This link encapsulates a specific state within the application – in this case, showcasing products tailored to the user's preference for pink color and XL size. The magic happens when this link is shared.
Here’s a list of examples showcasing how the URL can serve as an ideal state manager, particularly in the context of server components:
Pagination: /products?page=2
Search Inputs: /search?query=shoes&type=sneaker
Sorting Options: /products?category=clothing&sort=price-asc
Filtering by Category: /products?category=electronics
User Preferences: /profile?theme=dark&language=en
Dynamic Forms: /form?step=2
Conclusion
The examples showcased here represent just the tip of the iceberg when it comes to leveraging the URL as a powerful state manager in Next.js 14 server and client components. The beauty lies in the simplicity and flexibility of this approach, offering developers a canvas on which they can unleash their creativity.