Templates| Hitchhikers Platform

Overview

Templates are the key building blocks of Pages. They can be used to build a single page (static template) or can be connected to a stream of entities from the Knowledge Graph to create a page for each entity (streams template).


Streams Templates

Every template has three parts:

  1. Imports
  2. Named exports
  3. A required default export, which specifies the structure of your template

Below is an example of a simple template hooked up to a stream of entities. You can tell this is a streams template because the config export contains a top-level field, called stream. Including stream in the config connects the template to the Knowledge Graph, and allows you to specify the entity type and fields your template requires.

light bulb
Tip
Streams are an important abstraction, so if you want to learn more, check out this Hitchhikers guide.

The template below will create a page for every location entity type stored in your Knowledge Graph. The path of each page in production will be the slug field and the page itself will simply display the name, address and phone.

/*
* Part 1. Imports
*/

import * as React from "react";
import {
  Template,
  GetPath,
  TemplateConfig,
  TemplateProps,
  TemplateRenderProps,
  GetHeadConfig,
  HeadConfig,
} from "@yext/pages";

/*
* Part 2. Named Exports
*/

// Config
/**
 * Required when Knowledge Graph data is used for a template.
 */
export const config: TemplateConfig = {
  stream: { 
    $id: "locations",
    // Specifies the exact data that each generated document will contain. This data is passed in
    // directly as props to the default exported function.
    fields: [
      "id",      
      "name",
      "address",
      "mainPhone",      
      "description",      
      "slug",   
    ],
    // Defines the scope of entities that qualify for this stream.
    filter: {
      entityTypes: ["location"],
    },
    // The entity language profiles that documents will be generated for.
    localization: {
      locales: ["en"],
      primary: false,
    },
  },
};

// Path
/**
 * Defines the path that the generated file will live at for production.
 *
 * NOTE: This currently has no impact on the local dev path. Local dev urls currently
 * take on the form: featureName/entityId
 */
export const getPath: GetPath<TemplateProps> = ({ document }) => {
  return document.slug;
};

// Head
/**
 * This allows the user to define a function which will take in their template
 * data and procude 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 => {
  const { name, description } = document;
  return {
    title: name,
    charset: "UTF-8",
    viewport: "width=device-width, initial-scale=1",
    tags: [
      {
        type: "meta",
        attributes: {
          description
        },
      },
    ],
  };
};

/*
* Part 3. The Template (Default Export)
*/

// Template
/**
 * This is the main template. It can have any name as long as it's the default export.
 * The props passed in here are the direct result from `transformProps`. 
 */
const LocationTemplate: Template<TemplateRenderProps> = ({
  document
}) => {
  const { name, address, mainPhone } = document;

  return (
    <>
      <div>{name}</div>
      <div>{address.line1}</div>
      <div>{mainPhone}</div>
    </>
  );
};

export default LocationTemplate;


Static Templates

You don’t have to hook up a template to a stream. You can also use a template to build a single page on the website.

Static templates can make use of global data, which lives at document._site. Global data allows you to pass information across every page across your site, and is normally managed in the Knowledge Graph as a Site entity type.

The example below is a static template that uses global data: _site.c_logo , so any time a change is made to the site’s logo, all pages will reflect the update.

Learn more about global data.

import * as React from "react";
import {
  Template,
  GetPath,
  TemplateProps, 
  TemplateRenderProps
} from "@yext/pages";

export const getPath: GetPath<TemplateProps> = () => {
  return `about`;
};

const About: Template<TemplateRenderProps> = ({ document }) => {
  const {
    _site,
  } = document;

  return (
    <>
      <div>
        <img>
          {_site.c_logo}
        </img>
      </div>
      <div>
        <h1>About Page</h1>
      </div>
    </>
  );
};

export default About;
light bulb
Tip
The key difference between static and dynamic templates is that a static template doesn’t include a stream configuration. If you are using global data, the static template will reflect changes to global data, but a static template will not reflect any other changes to entities in the Knowledge Graph. Put simply, static templates only reflect changes in global data, whereas streams templates reflect changes in entities and global data.


Exports

Templates are structured with multiple exports that specify data inputs and configuration.

light bulb
Tip
Check out the interface and type definitions on the PagesJS Github repo. Understanding these interfaces will help you understand the type arguments used below.

getPath: GetPath

required

getPath is a required named export that defines the path that the generated file will live at in production.

Return value:

  • A string that defines the path for the page in production

    export const getPath: GetPath<TemplateProps> = ({ document }) => {
      return `${document.id.toString()}`;
    };

mainTemplate: Template

*required as default export*

mainTemplate is the required default export, where the page structure is configured in a templating framework of your choice. This function can be named arbitrarily, as long as it is the default export.

