DEV Community

Cover image for The first Strapi SEO plugin
Shada for Strapi

Posted on • Originally published at strapi.io

The first Strapi SEO plugin

First Strapi SEO plugin

It's been a long time since I've posted an article and I can say that I missed it! Today, I am very happy to announce the release of the first SEO plugin!

Happy girl GIF

Why?

It's true, why make an SEO plugin? I could have improved our FoodAdvisor demo application or updated our outdated tutorials on v4!

What am I doing GIF

More seriously, many of you ask how to properly manage your content so that it is SEO friendly. Indeed, Strapi does not generate views as a traditional CMS would. It is therefore difficult to have the feeling of having a direct impact on SEO. On Strapi, which is a Headless CMS, you only have access to the data. What concerns the front-end, i.e, the formatting which must please the search engine, is another distinct part that the content manager/editor does not have direct access to.

However, ensuring that your content is SEO friendly can and should be done upstream, during creation. It would therefore be interesting for you to be able to ensure that your content meets a few SEO criteria in order to rank on the SERP. Well, this is what this article is all about!

Disclaimer: This plugin has been developed internally but many other SEO plugins will emerge on our marketplace. It's up to you to use the one that best suits your needs. In any case, know that the ExFabrica dev team is currently working on another SEO plugin that works differently but can be combined with the one I'm about to present to you, which is just awesome!

Awesome GIF

How it works

The SEO plugin is divided into 2 parts:

  • Settings page This page will detect if you have a seo component in a shared category (shared.seo). For the v1 of this plugin, it was easier to define a classic and simple architecture in order to be able to reiterate more easily for a v2. The shared category means that it is a category of components containing components that can be shared among all the content-types of your application, which is the case of an SEO component.

If such a component is not detected, then it will automatically import it inside your Strapi project.

Otherwise, the plugin will list the Content-Types of your application with or without the SEO component. You will then be able to add your component for each Content-Type, being careful to name it: seo and include it at the first level (root) of your CT.

seo-6.png

  • Modal in Content Manager It will then be possible for you from the content manager to have an SEO menu that will appear on the right in the right-links section when you have started to fill in your SEO component.

IMAGE

This menu contains 4 things:

  • Browser preview button

This will open a modal containing a SERP preview of your content based on the metaTitle and metaDescription fields. You can see the web and mobile preview :)

seo-4.png

  • Social preview button

This will open a modal containing a Facebook or Twitter preview card based on your metaSocial components in your content.

seo-3.png

  • A Summary of your SEO results

This allows you to quickly take a look at the results of SEO checks on your content. Note that you have to click on "SEE DETAILS" to update it.

seo-summary

  • SEO Checks

The SEE DETAILS link button will open a modal containing all the SEO checks on your content. You'll be able to see what you can improve or fix for your content to be SEO friendly as it should be!

seo-1.png
seo-2.png

How was it created?

This part is for curious developers who wish to know how the plugin was developed. Before you start, know that you can find the source code on the official GitHub repository as well as its npm package page. I won't detail each file of the plugin but I want to introduce the basics of creating a simple plugin so that you can more easily create your own!

I redirect you to the documentation to learn more about the basics of plugin development.

It all starts with the following command:

strapi generate
Enter fullscreen mode Exit fullscreen mode

It will run a fully interactive CLI to generate APIs, controllers, content-types, plugins, policies, middlewares and services.

What interests us here is the creation of a plugin! Simply choose the name, and activate the plugin in the ./config/plugins.js file of your Strapi application:

module.exports = {
  // ...
  seo: {
    enabled: true,
    resolve: "./src/plugins/seo", // Folder of your plugin
  },
  // ...
};
Enter fullscreen mode Exit fullscreen mode

Server (back-end)

I needed for this plugin to have in the front some information from the back-end of Strapi. To do this, you just need to create routes in the back-end part of your plugin that will use controllers and services to fetch this information.

So I defined the following routes for the SEO plugin:

// ./server/routes/index.js

module.exports = [
  {
    method: "GET",
    path: "/component",
    handler: "seo.findSeoComponent",
    config: {
      auth: false,
      policies: [],
    },
  },
  {
    method: "POST",
    path: "/component",
    handler: "seo.createSeoComponent",
    config: {
      auth: false,
      policies: [],
    },
  },
  {
    method: "GET",
    path: "/content-types",
    handler: "seo.findContentTypes",
    config: {
      auth: false,
      policies: [],
    },
  },
];
Enter fullscreen mode Exit fullscreen mode

Let's look in detail at the first route. The handler is the findComponent action of the seo controller:

// ./server/controllers/index.js
const seo = require("./seo");

