Skip to main content

The usePosts hook

The usePosts hook is the Next.js binding for the useFetchPosts.

The usePosts hook fetches a collection of WordPress posts from a registered post type. Its basic usage is very simple.

Basic Usage

Assuming a src/pages/blog/[[...path]].js route with the following content.


This example is using the optional catch-all route [[..path]].js because we want the /blog route to be handled by the same file.

import { usePost } from '@headstartwp/next';

const ArchivePage = () => {
const { loading, error, data } = usePosts({ per_page: 10 });

if (loading) {
return 'Loading...';

if (error) {
return 'error...';

return (
{data?.posts?.map((post) => (
<h2 key={}>{post.title.rendered}</h2>

The route will automatically render the latest 10 posts and you get pagination, category, tags and custom taxonomies filtering for free as the following paths will automatically map URL segments into REST API requests:

  • /blog/page/2
  • /blog/category/category-name
  • /blog/tag/tag-name
  • /blog/category/category-name/page/2

Queried Object

The usePosts hook exposes a queriedObject. It's similar to WordPress get_queried_object() function.

It essentially returns what's being queried for, e.g., author or category. If the current page is querying posts within a certain author, then that author object will be populated in Similarly, if the current page is querying posts from a given category data.queriedObject.term will be populated with that category.


// category-name can either come from the URL or be manually specified.
const { data } = usePosts({ taxonomy: 'category', category: 'cat-name' });

return (
<h1>Category Page: {}</h1>

Category Archive

You can use the usePosts hook to create a category archive route (src/pages/category/[...path].js).


We use [...path].js here because we do not want the /category route to render anything.

import {
} from '@headstartwp/next';

const CategoryPage = () => {
const { data } = usePosts({ taxonomy: 'category' });

return (
<h1>Category Page: {}</h1>
{ => (
<li key={}>
<Link href={}>{post.title.rendered}</Link>
<Pagination pageInfo={data.pageInfo} />

Archive Path Matching

When using usePosts to create archive pages (e.g. a category archive) you can optionally enable "archive path matching" to ensure that your archive routes match the expected permalink dictated by WordPress. Without "archive path matching", your archive routes would match as long as the last segment of the url is a valid term slug.

For instance, let's take the /category/[...path].js route above. It will match URLs like:

  • /category/cat-name
  • /category/parent-cat-name/cat-name

The framework, however, does not check if parent-cat-name is the de facto parent of cat-name, and even worse, it has no way to know (without additional rest api calls) if parent-cat-name is even a valid category.

To address this, you can pass matchArchivePath to usePosts to tell the framework to check the link property of the queriedObject, i.e if the link property of cat-name returned by the WordPress REST API matches the front-end path.

This setting can also be enabled in headstartwp.config.js globally.

module.exports = {
// enable archive path mapping for all default taxonomies
customTaxonomies: (defaultTaxonomies) => {
return => ({ ...taxonomy, matchArchivePath: true })),

Known limitations

  • It is not possible to fetch posts from more than one post type.