Note:

  • The props passed into the mainTemplate are the return value from transformProps.
  • You can use different templating frameworks to stipulate the structure of your page. The example below uses React.

    // Template
    /**
     * This is the main template. It can have any name as long as it's the default export.
     * The props passed in here are the direct result from `transformProps`. 
    */
    const LocationTemplate: Template<TemplateRenderProps> = ({
      document
    }) => {
      const { name, address, mainPhone } = document;
    
      return (
        <>
          <div>{name}</div>
          <div>{address.line1}</div>
          <div>{mainPhone}</div>
        </>
      );
    };
    
    export default LocationTemplate;

getHeadConfig: GetHeadConfig

optional

getHeadConfig allows you to specify data that will be used to generate the inner contents of the HTML document’s <head> tag. This is the place to configure meta tags and other information relevant to SEO.

light bulb
Tip
While getHeadConfig is optional, it’s highly recommended for SEO best practices. At a minimum, you should populate the title and the meta description of the page.

Return value:

  • An object of type HeadConfig with top-level fields corresponding to tags included in the head section of an HTML document, e.g. title, tags, charset. Interface definition here.

    export const getHeadConfig: GetHeadConfig<TemplateRenderProps> = ({ document }): HeadConfig => {
      return {
        title: document.name,
        charset: "UTF-8",
        viewport: "width=device-width, initial-scale=1",
        tags: [
          {
            type: "meta",
            attributes: {
              name: "description",
              content: "Clever SEO Description",
            },
          },
          {
            type: "meta",
            attributes: {
              name: "og:image",
              content: document.logo.image.url,
            },
          },
        ],
      };
    };

config: TemplateConfig

optional — interface definition here.

config is an object of type TemplateConfig which is used for configuring streams and overriding the feature name. For streams-powered templates, the configuration of the desired stream is specified in this object.

  • stream - Stream configuration for the template. Stream interface definition is here.
    • $id - A unique identifier for the stream. This can be used to reference a single stream on multiple templates.
    • fields - These are the fields to include on the template. To reference fields across related entities, you should use dot notation - e.g. relationship.name
    • filter - You generally want to filter your stream to a single entity type but you can also include multiple entity types.
    • localization - The entity language profiles that documents will be generated for.
      • locales - An array of language profiles
      • primary - If set to true, only the primary profiles of the entities will be streamed
  • name - The name of the feature. If not set, the name of this file will be used (without extension). Use this when you need to override the feature name.

    export const config: TemplateConfig = {
      stream: {
        $id: "locations",
        // Specifies the exact data that each generated document will contain. This data is passed in
        // directly as props to the default exported function.
        fields: [
          "id",
          "uid",
          "meta",
          "name",
          "address",
          "c_featuredFAQs.question",
          "c_featuredFAQs.answer"
        ],
        // Defines the scope of entities that qualify for this stream.
        filter: {
          entityTypes: ["location"],
        },
        // The entity language profiles that documents will be generated for.
        localization: {
          locales: ["en"],
          primary: true,
        },
      },
        // The name of the feature. If not set the name of this file will be used (without extension).
      // Use this when you need to override the feature name.
      name: "template name"
    };

getRedirects: GetRedirects

optional

getRedirects defines a list of paths that will redirect to the path created by getPath

export const getRedirects: GetRedirects<TemplateProps> = ({ document }) => {
  return [`take-me-to-getPath`, `go-fast-{document.name}`];
};

Learn more about redirects.

transformProps: TransformProps

optional

transformProps is an async function that is used to alter or augment props passed into the template at render time. This function is run during generations and its return value is passed directly as props to the default exported function.

This function can be used to retrieve data from an external (non-Knowledge Graph) source. See example.

export const transformProps: TransformProps<TemplateProps> = async (
  data
) => {
  const url = "https:/some-api/posts/2";
  const dataFromExternalAPI = (await fetch(url).then((res: any) =>
    res.json()
  )) as ExternalImage;
  return { ...data, dataFromExternalAPI };
};

Return values:

  • Any valid Javascript object, which is then passed as props to the default export (template)
light bulb
Gotcha Alert
  • This function is async to permit data fetching from external APIs. Note: if this function is slow, it will slow down your page generation time.
  • This function runs in Deno, so ensure it is installed and configured.
  • This function will only run on generation and will not run each time the page loads.
  • You must return the data that is passed into transformProps in order for it to make it to the template.

You can think of transformProps like a piece of middleware. It accepts some data (the props being passed around) and can pass that data to the default export after any modifications. But remember, it will not automatically pass the data through. You must include it in your return object to reach the template.


Use Cases

Here are some recommended ways to use transformProps.

  1. Computationally expensive formatting

    If you want to run some function that is expensive but only depends on data then transformProps is a great place to do that. This will only run on generation so it won’t impact page load.

  2. Enriching Content with API

    If you want to enrich content via a third-party API, this function is a great place to do it assuming that the API response doesn’t change over time. For example, if you wanted to hit an API to automatically adjust the saturation of an image you could do it in this step.

Feedback