Skip to content
Merged
281 changes: 281 additions & 0 deletions docs/documentation/01-Getting Started/headless-config.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,281 @@
---
slug: /getting-started/headless-config
sidebar_position: 3
---
# Configuring the Framework

The `headstartwp.config.js` (previously, `headless.config.js`) file contains several config options for HeadstartWP. This file should export an object of type [HeadlessConfig](/api/modules/headstartwp_core/#headlessconfig).

## Usage with Next.js

The file **must be named** either `headstartwp.config.js` or `headless.config.js`. When `injectConfig` param of `withHeadstartWPConfig` (previously `withHeadlessConfig`) is set to true, the framework will look for these two files to be injected and loaded in the runtime bundle of the Next.js App.

```js title=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);
```
:::caution
Since `@headstartwp/next@1.2.0` you do not need to import `headstartwp.config.js` in `next.config.js` anymore, the framework will dynamically load the config.
:::

Here's a sample config file

```javascript title="headstartwp.config.js"
module.exports = {
sourceUrl: process.env.NEXT_PUBLIC_HEADLESS_WP_URL,
hostUrl: process.env.HOST_URL,
customPostTypes: [],
customTaxonomies: [],
redirectStrategy: '404',
useWordPressPlugin: true,
debug: {
redirects: false,
requests: false,
}
};
```

## Splitting client/server config

You can split the config between a client and a server config. This is needed when you're setting up [cache handler](/learn/data-fetching/caching/).

Simply create a `headstartwp.config.client.js` and a `headstartwp.config.server.js` file and HeadstartWP will pick them up and inject on the appropriate bundle.

We recommend `headstartwp.config.client.js` to be the source of truth and that you import it in the server config and make only the changes needed.

## sourceUrl

The `sourceUrl` option should point to a valid WordPress installation from where the headless site should be sourced to.

## useWordPressPlugin

The `useWordPressPlugin` indicates whether the WordPress instance at `sourceUrl` contains the Headless WordPress plugin. While it is possible to use this framework without the plugin, it is strongly recommended to install the WP plugin and set this option to true.

## hostUrl

The `hostUrl` option should contain the value where the frontend app lives. This would typically be the public domain of the site.

## host

The `host` option is automatically inferred if `hostUrl` is set. You probably don't need to set this option by yourself. The `host` value is used by the multisite feature to match the current site to a site config.

## customPostTypes

To add support for custom post types, add your custom post type to the `customPostTypes` setting in `headstartwp.config.js`.

```js title="headstartwp.config.js"
module.exports = {
sourceUrl: process.env.NEXT_PUBLIC_HEADLESS_WP_URL,
hostUrl: process.env.HOST_URL,
customPostTypes: [
{
slug: 'book',
endpoint: '/wp-json/wp/v2/book',
// these should match your file-system routing
single: '/book',
archive: '/books',
},
],
}
```

After adding a custom post type to the config, you will be able to fetch posts from the registered post type via the slug:

```js
queryPost({ params: { postType: ['book'] } });
queryPosts({ params: { postType:'book', perPage: 10 } });
```

The `single` option is required for several things including:
- properly previewing custom post types when the "single" route is at a different prefix. E.g: `/book/da-vince-code` instead of `/da-vice-code`; In this case, the framework will use the `single` path to redirect the previewed post to the right path/route.
- Matching post path permalinks with the current URL. E.g: when fetching a single custom post type the framework will filter the returned posts to the one that matches the existing URL. Therefore, the framework needs to know the single prefix url for custom post types. This is required to properly handle parent pages that share the same child slug.

It is also possible to pass a function, when doing so the default post types (post and pages) will be passed to the function. The code snipped below will disable post path mapping to the default post types.

```js title="headstartwp.config.js"
module.exports = {
sourceUrl: process.env.NEXT_PUBLIC_HEADLESS_WP_URL,
hostUrl: process.env.HOST_URL,
customPostTypes: (defaultPostTypes) => {
// disable post path mapping for default post types
return defaultPostTypes.map((postType) => ({...postType, matchSinglePath: false}));
}
}
```

Another use case is if you want your posts to sit at a different prefix (e.g: `/blog`), you can change your permalinks in WordPress (e.g: `/blog/%postname/`) and update the default `post` post type so that its `sigle` property is equal to `/blog`.

```js title="headstartwp.config.js"
module.exports = {
sourceUrl: process.env.NEXT_PUBLIC_HEADLESS_WP_URL,
hostUrl: process.env.HOST_URL,
customPostTypes: (defaultPostTypes) => {
return defaultPostTypes.map((postType) => {
if (postType === 'post') {
return {
...postType,
single: '/blog'
}
}

return postType;
};
}
}
```

## customTaxonomies

To add support for custom taxonomies, add your custom taxonomy to the `customTaxonomies` setting in `headstartwp.config.js`.

```js title="headstartwp.config.js"
module.exports = {
customPostTypes: [
{
slug: 'book',
endpoint: '/wp-json/wp/v2/book',
// these should match your file-system routing
single: '/book',
archive: '/books',
},
],
customTaxonomies: [
{
slug: 'genre',
endpoint: '/wp-json/wp/v2/genre',
postType: ['book'],
rewrite: 'genre',
restParam: 'genre'
},
],
}
```

After adding a custom taxonomy to the config, you will be able to filter posts by the registered taxonomy or fetch terms from it.

```js
queryPost({ params: { postType: ['book'], genre: 'action' } });
queryPosts({ params: { postType:'book', genre: 'action', perPage: 10 } });
queryTerms({ params: { taxonomy: 'genre' } });
```

Additionally, if you have an archive route such as `/blog` or `/books` filtering for all registered taxonomies works out of the box. For instance, take the headless config above the following page route:

```js title=src/app/books/[[...path]]/page.tsx
import { queryPosts } from '@headstartwp/next/app';

const BooksPage = async ({ params }) => {
const { data } = await queryPosts({
routeParams: await params,
params: { postType: 'book' }
});

return (
<ul>
{data.posts.map((post) => (
<li key={post.id}>{post.title.rendered}</li>
))}
</ul>
);
};

export default BooksPage;
```

This route would automatically handle the following URLs:
- /books -> list latest books
- /books/page/x -> paginate books
- /books/genre/genre-name -> filter books by genre
- /books/genre/genre-name/page/2 -> paginate books filtered by genre

:::caution
The code snippet above does not implement pre-fetching, which you probably want to. Check out the [pre-fetching docs](/learn/data-fetching/prefetching-data-server) for instructions.
:::

It is also possible to specify a function for 'customTaxonomies', when doing so the default taxonomies will be passed to the function. This can be used for instance to enable archive path matching.

```js title="headstartwp.config.js"
module.exports = {
customPostTypes: [
{
slug: 'book',
endpoint: '/wp-json/wp/v2/book',
// these should match your file-system routing
single: '/book',
archive: '/books',
},
],
customTaxonomies: (defaultTaxonomies) => {
return defaultTaxonomies.map((taxonomy) => ({ ...taxonomy, matchArchivePath: true })),
},
}
```
### restParam

This option shouldn't be necessary most of the time, but this is used to map a custom taxonomy to its REST API parameter. Most of the times the slug is equal to the restParam but in some cases it differs. For instance, the default post tag taxonomy has a slug of `post_tag` but a `restParam` of `tags` (i.e., to filter posts by `tags` in the REST API, we must use `tags=<tag-id>`).


### rewrite

This option controls the expected prefix the taxonomy must use in front-end urls. This generally should match `rewrite.slug` of the [register_taxonomy](https://developer.wordpress.org/reference/functions/register_taxonomy/) function.

## redirectStrategy

This option control how redirects are handled. There are 2 supported methods of handling redirects.
- 404: If a route 404, the framework will check to see if there's a redirect for that page in WP. If so it performs the redirect. This is the recommended option.
- always: When this option is set, the framework will **always** check for redirects before rendering any page. Using this option carefully since it will impact performance.

## debug

You can enable log debugging for both requests and redirects. `debug.requests` will enable logging all API requests made by the framework and `debug.redirects` will log all attempts to detect and fetch a redirect from WordPress.

## preview

### alternativeAuthorizationHeader

Tells HeadstartWP to use an alternative header (`X-HeadstartWP-Authorization`) instead of the default `Authorization` header for making authenticated preview requests.

Make sure you have HeadstartWP plugin >= 1.0.1, `@headstartwp/core` >= 1.3.1 and `@headstartwp/next`>= 1.3.1 to use this setting.

```js
module.exports = {
// other configs.
// ...

preview: {
alternativeAuthorizationHeader: true
}
}
```

### usePostLinkForRedirect

:::info
This feature was added in `@headstartwp/next@1.3.3` and requires the plugin version >= 1.1.2.
:::

This option, if enabled, will use the `post.link` property of the post being previewed to redirect to the appropriate route for previewing. This can be very useful to avoid the need for providing a custom [getRedirectPath](/learn/wordpress-integration/previews#getredirectpath) implementation by telling the preview handler to simply use the post's link as returned via the WordPress `get_permalink` function.

Note that you will want to make sure that your WordPress permalink structure closely follows the route structure of your Next.js app for this option to work well.

```js
module.exports = {
// other configs.
// ...

preview: {
usePostLinkForRedirect: true
}
}
```

More for info check out the [preview docs](/learn/wordpress-integration/previews#the-usepostlinkforredirect-setting).

45 changes: 40 additions & 5 deletions docs/documentation/01-Getting Started/quick-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,50 @@ sidebar_position: 0

# Quick Setup

If you're new to Next.js, we recommend reviewing [Next.js docs](https://nextjs.org/docs/getting-started).
If you're new to Next.js App Router, we recommend reviewing [Next.js App Router docs](https://nextjs.org/docs/app).

## System Requirements

- Node.js 16 or later
- Node.js 18 or later
- NPM >= 7
- WordPress >= 5.9 (prior versions might work but haven't been tested).
- WordPress >= 5.9 (prior versions might work but haven't been tested)
- Next.js 15+ (HeadstartWP only supports App Router with Next.js 15+)

## Installation

The easiest way to get started with HeadstartWP is by using `create-next-app` with the official starter project.
The easiest way to get started with HeadstartWP and App Router is by using `create-next-app` with the official App Router starter project.

```bash
npx create-next-app --use-npm -e https://github.com/10up/headstartwp/tree/trunk/projects/wp-nextjs
npx create-next-app --use-npm -e https://github.com/10up/headstartwp/tree/trunk/projects/wp-nextjs-app
```

Then run `npm run dev` and open http://localhost:3000 in your browser.

### Project Structure

The starter project follows Next.js App Router conventions:

```
src/
├── app/
│ ├── layout.tsx # Root layout
│ ├── page.tsx # Home page
│ ├── not-found.tsx # 404 page
│ ├── (single)/ # Route group for posts/pages
│ │ └── [...path]/
│ │ └── page.tsx # Dynamic catch-all route
│ ├── blog/
│ │ └── page.tsx # Blog archive
│ ├── category/
│ │ └── [slug]/
│ │ └── page.tsx # Category archive
│ └── globals.css
├── components/
│ ├── Blocks.tsx # Gutenberg blocks renderer
│ └── ...
└── middleware.ts # middleware
```

### Environment Variables

By default, the starter project will point to `js1.10up.com`. Either change the
Expand All @@ -35,6 +62,14 @@ NEXT_PUBLIC_HEADLESS_WP_URL=https://wordpress.test
NODE_TLS_REJECT_UNAUTHORIZED=0
```

### Key Differences from Pages Router

- **File-based routing**: Routes are defined in the `app/` directory
- **Server Components**: Components are server-rendered by default
- **Layouts**: Shared UI between routes using `layout.tsx`
- **Loading & Error states**: Special `loading.tsx` and `error.tsx` files
- **Async components**: Direct data fetching in Server Components

## Something Missing?

If something is missing in the documentation or if you found some part confusing, please file an [issue](https://github.com/10up/headstartwp/issues) for the documentation repository with your suggestions for improvement.
Loading
Loading