This is WordPress VIP's Next.js boilerplate for decoupled WordPress. It is not required for Node.js applications on VIP, but it helps solve many common use cases for decoupled / headless WordPress applications. If you choose to use it, VIP's decoupled plugin bundle must be installed and activated on the WordPress backend. The notes below describe its behavior when run on WordPress VIP's platform.
⚠️ This project is under active development. If you are a VIP customer, please let us know if you'd like to use this boilerplate and we can provide additional guidance. Issues and PRs are welcome. 💖
- Next.js 13
- Fetch data with Apollo and WPGraphQL
- Seamless previewing
- Easily map Gutenberg blocks to React components and incorporate your design system
- Automatic code generation from GraphQL queries
- Optional TypeScript support
npm install
The application is preconfigured to run with a local install of WordPress:
-
Install
wp-env
via these instructions. -
In the root directory of this repository, start and configure WordPress via:
wp-env start wp-env run cli "wp rewrite structure '/%year%/%monthnum%/%postname%/'"
Note that plain permalinks are not supported by the boilerplate project.
-
Next, run the Next.js development server with:
npm run dev
You should now be able to access:
-
Next.js front-end via http://localhost:3000
-
WordPress backend via http://localhost:8888/wp-admin with the default credentials:
- Username:
admin
- Password:
password
- Username:
Update the following environment variables defined in the .env
file:
NEXT_PUBLIC_GRAPHQL_ENDPOINT
: The full URL, including protocol, of your WPGraphQL endpoint. You can find it in the WordPress Admin Dashboard > Settings > VIP Decoupled.NEXT_PUBLIC_SERVER_URL
: The full URL, including protocol, of this Next.js site. This allows things like sitemaps and link routing to be configured correctly.
If you have additional environment variables, you can add them here.
Working remote environment settings are available in .env.production
to test against a live VIP WordPress backend.
Note that plain permalinks are not by the boilerplate project. Read the Permalink Setup section below for supported permalink configurations.
You should also review vip.config.js
for additional configuration options.
Start a development server, with hot-reloading, at http://localhost:3000.
npm run dev
npm run build
npm start
These are the exact same commands that will be executed when your application runs on WordPress VIP. Testing your production build locally is a good step to take when troubleshooting issues in your production site.
Note that the build
directory has been added to the .gitignore
file. This avoids a steady buildup of build artifacts in the git repository, but means that your local build will not be pushed to WordPress VIP. Instead, we'll run the build automatically whenever you push up code changes.
Previewing unpublished posts or updates to published posts works out of the box. Simply click the “Preview” button in WordPress and you’ll be redirected to a one-time-use preview link on the Next.js site. You can share your preview link with others; as long as they are logged in to WordPress in the same browser and have permissions to access that content, they will be able to preview it as well.
Plain permalinks are not recommended or supported by the boilerplate project. Any other pretty permalink structure that includes the %postname%
slug in the URL are supported, like:
- Day and name:
/%year%/%monthnum%/%day%/%postname%/
- Month and name:
/%year%/%monthnum%/%postname%/
- Post name:
/%postname%/
Pretty permalinks can be enabled via the WordPress backend.
When you query for content (posts, pages, and custom post types), you'll receive the post content as blocks. If the content was written with WordPress's block editor (Gutenberg), these blocks will correspond directly with the blocks you see in the editor. The block data you will receive roughly matches the output of WordPress’s parse_blocks
function, with some enhancements. To learn more, you can follow how block data is parsed and resolved in our extension of WPGraphQL.
Receiving the content as blocks allow you to easily create customizations defining the related component for each block type. This boilerplate provides a mapping for basic components like headings, paragraphs, lists, and tables (see '@/components/Blocks/index.tsx'
). We supply components for a few basic block types in order to demonstrate this approach, but you will undoubtedly need to write additional components.
Here is a simple example of how to override the default block mapping to support all of the default and custom blocks that you use in your WordPress instance:
import PostContent from '@/lib/components';
import MyCustomHeader from 'my-design-system';
export default function Post( props: Props ) {
return (
<main>
<h1>{props.title}</h1>
<PostContent
blockMapOverrides={ {
'core/heading': MyCustomHeader,
} }
blocks={props.blocks}
/>
</main>
);
}
If you used WordPress's classic editor, you will receive a single block representing the HTML content of the entire post. A ClassicEditorBlock
component is provided to render these blocks.
When running the development server, in order to help you identify blocks that have not yet been mapped to a React component, this boilerplate will display an "Unsupported block" notice. This notice is suppressed in production and the block is simply ignored.
When writing code that links to another page in your Next.js application, you should use Next.js's Link
component so that the request is routed client-side without a full-round trip to the server.
However, when user-authored blocks contain links, the innerHTML
is handled by React and you don't have an opportunity to use the Link
component. To address this, our boilerplate listens for link clicks and will route them client-side if the link destination is determined to be internal. You can configure which hostnames are considered internal in lib/config
.
Next.js is optimized to create performant pages that are statically generated at build time (getStaticProps
) or server-side-rendered at request time (getServerSideProps
). This results in HTML that is cacheable at the edge and immediately crawlable by search engines—both critically important factors in the performance and success of your site.
This boilerplate uses Apollo to query for data using GraphQL, and it is configured and ready to use. Note that Apollo hooks (e.g., useQuery
) are not compatible with getStaticProps
or getServerSideProps
.
Many Apollo implementations, including Next.js’s official examples, implement a complex, isomorphic approach that bootstraps and hydrates the data from the server-side render into an in-memory cache, where it can be used for client-side requests. We have intentionally avoided this approach because it introduces a large performance penalty and increases the risk that performance degrades even more over time.
Before adding client-side data fetching, examine your typical user flows in detail and consider whether it truly benefits your application and its users. Skipping this complicated step simplifies your configuration, decreases page weight, and usually increases overall performance. If you absolutely need to perform client-side data fetching, an ApolloProvider
is exported and ready to use in graphql-provider
. Note that data from the server-side render will not be hydrated into the store.
Our boilerplate has a code generation step that examines the GraphQL queries in ./graphql/queries/
, introspects your GraphQL schema, and generates TypeScript code that can be used to load data via Apollo. See LatestContent
for an example of using generated code with getStaticProps
and Post
for an example with getServerSideProps
.
Having declared types across the entire scope of data fetching chain—queries, responses, and React component props—is incredibly powerful and provides confidence as you build your site. Code generation runs automatically on all GraphQL queries in ./graphql/queries/
whenever you start the development server or build the application. If you need to run it manually, you can use:
npm run codegen
In development, if you make changes or additions to your queries, you will need to restart the development server to see those changes reflected.
Responses from Next.js are cached by VIP's page cache for five minutes by default, overriding the default behavior of Next.js to help avoid invalidation issues.
As POST
requests, GraphQL queries are not cached. However, when using static or server-side data loading—which is strongly recommended—these queries are effectively cached by the page cache.
If you have Redis deployed alongside your application hosted on VIP Go, you can cache API responses and other data there. An example preconfigured to work on VIP is provided at ./pages/api/books.ts
.
Next.js offers a way to implement middleware, which can be a great way to separate logic from presentation. Additionally, this middleware can target an "edge runtime" on some platforms (for example, Vercel’s "edge functions"). Middleware is supported on VIP, but will not run at the edge. Instead, middleware runs on your origin servers with the rest of your application.
This project uses middleware (.pages/_middleware.ts
) to implement the healthcheck endpoint required by WordPress VIP's platform. Middleware is a great way to solve for use cases that would otherwise require a custom server.
"Serverless" functions that live in /pages/api/
and target Node.js (via JavaScript or TypeScript) are supported on VIP. Just like middleware, they will run on your origin servers.
WordPress has provided a default sitemap since version 5.5, and there are many plugins that provide this functionality as well. Rather than recreate sitemaps in Next.js, we recommend that you point to the sitemaps produced by WordPress. If the permalinks generated by WordPress point to your decoupled front-end (and they should!), this will work seamlessly. Google and other search engines will happily crawl sitemaps on other domains, provided you point them there. The robots.txt
provided by this boilerplate does just that (see ./pages/api/robots.ts
).
Feeds can be linked by passing a feedLink
prop to the Page
component. Again, we recommend pointing to the feeds that are already being generated by WordPress.
This boilerplate is written in TypeScript. Next.js has built-in support for TypeScript and processes it automatically in both development and production. If you're already proficient in TypeScript, see tsConfig.json
for details.
You don’t need to use TypeScript to use this boilerplate: our tsConfig.json
is lenient and allows you to write code in either TypeScript or JavaScript.
Next.js provides an ESLint integration, which means you don't need to separately install eslint
packages. This boilerplate provides an ESLint config based on the default Next.js ESLint rules, which can be integrated with most code editors. To run linting manually, use:
npm run lint
Many linting issues can be fixed automatically with:
npm run lint:fix
There is support for tests using Jest. Some basic unit tests are provided for boilerplate code and carry a .test.ts
extension. Run tests using:
npm test
URL imports allow you to import packages or images directly from URLs instead of from local disk. At this time, the feature is not stable, introduces security risk, and is not recommended.
The Next.js Image
component, next/image, is an extension of the HTML <img />
element, evolved for the modern web. It includes a variety of built-in performance optimizations. Next.js will automatically determine the width and height of your image based on the imported file.
For the API images, the srcSet
property is automatically defined by the deviceSizes
and imageSizes
properties added to the next.config.js
file. If you need to manually set the srcSet
for a particular image, you should use the <img />
HTML tag instead.
- Webpack 4 support has been removed. See the Webpack 5 upgrade documentation for more information.
- The
target
option has been deprecated. If you are currently using thetarget
option set toserverless
, please read the documentation on how to leverage the new output. - Next.js
Image
component changed its wrapping element. See the documentation for more information. - The minimum Node.js version has been bumped from
12.0.0
to12.22.0
which is the first version of Node.js with native ES Modules support.