module.exports = {
  seo,
};
Enter fullscreen mode Exit fullscreen mode
// ./server/controllers/seo.js
module.exports = {
  // ...
  findSeoComponent(ctx) {
    ctx.body = strapi.plugin('seo').service('seo').getSeoComponent();
  },
  // ...
Enter fullscreen mode Exit fullscreen mode

This action directly uses a function present in the seo service:

// ./server/services/index.js
const seo = require("./seo");

module.exports = {
  seo,
};
Enter fullscreen mode Exit fullscreen mode
// ./server/services/seo.js
module.exports = ({ strapi }) => ({
  // ...
  getSeoComponent() {
    const seoComponent = strapi.components['shared.seo'];
    return seoComponent
      ? { attributes: seoComponent.attributes, category: seoComponent.category }
      : null;
  },
  // ...
}
Enter fullscreen mode Exit fullscreen mode

This service allows me to have access to the strapi object containing a lot of information about my project, such as whether the shared.seo component exists in my whole project.

Once the back-end is ready, all I have to do in the front-end (./admin/src/...) of the plugin is to call this route to get the desired information.

// ./admin/src/utils/api.js
// ...
const fetchSeoComponent = async () => {
  try {
    const data = await request(`/seo/component`, { method: "GET" });
    return data;
  } catch (error) {
    return null;
  }
};
// ...
Enter fullscreen mode Exit fullscreen mode

Voila, this is how I can get information about my Strapi application in the front-end of my plugin! Simple isn't it?

Learn more about plugin development on our v4 documentation

Admin (front-end)

The admin panel is a React application that can embed other React applications. These other React applications are the admin parts of each Strapi plugin. As for the front-end, you must first start with the entry point: ./admin/src/index.js.

This file will allow you to define more or less the behavior of your plugin. We can see several things:

register(app) {
    app.addMenuLink({
      to: `/plugins/${pluginId}`,
      icon: PluginIcon,
      intlLabel: {
        id: `${pluginId}.plugin.name`,
        defaultMessage: 'SEO',
      },
      Component: async () => {
        const component = await import('./pages/App');

        return component;
      },
    });
    app.registerPlugin({
      id: pluginId,
      initializer: Initializer,
      isReady: false,
      name,
    });
  },
Enter fullscreen mode Exit fullscreen mode

First of all, there is a register function. This function is called to load the plugin, even before the app is actually bootstrapped. It takes the running Strapi application as an argument (app).

Here it tells the admin to display a link in the Strapi menu for the plugin with a certain Icon, name, etc...

Then we find the bootstrap function:

bootstrap(app) {
    app.injectContentManagerComponent('editView', 'right-links', {
      name: 'SeoChecker',
      Component: SeoChecker,
    });
  },
Enter fullscreen mode Exit fullscreen mode

This will expose the bootstrap function, executed after all the plugins are registered. Here we inject into the content manager a component that I created: SeoChecker. This component contains the button opening the modal containing all the SEO checks in the content manager. I let you look at the code for more details.

Also, I redirect you to our documentation concerning the injection zones API.

Note: Know that it is possible to customize the admin using the injection zones API without having to generate a plugin. To do this, simply use the bootstrap function in your ./src/admin/app.js file of your Strapi project to inject the components you want.

This is what was done on our demo FoodAdvisor, I redirect you to this file.

Back to our plugin!

The last part reffers to the translation management of your plugin:

async registerTrads({ locales }) {
    const importedTrads = await Promise.all(
      locales.map((locale) => {
        return import(`./translations/${locale}.json`)
          .then(({ default: data }) => {
            return {
              data: prefixPluginTranslations(data, pluginId),
              locale,
            };
          })
          .catch(() => {
            return {
              data: {},
              locale,
            };
          });
      })
    );

    return Promise.resolve(importedTrads);
  },
Enter fullscreen mode Exit fullscreen mode

You will be able in the ./admin/src/translations folder to add the translations you want. This plugin has been translated into French only for now. Feel free to add any other translations :)

For the rest, it is very simple React code, the beginning is in the file ./admin/src/pages/HomePage. This file will contain the front-end of the plugin's Settings page. Then the part located in the content manager is managed by the SeoChecker component.

I don't think it's useful in this article to go deeper into the code. If you are more curious then I leave you the freedom to look at the source code of the plugin and do not hesitate to give any feedback or to directly contribute to the plugin, you are more than welcome!

See you later!

Oldest comments (4)

Collapse
 
getseowebsite profile image
Getseowebsite

I am new to this plugin, but I will try it on my blog site. Congrats on your achievement!

Collapse
 
olayinkade profile image
olayinkade

I went through your code base and saw how you seed data is this the convention for strapi v4. I was looking through v3 and a lot of example used the bootstrap function exposed that runs on start

Collapse
 
gamerseo profile image
Gamerseo

Very interesting plug.

Collapse
 
lastreaction22 profile image
lastreaction

That's great news I am working with converted agency and we'll try this