Crash Course: Build a Headless WordPress App with Faust and Atlas Content Modeler

25 minute read
Grace Erixon
Grace Erixon
Associate Developer Advocate

This tutorial will teach you how to create a headless WordPress app using Faust and Atlas Content Modeler. It assumes a basic understanding of JavaScript, React fundamentals, GraphQL fundamentals, and WordPress.

By the end of this tutorial, you will be able to:

  • Create ACM models
  • Create new posts for those models in the WordPress admin
  • Run test queries to get ACM model data using the GraphiQL IDE
  • Use Faust.js to fetch ACM model data in our decoupled Next.js app
  • Use the fetched ACM model data to render our React components

What We’ll Build

To showcase how to use Faust and ACM, we’re going to build a site containing Taylor Swift’s discography.

We’ll use ACM to create the content models we need in WordPress, then we’ll query for that data from a decoupled frontend Next.js app.

This will require the following three types of pages:

  • /– This main “Albums” page should contain a gallery of each of Taylor Swift’s albums.
  • /albums/red-taylors-version – This “Album details” page should contain the info about an individual album. “red-taylors-version” in this example represents the slug of the album.
  • /songs/all-too-well-10-minute-version-taylors-version – This “Song details” page should contain the info about an individual song. “all-too-well-10-minute-version-taylors-version” in this example represents the slug of the song.

Prerequisites

To get started, you’ll need to make sure that you have both Node.js and npm installed. If you aren’t sure whether or not you have this software installed, you can run the following commands in your terminal:

node -v
npm -v

The terminal output should either tell you which versions you have installed or that it cannot find the commands node or npm to run them. Using a tool like nvm (Node Version Manager) to install and manage versions of Node.js on your machine can be helpful when working on multiple projects.

Configure Your WordPress Site

Next, let’s set up a local WordPress site that can be used as the data source for our example application. We will walk through creating a WordPress site via Local and WP Engine’s Atlas: Headless WP extension. Then, we’ll populate the site with the specific data we need for this tutorial.

Install Local and the Atlas: Headless WP Extension

Local is WP Engine’s local development tool, and it makes it very easy to work with WordPress locally. You can install the app and create a new WordPress site in a few steps.

Before we create the WordPress site, we need to install and enable the Atlas: Headless WP extension. Navigate to the Add-ons page and find the Atlas: Headless WP extension. Click the Install Add-on button on the extension’s page and then the Enable & relaunch button. After Local reloads, this extension should show that it is Installed & enabled.

Create a Headless WordPress Site with Local

Now, we’re ready to create a WordPress site! Click the + button at the bottom left corner of the window. Select Create A New Site then click Continue.

Choose a name for your new site, and then click Continue.

In order to enable the Atlas Add-on for this site, you have to choose a custom environment for the site. We can select the default options for the PHP version, Web server, and Database options. Below, make sure to check the box to enable the Atlas Add-on. Then, click Continue.

Lastly, specify values for the WordPress Username and WordPress Password, and be sure to remember these values. Once that is complete, click Add Site.

Depending on your permissions, Local may ask for permission to make modifications to your system. After your site has been successfully installed, you will see it in the Local dashboard.

To access the WP Admin panel of your new site, click WP Admin in the site details pane. You will need to authenticate with the username and password you created in the previous step.

To access the Next.js front-end application of your new site, click Open site in the site details pane.

Check Plugins and Settings

With a basic headless WordPress site up and running, there are a few things left to install and some settings to check.

The Atlas Add-on will have automatically installed and activated the FaustWP plugin and the WP GraphQL plugin to your site. The WP GraphQL plugin also adds a GraphQL tab to the WP Admin sidebar.

From the Plugins > Add new menu, search for Atlas Content Modeler in the WordPress plugin repository. Install and activate the plugin, which will add a Content Modeler tab to your WP Admin sidebar below the GraphQL tab.

Open the GraphQL > Settings menu and check the option labeled Enable GraphQL Debug Mode and then check the option labeled Enable Public Introspection. Click Save Changes.

With Debug Mode enabled, you will get more helpful error output as you develop your application. Note that using Debug Mode is not recommended for production sites.

Create Custom Content Models with ACM

Plan Content Models

Let’s think about what content models we need to create to store the necessary data.

Songs

  • Song Title – Text field. The song’s name.
  • Length – Text field. The song’s timestamp.
  • Genre – Taxonomy. These taxonomy terms represent the song’s genres (Pop, Country, Alternative, etc.).
  • Lyrics – Rich text field. The song’s formatted lyrics.

