Skip to main content

Setting up the framework from scratch

The recommended way to get started with the framework is by installing the official starter project. See Quick Setup for more information.

This guide will help you set up the framework in a clean Next.js project.

Bootstrap the Next.js project

Start by bootstrapping your next.js project.

npx create-next-app@latest --use-npm

and install the following packages

npm install --save @headstartwp/core @headstartwp/next

headstartwp.config.js

Create a headstartwp.config.js file at the root of your Next.js project.

headstartwp.config.js
/**
* Headless Config
*
* @type {import('@headstartwp/core').HeadlessConfig}
*/
module.exports = {
sourceUrl: process.env.NEXT_PUBLIC_HEADLESS_WP_URL,

useWordPressPlugin: true,
};

Env variables

Then create a .env (or .env.local) with the following contents:

NEXT_PUBLIC_HEADLESS_WP_URL=https://my-wordpress.test

You can call the env variable anything you want, just make sure to update headstartwp.config.js accordingly.

If you're developing locally and your WordPress instance uses https but does not have a valid cert, add NODE_TLS_REJECT_UNAUTHORIZED=0 to your env variables.

next.config.js

Create a next.config.js file with the following contents:

next.config.js
const { withHeadstartWPConfig } = require('@headstartwp/next/config');

/**
* Update whatever you need within the nextConfig object.
*
* @type {import('next').NextConfig}
*/
const nextConfig = {};

module.exports = withHeadstartWPConfig(nextConfig);

pages/_app.js

Create a custom _app.js to wrap the application with HeadlessApp component.

src/pages/_app.tsx
type MyAppProps = {
themeJson: Record<string, unknown>;
fallback: Record<string, unknown>;
};

const MyApp = ({ Component, pageProps }: AppProps<MyAppProps>) => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { fallback = {}, themeJson = {}, ...props } = pageProps;

return (
<HeadlessApp
pageProps={pageProps}
settings={{
// instruct the framework to use Next.js link component
// or your own version
linkComponent: Link,
}}
useYoastHtml
>
<Layout>
<Component {...props} />
</Layout>
</HeadlessApp>
);
};

Setting up the preview endpoint

The WordPress plugin expects the preview endpoint to be located at /api/preview.

To enable support for previews, create a src/pages/api/preview.js with the following contents:

src/pages/api/preview.ts
import { previewHandler } from '@headstartwp/next';
import type { NextApiRequest, NextApiResponse } from 'next';

/**
* The Preview endpoint just needs to proxy the default preview handler
*
* @param req Next.js request
* @param res Next.js response
*
* @returns the preview handler
*/
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
return previewHandler(req, res);
}

Setting up the revalidate endpoint

The framework supports ISR revalidation triggered by WordPress. To enable ISR revalidate, make sure you have the WordPress plugin enabled and activate the option in WordPress settings.

ISR Option

Then add the revalidateHandler to src/pages/api/revalidate.js

src/pages/api/revalidate.ts
import { revalidateHandler } from '@headstartwp/next';
import type { NextApiRequest, NextApiResponse } from 'next';

/**
* The revalidate endpoint just needs to proxy the default revalidate handler
*
* @param req Next.js request
* @param res Next.js response
*
* @returns the revalidate handler
*/
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
return revalidateHandler(req, res);
}

Creating your first route

To make sure everything is working as expected create a catch-all route called pages/[...path].js. This route will be responsible for rendering single post and pages.

By creating a [...path].js route, the framework will automatically detect and extract URL parameters from the path argument.

src/pages/[...path].tsx
import type { PostParams } from '@headstartwp/core';
import {
usePost,
fetchHookData,
addHookData,
handleError,
usePosts
} from '@headstartwp/next';
import { BlocksRenderer } from '@headstartwp/core/react';

const params: PostParams = { postType: ['post', 'page' ] };

const SinglePostsPage = () => {
const { loading, error, data } = usePost(params);

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

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

return (
<div>
<h1>{data.post.title.rendered}</h1>
<BlocksRenderer html={data.post.content.rendered} />
</div>
);
};

export default SinglePostsPage;

Then, visit any single post or page, e.g: http://localhost:3000/hello-world and you should see both the title and the content of that post/page.

Date URLs will also work: e.g: http://localhost:3000/2022/10/2/hello-world

Add SSR/SSG

With the example above, you might have noticed that the data is only being fetched on the client. You must use one of the Next.js data fetching methods to enable SSR/SSG.

Add this to the pages/[...path].js file

src/pages/[...path].tsx
import type { HeadlessGetStaticProps } from '@headstartwp/next';

export const getStaticProps = (async (context) => {
try {
const settledPromises = await resolveBatch([
{
func: fetchHookData(usePost.fetcher(), context, { params: singleParams }),
},
{ func: fetchHookData(useAppSettings.fetcher(), context) },
]);

return addHookData(settledPromises, { revalidate: 5 * 60 });
} catch (e) {
return handleError(e, context);
}
}) satisfies HeadlessGetStaticProps;

// `satisfies` allow TS to infer the correct types for context as well as makes it
// possible to correctly infer the page props

Then refresh the page and voilá! Data is now being fetched on the server.