Google Web Stories + Next.js

Google Web Stories + Next.js

Integrating Google's Web Stories plugin for Wordpress as a Headless CMS structure, using Next.js as its front end.

Wordpress is one of the most used CMS ever. It's easy to use, considerably easy to develop and everybody knows it, so it's easy to find answers to whatever question you may have about it. However, using Wordpress as a Headless CMS is still a growing thought for many people, so there's a lot of unknown grounds to discover.

The biggest issue we face when using it to serve content through APIs is how to adapt the plugins for this purpose. Many plugins are already adopting Wordpress' native REST API, but even though it's necessary to understand how they are doing it first.

Web Stories

Google has developed a way to create your own Stories, outside Instagram, Facebook, etc. It's an AMP based page which behaves just as a regular story you would see in any of these networks. Using it, it's possible to share content in a more dinamic way and reach the young and techy generation.

If you use Wordpress you can easily add this feature to your website by simply installing Google's plugin for Web Stories. It is very easy to use: click on the editor, create your stories and publish them! Simple as that, the stories are there.

Now, what if I don't want to use Wordpress' views and use my own React based front end? Well, then I have some things to do to adapt it. This is what I did when I was asked to find a solution to integrate this plugin using Next.js.

Fetching the Stories

If you want to see your stories outside a Wordpress' environment, you gotta do it through an API, right? There are basically two ways to do it:

a) Using Wordpress REST API - register you own routes, apply some filters here and there, check for authentication, and so on. I won't be covering this way here, but it's pretty easy to figure out how to do it.

Or (my favorite):

b) Using GraphQL!

If you are already using your Wordpress in a headless model, you've probably chosen one of these two ways, so now you can stick to the one you've got first.

I like to use WPGraphQL plugin. So, if you want to do it my way, you can go ahead and install it. If you're not familiar with a GraphQL API, it works as a single endpoint (usually /graphql) where you send POST requests declaring the exact data you want from the database, in a super nice and friendly written query.

If you have both Web Stories and GraphQL plugins installed, you will notice that the stories are not registered, so you can't query them. So the first thing to do is to register the 'web-story' post type. You can do it with this simple function:

add_filter('register_post_type_args', function ($args, $post_type) {
   if ('web-story' === $post_type) {
    $args['show_in_graphql'] = true;
    $args['graphql_single_name'] = 'webStory';
    $args['graphql_plural_name'] = 'webStories';
  }
  return $args;
}, 10, 2);

There you go! Your stories are now registered to the type registry and you can query them as if they were a regular post type. You can play with it by going to the GraphiQL IDE which comes with the plugin, and try to fetch your stories (don't forget to create them first if you haven't so far xD)

A basic query would look something like this:

query GETALLWEBSTORIES {
  webStories(first: 10) {
    nodes {
      title
      slug
      featuredImage {
        node {
          mediaItemUrl
          altText
          description
        }
      }
    }
  }
}

With this query you will get the stories' titles, slugs and thumbnails. You're already able to list your stories the way you want in your Next.js front end. The listing is pretty much like any data fetching you would do (static props, server side, etc), it's up to you.

Read more about it here.

Rendering the Web Story

Now that you're all set on the back end, let's move on to your front end.

When I first tried to do it, I wanted to statically generate the stories. Unfortunately there's no way to do it, as the plugin gives you the HTML code for the entire page of the Web Story. You can try to clean this code with some RegExp, leaving only the code inside the <body> and injecting everything from <head> inside next/head component, and activating AMP on Next, but it's just too much work and there's great chances you will have issues to validate this story.

The only way I could find is to Server Side Generate the Web Story. You will need another query to get the individual story, which will be something like:

query GETWEBSTORYBYSLUG($id: ID!) {
  webStory(id: $id, idType: SLUG) {
    content
  }
}

As you only need the content of the page, the query is this short.

To query GraphQL data in Next.js I recommend using Apollo Client. I will probably write something about it, but for now if you want to know more you can go to Apollo's documentation.

Having Apollo set up, and your query defined, you can create your Web Story page, using server side props:

/pages/web-story/[slug].tsx

import { GetServerSideProps } from "next";

export const getServerSideProps: GetServerSideProps = async (context) => {
  if (context && context.res) {
    const { res, params } = context;
    const slug = params?.slug;

    // fetch your web story here 
    // const {data} = await api.query(...)

    if (!data.webStory.content) {
      return {
        notFound: true,
      };
    }

    res.setHeader("content-type", "text/html");
    res.setHeader("Cache-Control", "s-maxage=900, stale-while-revalidate=900");
    res.write(content);
    res.end();
  }

  return {
    props: {},
  };
};

const StoryPage = () => {};

export default StoryPage;

On this example, I am fetching the story by its slug. You can do it using its ID, no problem, but using the slug is better for your website's SEO.

After fetching the story's data, I check if it exists. If it doesn't, I return notFound = true, which will redirect to a 404 page (in case you have a custom 404 page this is necessary, otherwise it will go to Next's default page.

Going ahead, I set the headers. This part is very important, as you are rendering it server side, you have to set a cache control header. Unless you are planning on updating your story content repeatedly, you should avoid rendering it everytime you call this function, that's why you need to cache it. You can set the time in seconds, just like you do with a revalidation using getStaticProps.

Having done that, you just need to write the content to the response body! Done! You now have integrated Google Web Stories to yout Next.js website.

Possible problems you might have to solve

If you pay attention to the content code when you fetch it, you will see it's a bit "dirty". This might cause you some trouble. If it does, you might need to clean up this content before writing it to the response body. You can create a simple function with some RegExp to replace some useless things in the code:

const cleanStory(content: string) {
  let cleanContent = "";

  // remove all the line breaks

  cleanContent = content.replace(/\r?\n|\r/gs, "");

  // remove all the paragraphs wordpress might add

  cleanContent = cleanContent.replace(/<p>/gs, "");
  cleanContent = cleanContent.replace(/<\/p>/gs, "");

  // BONUS: replacing the canonical url to your front end url
  // For this you might need to add some extra params to the function
  // to write the new url here

  cleanContent = cleanContent.replace(
      /<link rel="canonical".*?[>]/g,
      "www.myfrontend.com/webstory/slug"
    );
}

That's it! I hope this helps you somehow! Feel free to contact me if you have any doubts! Maybe I can help you with it too!

Did you find this article valuable?

Support Ramon Metzker by becoming a sponsor. Any amount is appreciated!