Albums

  • Album Title – Text field. The album’s name.
  • Release Date – Date field. The album’s release date.
  • Cover – Media field. An uploaded image of the album’s cover.
  • Track List – Relationship field. These relationships between Album → Songs represent the songs on each album.

Now that we have our game plan together for what pages we’ll need and what content models we’ll need to set up in Atlas Content Modeler, let’s hop to it!

Create Content Models

Songs Model

Let’s create our Songs model first since it’s a bit simpler. In the WordPress admin sidebar, we’ll go to Content Modeler > Models and click the Get Started button to create a new model.

We’ll fill out the fields as follows:

  • Give the model a Singular Name of “Song” and a Plural Name of “Songs”.
  • Stick with the auto-generated Model ID of “song”.
  • Set the API Visibility to “Public” so we’ll be able to query for Song data from our front-end JavaScript app.
  • Select a Model Icon.
  • Enter a Description for the model.

Then click the Create button to create the new model.

In the WordPress admin sidebar, you’ll see that a menu item has been added for our new Songs custom post type.

Songs Model Fields

First, let’s create the Song Title field to store the name of each song. We’ll set the field up like this:

  • Select Text for the type of field.
  • Enter a Name of “Song Title”.
  • Stick with the auto-generated API Identifier of “songTitle”.
  • Check the box to make this the Title Field.
  • Select an Input Type of “Single line”.

Then click the Create button to create the new field and click the + button to edit the next one. We’ll create the Length and Lyrics fields in a similar fashion.

The Length field will look like this:

  • Select Text for the type of field.
  • Enter a Name of “Length”.
  • Stick with the auto-generated API Identifier of “length”.
  • Select an Input Type of “Single line”.

The Lyrics field will look like this:

  • Select Rich Text for the type of field.
  • Enter a Name of “Lyrics”.
  • Stick with the auto-generated API Identifier of “lyrics”.

As the last step in creating our Songs model, we need a way to group together Songs based on their genre (Alternative, Country, Pop, etc.). That’s a perfect use case for a WordPress taxonomy.

From the WordPress admin sidebar, go to Content Modeler > Taxonomies.

Add a new taxonomy and configure it like this:

  • Enter a Singular Name of “Genre”.
  • Enter a Plural Name of “Genres”.
  • Stick with the auto-generated Taxonomy ID of “genre”.
  • For Models, select “Songs”.
  • Set API Visibility to “Public” to make sure this taxonomy is added to the GraphQL schema.

That’s it for our Songs model! Now, let’s turn to the Albums model.

Albums Model

In the WordPress admin sidebar, we’ll go back to Content Modeler > Models and click the New Model button to create our second model.

We’ll fill out the fields as follows:

  • Give the model a Singular Name of “Album” and a Plural Name of “Albums”.
  • Stick with the auto-generated Model ID of “album”.
  • Set the API Visibility to “Public” so we’ll be able to query for Song data from our front-end JavaScript app.
  • Select a Model Icon.
  • Enter an optional Description for the field.

Then click the Create button to create the new model.

In the WordPress admin sidebar, you’ll see that a menu item has been added for our new Albums custom post type.

Albums Model Fields

First, let’s create the Album Title field to store the name of each album. We’ll set the field up like this:

  • Select Text for the type of field.
  • Enter a Name of “Album Title”.
  • Stick with the auto-generated API Identifier of “albumTitle”.
  • Check the box to make this the Title Field.
  • Select an Input Type of “Single line”.

Then click the Create button to create the new field and click the + button to edit the next one. We’ll create the Release Date, Cover, and Track List fields in a similar fashion.

The Release Date field will look like this:

  • Select Date for the type of field.
  • Enter a Name of “Release Date”.
  • Stick with the auto-generated API Identifier of “releaseDate”.

The Cover field will look like this:

  • Select Media for the type of field.
  • Enter a Name of “Cover”.
  • Stick with the auto-generated API Identifier of “cover”.

The Track List field will look like this:

  • Select Relationship for the type of field.
  • Enter a Name of “Track List”.
  • Stick with the auto-generated API Identifier of “trackList”.
  • Select “Songs” for the Model to Reference.
  • Select “One to Many” for the Connections.
  • Check the box to Configure Reverse Reference.
  • Enter a Reverse Display Name of “Album”.
  • Stick with the auto-generated Reverse API Identifier of “album”.

