SEO Best Practices | Yext Hitchhikers Platform

Overview

SEO is one of the most important features of a successful web presence, and Pages enables you to easily build a highly performant site that will succeed in search. Yext has loads of resources about SEO, so if you want more general information, check out this module .

This reference doc will explain the key features that Pages offers for developing with top-notch SEO.


SEO Checklist

This checklist is meant to serve as a starting point to ensure all of your pages satisfy these basic SEO criteria:

  • h1 tag
    • Each page has ONE relevant h1 tag
    • The h1 tag should normally be the title of your page
  • Meta tags / OG tags / title tags (see below for Pages configuration)
    • Tags are present with no spelling errors (see below for detailed info)
    • OG tags are included where appropriate to enhance social media presence (more info here )
  • Canonical URL
    • Use of canonical urls where appropriate - learn more here
  • Schema (see below for Pages configuration)


Head Configuration and Meta Tags

Yext makes it easy to power HTML <head> tags across your website with dynamic content from your CMS.

The <head> element contains metadata about your web page, such as the title, character set, styles, scripts, and CSS style sheets. Metadata provides browsers and search engines with technical information about the web page, and is a crucial component of your SEO strategy.

Pages offers two entry-points in your repo for <head> configuration:

  1. _server.tsx

    The _server.tsx file allows you to configure how your templates will be rendered on the server (refer to the Client-Server Templates reference doc for more information). This can be used to create a global <head> tag, which will be used by all templates in your project.

    The example below shows how to add a favicon to the <head> tag, as well as pull data from a _site object to power a script tag.

    import * as ReactDOMServer from "react-dom/server";
    import * as React from "react";
    import { PageContext } from "@yext/pages";
    import Favicon from "../assets/images/yext-favicon.ico";
        
    export { render };
        
    const render = async (pageContext: PageContext<any>) => {
      const { Page, pageProps } = pageContext;
      const viewHtml = ReactDOMServer.renderToString(<Page {...pageProps} />);
        
      const customField = pageProps.document._site.c_customField;
        
      return `<!DOCTYPE html>
        <html lang="<!--app-lang-->">
          <head>
            <link rel="icon" type="image/x-icon" href=${Favicon}>
            <script>
              thirdPartyAnalytics(${customField})
            </script>
          </head>
          <body>
            <div id="reactele">${viewHtml}</div>
          </body>
        </html>`;
    };
  2. getHeadConfig export

    The getHeadConfig export can be used to append custom configuration on a per-template basis. For each template, PagesJS will inject the contents of getHeadConfig at the top of the <head> configuration in _server.tsx.

    The example below provides a simple meta tag with a title and description, Open Graph tags for image and URL, and canonical URL.

    /**
     * This allows the user to define a function which will take in their template
     * data and produce a HeadConfig object. When the site is generated, the HeadConfig
     * will be used to generate the inner contents of the HTML document's <head> tag.
     * This can include the title, meta tags, script tags, etc.
     */
    export const getHeadConfig: GetHeadConfig<TemplateRenderProps> = ({
      document,
    }): HeadConfig => {
      return {
        title: document.name, // Page Title
        charset: "UTF-8",
        viewport: "width=device-width, initial-scale=1",
        tags: [
          {
            type: "meta", // Meta Tag (Description)
            attributes: {
              name: "description",
              description: "This site was generated by the Yext SSG",
            },
          },
          {
            type: "meta", // Open Graph: An image URL which should represent your object within the graph
            attributes: {
              name: "og:image",
              description: "https://images.google.com/",
            },
          },
          {
            type: "meta", // Open Graph: The canonical URL of your object that will be used as its permanent ID in the graph
            attributes: {
              property: "og:url",
              content: xxxxxxx,
            },
          },
          {
            type: "link",
            attributes: {
              rel: "canonical",
              href: document.,
            },
          }
        ],
      };
    };

Schema

Schema helps search engines more effectively understand your page content and provide rich search results. Refer to our schema module for a full overview of the benefits.

Using the streams data architecture, it is possible to generate dynamic schema objects without any client-side logic! The example below gives an example of a simple schema in json/ld format. Check out the schema configuration in the default export below for a simple example.

light bulb
Note
Always test your schema changes locally using the Schema.org validator . This tool allows you to verify whether or not you’ve generated a valid schema object. During local development, you can build your site and then copy the HTML for a given page from the sites-rendered-output into the schema validator.

Note a few things about the example:

  1. It uses two imports : react-schemaorg and schema-dts, which make formatting your schema in json/ld very simple.
  2. The JsonLd object should be included in the TSX returned from your default export.
  3. The various address fields will be populated at build-time by the address object which is returned by the streams.

    import {
      GetHeadConfig,
      GetPath,
      HeadConfig,
      Template,
      TemplateConfig,
      TemplateProps,
      TemplateRenderProps,
    } from "@yext/pages";
    import * as React from "react";
    import { JsonLd } from "react-schemaorg";
    import { Dentist } from "schema-dts";
    
    export const config: TemplateConfig = {
      stream: {
        $id: "index-stream",
        filter: {
          entityIds: ["location"],
        },
        fields: [
          "name",
          "address",
        ],
        localization: {
          locales: ["en"]
        },
      },
    };
    
    export const getPath: GetPath<TemplateProps> = ({ document }) => {
      return `index.html`;
    };
    
    const Index: Template<TemplateRenderProps> = ({
      document,
    }) => {
      const {
        name,
        address,
      } = document;
    
      return (
        <>
          // Start Configuration of Schema 
          <body>
            <JsonLd<Dentist> 
              item={{
                "@context": "https://schema.org",
                "@type": "Dentist",
                name,
                address: {
                  "@type": "PostalAddress",
                  streetAddress: address.line1,
                  addressLocality: address.city,
                  addressRegion: address.region,
                  postalCode: address.postalCode,
                  addressCountry: address.countryCode,
                },
              }}
            />
          // End Configuration of Schema
            <h1>A dentist site called {name} </h1>
          </body>
        </>
      );
    };
    
    export default Index;