December 3, 2023
O. Wolfson
A common web development practice is where state is actually stored in the browser's URL as query parameters. This might be referred to as URL State Management. For example, this is a common practice for pagination, where the page number and number of items per page are stored in the URL.
Example:
http://myapp.com/blog?limit=4&page=2
Base url: http://myapp.com/blog
Query parameters: ?limit=4&page=2
This approach has its roots in the early days of the web, where interactive elements on websites were limited and server-side rendering was prevalent. Storing state in the URL made it easy to maintain state across page reloads and server requests. Historically, as the web evolved from static pages to more dynamic and interactive experiences, developers sought ways to preserve and share the state of web applications. Utilizing the URL's query parameters became a popular method for achieving this, especially for features like pagination, where the state needs to be consistent across client and server interactions.
In this article, we'll explore the advantages of storing state in the URL and how it can be implemented in a Next.js application.
In the URL http://myapp.com/blog?limit=4&page=2
, the main components are the base URL (http://myapp.com/blog
) and the query parameters (?limit=4&page=2
). This URL is a typical example of URL State Management in a web application, specifically in the context of a blog page with pagination. Here's a breakdown of what's going on:
Base URL (http://myapp.com/blog
):
myapp.com
.Query Parameters (?limit=4&page=2
):
?
symbol marks the beginning of the query parameters in the URL.limit=4
: This parameter indicates the number of blog posts to display on a single page. In this case, it's set to show 4 posts per page. This is a part of the pagination functionality, controlling how much content is rendered at one time for the user.page=2
: This parameter specifies the current page number in the pagination sequence. It tells the web application to display the second page of the blog posts.Easy Bookmarking and Link Sharing: By storing state information like page number and post limit in the URL, users can bookmark, share, or return to a specific state of the web page. For instance, sharing a link to the third page of a blog roll is as simple as copying and sharing the URL.
Improved Search Engine Optimization (SEO): Search engines can crawl and index multiple pages of content more efficiently when the distinct pages are represented by unique URLs. This helps in better organizing and presenting content in search results.
State Persistence: Storing the state in the URL ensures that the state is preserved even when the page is reloaded or accessed from different browsers or devices. This is particularly important for server-side rendered applications.
Enhanced User Experience: Users can easily understand and manipulate the URL to navigate through the content. For example, changing the page number directly in the URL provides a quick way to jump to a specific page.
No Need for Additional State Management: Using the URL to store state reduces the need for additional client-side state management tools or cookies, simplifying the application architecture.
In the code snippet below, from a Nest.js app, the page, currentPage
, and limit of posts per page, postsPerPage
, values are derived from the URL's query parameters (searchParams
). This design choice reflects the advantages described above, making the blog roll both user-friendly and optimized for web standards.
javascript// Importing necessary components and functions
import Link from "next/link";
import { getPosts } from "@/lib/posts.mjs";
// Defining the BlogPost interface for TypeScript type checking
interface BlogPost {
slug: string;
type: string;
date: string;
title: string;
description: string;
image: string;
author: string;
tags: string[];
formattedDate?: string; // Optional property
}
// Async function component 'Blog'
const Blog = async ({
searchParams,
}: {
searchParams: { [key: string]: string | string[] | undefined },
}) => {
// Parsing and setting the current page from search parameters, defaults to 1
const currentPage =
typeof searchParams.page === "string" ? Number(searchParams.page) : 1;
// Parsing and setting posts per page, defaults to 5
const postsPerPage =
typeof searchParams.limit === "string" ? Number(searchParams.limit) : 5;
// Retrieving posts with filters (type, page, limit) and calculating total pages
const { posts: blogs, totalPosts } = getPosts(
"blog",
postsPerPage,
currentPage
);
const totalPages = Math.ceil(totalPosts / postsPerPage);
isPreviousDisabled = currentPage <= ;
isNextDisabled = currentPage >= totalPages;
disabledLinkStyle = ;
(
);
};
;
BlogPost
: Defines the structure for a blog post object for TypeScript type checking.Blog
: This is an asynchronous functional component that accepts searchParams
as props. These parameters are used to control pagination.Find the full code for this example.
See the live demo for this example.