That’s the last of the fields for our Albums model.

Populate Data about Taylor Swift’s Discography

Next, let’s see how our content creators can create and edit Album and Song data.

Add Song Data

From the WordPress admin sidebar, go to Songs > Add New. You’ll see the fields we registered reflected here. Go ahead and create entries for each of Taylor Swift’s songs (Taylor’s Version, of course).

Add Album Data

From the WordPress admin sidebar, go to Albums > Add New. You’ll see the fields we registered reflected here. Go ahead and create entries for each of Taylor Swift’s albums (again, Taylor’s Version, of course).

Test a Query in the GraphiQL IDE

At this point, our models and fields have been created and populated with data, and ACM has automatically added them to the GraphQL schema for us. This means that things are already set up for us to query for Songs or Albums data from our front-end JavaScript app! Let’s fire off a test query to try that out.

Head back to the Content Modeler > Models page.

Click the ... icon next to our Songs model, then click Open in GraphiQL.

This will shoot you over to the embedded GraphiQL IDE that the WPGraphQL plugin provides and auto-populate it with a query and fragment for the Songs model’s data.

Go ahead and click the ▶️ icon to fire off the query, and you’ll see that the data for the Songs posts you had created comes back in the response. Magic! ✨

RUN GQty’s generate script

Finally, we have to update the Next.js application on the new data structure. In Local, click the Open site shell link, which will open your terminal. The generate script creates a new schema file that describes all of our data that gets used by GQty, Faust’s current GraphQL client. Find the app-node directory and then run the generate script. :

cd ../../app-node
npm run generate

Done! Our Songs and Albums content models are now fully built out, and we’ve seen how we can query for them via WPGraphQL. Now let’s use this data in our front-end Next.js app.

Build the Albums Page

Now that you have a WordPress site with the Taylor Swift discography data, we can get started with Next.js. Remember that the Next.js application is running through Local in conjunction with the WordPress instance.

In a code editor, open the project directory titled app-node. This will exist in Local Sites > faustandacm (or whatever you named your Local site) > app-node. After opening the project directory, your text editor should look like this:

Replace index.tsx

To get started, open up the src/pages/index.tsx file in your editor. Next.js uses a routing method known as page-based routing, meaning that generally, the routes of your site or application will correspond to the file structure of your /pages folder. In this case, the index.tsx file at the root of the directory corresponds to the root of your site, which should be available at the / URL.

We want to replace this default home page with a new page displaying a grid of all of Taylor Swift’s albums, so delete everything that is currently in index.tsx. With a blank slate, we can start to build out the page.

To get the data we need, start by add an import statement to the top of our /pages/index.tsx file:

import { client } from "client";

Then, create a function to hold our Home page with a blank return statement for now. The useQuery() hook is a GQty hook that gives access to your entire schema. Inside of the Home function, use the provided client to query for the list of all of the albums you created in your WordPress admin.

export default function Home() {
  const { useQuery } = client;
  const albums = useQuery().albums()?.nodes;

  return ();
}

Next, add another import statement for a component that we will build in a later step – AlbumCard.

import AlbumCard from "../components/AlbumCard";

Inside of the Home component, we need to write an expression that iterates over the albums array to display each. We will do this by rendering an unordered list, then map over the albums and render a list item containing an AlbumCard component for each album.

  return (
    <ul>
      {albums.map((album) => (
        <li key={album.databaseId}>
          <AlbumCard album={album} />
        </li>
      ))}
    </ul>
  );

Create the AlbumCard Component

Now, let’s create the component that we are calling in this main file. Inside of the src/components directory, create a new file called AlbumCard.tsx.

First, add an import statement for the Next.js Link component.

import Link from "next/link";

Then, create a function called AlbumCard that accepts an individual album’s data as a prop. Inside of the return statement, display an image of the album’s cover wrapped in a link that points to the individual album details page that we will build out later.

export default function AlbumCard({ album }) {
  return (
    <Link href={`/albums/${album.slug}`}>
      <img src={album?.cover?.mediaItemUrl} alt={album.cover.altText} />
    </Link>
  );
}

Create the Layout Component

Now, let’s create the component that we can use to wrap every other JSX element in to follow Next.js’ rule that JSX expressions must have one parent element. Inside of the src/components directory, create a new file called Layout.tsx.

Add an import statement for the Next.js Head component. Add the following code to the return statement of the Layout function.

import Head from "next/head";

export default function Layout({ children }) {
  return (
    <>
      <Head>
        <title>TS Discography</title>
      </Head>
      <main>{children}</main>
    </>
  );
}

Back in index.tsx, import this file:

import Layout from "../components/Layout";

Then, wrap everything currently inside of the return statement inside of a <Layout> component.

<Layout>
  ...
</Layout>

Style the Albums Page

Check out the site running in the browser! You can see the nine album covers displayed, but it’s not very pretty. Let’s add some styles to this page! Create a new file in src/scss/pages called albums.module.scss and add the following SCSS declarations:

.gallery {
  margin: 5% 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-gap: 15px;
}

.galleryItem {
  list-style-type: none;
  opacity: 1;
}

.galleryItem:hover img {
  opacity: .6;
}

Back in the index.tsx file, import the new SCSS file that you created:

import styles from "scss/pages/albums.module.scss";

Then, add the .gallery class to the ul element and the .galleryItem class to the li element.

      <ul className={styles.gallery}>
        ...
          <li className={styles.galleryItem} key={album.databaseId}>

Review the index.tsx File

After all of these steps, your index.tsx file should look like this:

import { client } from "client";
import AlbumCard from "../components/AlbumCard";
import Layout from "../components/Layout";
import styles from "scss/pages/albums.module.scss";

export default function Home() {
  const { useQuery } = client;
  const albums = useQuery().albums()?.nodes;

  return (
    <Layout>
      <ul className={styles.gallery}>
        {albums.map((album) => (
          <li className={styles.galleryItem} key={album.databaseId}>
            <AlbumCard album={album} />
          </li>
        ))}
      </ul>
    </Layout>
  );
}

Build the Single Album Details Page

Create [albumSlug].tsx

Now, let’s create the page that display’s an individual album’s details. Inside of the src/pages directory, create a new folder called albums with a new file inside of it called [albumSlug].tsx.

To get the data we need, we need to import several things:

import { client } from "client";
import { useRouter } from "next/router";
import { getNextStaticProps } from "@faustjs/next";

Then, create a function called Album() with a blank return statement for now. Inside of this Album function, use the provided client, the Next.js Router component, and GQty’s useQuery() hook to query for the specific album’s page based on the URL. The useRouter hook’s query object allows us to access the dynamic route parameter albumSlug that we created with the [albumSlug].tsx file and use that inside of our GraphQL client.

export default function Album() {
  const { useQuery } = client;
  const { query = {} } = useRouter();
  const { albumSlug } = query;

  const album = useQuery().album({
    id: albumSlug,
    idType: "SLUG",
  });

  return();
}

Next.js supports Static Site Generation (SSG) out of the box, however, you are responsible for defining getStaticProps, fetching the necessary data, and providing it on props. Faust.js provides getNextStaticProps, a function that accepts two arguments: the static props context and an object with a Page key and client key. The reason Album and client are passed to getNextStaticProps is because under the hood Faust.js performs a skeleton render of the page component to know what data to fetch and what queries to build.

If you don’t know exactly how you want to statically generate your site, a good option for getStaticPaths is to inform Next.js not to generate any HTML at build time but to generate it on the fly as new requests come in.

Check out the Faust.js documentation to read more about how Faust.js handles SSG. Add the two functions discussed to the end of your [albumSlug].tsx file:

export async function getStaticProps(context) {
  return getNextStaticProps(context, {
    Page: Album,
    client,
  });
}

export function getStaticPaths() {
  return {
    paths: [],
    fallback: "blocking",
  };
}

Now that we have access to the individual album’s data, we’ll focus on the view for the page. Start with adding import statements for our Layout component and the Next.js Link component.

import Layout from "../../components/Layout";
import Link from "next/link";

Inside of the return statement of the Album function, we will build the JSX elements. First, wrap everything inside of the Layout component. At the top of the screen, we will also add a button for users to return to the main page of the site. List out the details of the album that we entered through Atlas Content Modeler in WordPress: title, release date, cover image, and the list of songs.

  return (
    <Layout>
      <Link href="/">
        <p> &#x2190; View All Albums</p>
      </Link>
      <h1>{album.albumTitle}</h1>
      <p>Released on {album.releaseDate}</p>
      <img src={album.cover.mediaItemUrl} alt={album.cover.altText} />
      <h3>Track List</h3>
      <ol>
        {album.trackList().nodes?.map((song) => (
          <li key={song.databaseId}>
            <Link href={`/songs/${song.slug}`}>
              <a>{song.songTitle}</a>
            </Link>
          </li>
        ))}
      </ol>
    </Layout>
  );

Style the Single Album Page

Check out the site running in the browser and navigate to the album details page by clicking on one of the cover images. You can see all of the information that we want, but it’s not very pretty. Let’s add some styles to this page! Import the albums.modules.scss file that you previously created into this [albumSlug].tsx file:

import styles from "scss/pages/albums.module.scss";

Then, inside of the albums.module.scss file, add the following additional SCSS declarations:

.backButton {
  text-align: right;
  margin: 2% 5%;
  font-weight: bold;
}

.backButton:hover {
  text-decoration: underline;
}

.title {
  text-align: center;
}

.details {
  text-align: center;
}

.cover {
  display: block;
  margin: auto;
}

.trackList {
  text-align: center;
  text-decoration: none;
  list-style-type: none;
  padding-inline-start: 0;
}

.listItem a {
  text-decoration: none;
  color: black;
}

.listItem a:hover {
  font-weight: bold;
}

Use these SCSS classes and assign them to their respective elements in the [albumSlug].tsx file:

        <p className={styles.backButton}> &#x2190; View All Albums</p>
      ...
      <h1 className={styles.title}>{album.albumTitle}</h1>
      <p className={styles.details}>Released on {album.releaseDate}</p>
      <img
        className={styles.cover}
        src={album.cover.mediaItemUrl}
        alt={album.cover.altText}
      />
      <h3 className={styles.details}>Track List</h3>
      <ol className={styles.trackList}>
        ...
          <li className={styles.listItem} key={song.databaseId}>

Review the [albumSlug].tsx File

After all of these steps, your [albumSlug].tsx file should look like this:

import { client } from "client";
import { useRouter } from "next/router";
import { getNextStaticProps } from "@faustjs/next";
import Layout from "../../components/Layout";
import Link from "next/link";
import styles from "scss/pages/albums.module.scss";

export default function Album() {
  const { useQuery } = client;
  const { query = {} } = useRouter();
  const { albumSlug } = query;

  const album = useQuery().album({
    id: albumSlug,
    idType: "SLUG",
  });

  return (
    <Layout>
      <Link href="/">
        <p className={styles.backButton}> &#x2190; View All Albums</p>
      </Link>
      <h1 className={styles.title}>{album.albumTitle}</h1>
      <p className={styles.details}>Released on {album.releaseDate}</p>
      <img
        className={styles.cover}
        src={album.cover.mediaItemUrl}
        alt={album.cover.altText}
      />
      <h3 className={styles.details}>Track List</h3>
      <ol className={styles.trackList}>
        {album.trackList().nodes?.map((song) => (
          <li className={styles.listItem} key={song.databaseId}>
            <Link href={`/songs/${song.slug}`}>
              <a>{song.songTitle}</a>
            </Link>
          </li>
        ))}
      </ol>
    </Layout>
  );
}

export async function getStaticProps(context) {
  return getNextStaticProps(context, {
    Page: Album,
    client,
  });
}

export function getStaticPaths() {
  return {
    paths: [],
    fallback: "blocking",
  };
}

Build the Song Details Page

Creating the individual song details page will be very similar to creating the individual album details page.

Create [songSlug].tsx

First, let’s create a page to display an individual song’s details. Inside of the src/pages directory, create a new folder called songs with a new file inside of it called [songSlug].tsx.

To get the data we need, we need to import several things:

import { client } from "client";
import { useRouter } from "next/router";
import { getNextStaticProps } from "@faustjs/next";

Then, create a function called Song() with a blank return statement for now. Inside of this Song function, use the provided client, the Next.js Router component, and GQty’s useQuery() hook to query for the specific song’s page based on the URL.

export default function Song() {
  const { useQuery } = client;
  const { query = {} } = useRouter();
  const { songSlug } = query;

  const song = useQuery().song({
    id: songSlug,
    idType: "SLUG",
  });

  return ();
}

Add the getStaticProps and getStaticPaths functions to the end of your [songSlug].tsx file for Static Site Generation (SSG).

export async function getStaticProps(context) {
  return getNextStaticProps(context, {
    Page: Song,
    client,
  });
}

export function getStaticPaths() {
  return {
    paths: [],
    fallback: "blocking",
  };
}

Now that we have access to the individual song’s data, we’ll focus on the view for the page. Start with adding import statements for our Layout component and the Next.js Link component.

import Layout from "../../components/Layout";
import Link from "next/link";

Inside the return statement of the Song function, we will build the JSX elements. This will be very similar to what we built for the albumSlug page, but we will add a second button to return to the corresponding album’s details page and handle the genre taxonomy. List out the details of the song that we entered through Atlas Content Modeler in WordPress: title, length, genre, and lyrics.

  return (
    <Layout>
      <Link href="/">
        <p> &#x2190; View All Albums</p>
      </Link>
      {song.album().nodes.map((album) => (
        <Link key={album.databaseId} href={`/albums/${album.slug}`}>
          <p> &#x2190; View {album.albumTitle}</p>
        </Link>
      ))}
      <h1>{song.songTitle}</h1>
      <p>Song Length: {song.length}</p>
      <p>
        Genre:&nbsp;
        {song
          .genres()
          .nodes?.map((genre) => genre.name)
          .join(", ")}
      </p>
      <h3>Lyrics</h3>
      <div dangerouslySetInnerHTML={{ __html: song.lyrics }} />
    </Layout>
  );

Style the Song Details Page

Check out the site running in the browser and navigate to the song details page by clicking on one of the song titles from the album’s tracklist. You can see all of the information that we want, but it’s not very pretty. Let’s add some styles to this page! Create a new file in src/scss/pages called songs.module.scss and add the following SCSS declarations:

.backButton {
  text-align: right;
  margin: 2% 5%;
  font-weight: bold;
}

.backButton:hover {
  text-decoration: underline;
}

.title {
  text-align: center;
}

.details {
  text-align: center;
}

Use these SCSS classes and assign them to their respective elements in the [songSlug].tsx file:

        <p className={styles.backButton}> &#x2190; View All Albums</p>
      ...
          <p className={styles.backButton}> &#x2190; View {album.albumTitle}</p>
      ...
      <h1 className={styles.title}>{song.songTitle}</h1>
      <p className={styles.details}>Song Length: {song.length}</p>
      <p className={styles.details}>
      ...
      <h3 className={styles.details}>Lyrics</h3>
      <div
        className={styles.details}
        dangerouslySetInnerHTML={{ __html: song.lyrics }}
      />

Review the [songSlug].tsx File

After all of these steps, your [songSlug].tsx file should look like this:

import { client } from "client";
import { useRouter } from "next/router";
import { getNextStaticProps } from "@faustjs/next";
import Layout from "../../components/Layout";
import Link from "next/link";
import styles from "scss/pages/songs.module.scss";

export default function Song() {
  const { useQuery } = client;
  const { query = {} } = useRouter();
  const { songSlug } = query;

  const song = useQuery().song({
    id: songSlug,
    idType: "SLUG",
  });

  return (
    <Layout>
      <Link href="/">
        <p className={styles.backButton}> &#x2190; View All Albums</p>
      </Link>
      {song.album().nodes.map((album) => (
        <Link key={album.databaseId} href={`/albums/${album.slug}`}>
          <p className={styles.backButton}> &#x2190; View {album.albumTitle}</p>
        </Link>
      ))}
      <h1 className={styles.title}>{song.songTitle}</h1>
      <p className={styles.details}>Song Length: {song.length}</p>
      <p className={styles.details}>
        Genre:&nbsp;
        {song
          .genres()
          .nodes?.map((genre) => genre.name)
          .join(", ")}
      </p>
      <h3 className={styles.details}>Lyrics</h3>
      <div
        className={styles.details}
        dangerouslySetInnerHTML={{ __html: song.lyrics }}
      />
    </Layout>
  );
}

export async function getStaticProps(context) {
  return getNextStaticProps(context, {
    Page: Song,
    client,
  });
}

export function getStaticPaths() {
  return {
    paths: [],
    fallback: "blocking",
  };
}

Done! 🎉

You should now be able to navigate back and forth between the home page, the album details page, and the song details page. Don’t forget to update your ACM data as Taylor Swift releases her re-recordings. 😉

Congratulations on creating a headless WordPress site! Hopefully, you now have a good understanding of how you can leverage tools like Faust.js and ACM to build headless WordPress sites.

If for any reason you weren’t able to follow along with the steps outlined in this post, you can access the finished state of the example app at this Github repository.

Looking for a place to host your headless WordPress project? Check out WP Engine’s Atlas platform.