Templates | Yext 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 Content 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 contains the TSX displayed on the screen

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 Content, and allows you to specify the entity type and fields your template requires.

light bulb
Want to learn more?
Streams are an important abstraction — for more, check out this guide .

The template below will create a page for every location entity type stored in the platform. The path of each page in production will be the slug field and the page itself will simply display the name, addres, 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 Content 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"]
    },
  },
};

// Path
/**
 * Defines the path that the generated file will live at for production.
 */
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;


Stream Configuration Properties

When configuring your stream, refer to the table below for the available fields.

Property Type Required Description
$id string Yes A human-readable ID used as the name of your stream. When your stream is instantiated within our system, we automatically append a unique ID to this ID. Thus, your-stream would become your-stream-[unique id].
filter object Yes Specifies what data from Content to include in the stream. Filter supports three sub-properties: entityTypes, entityIds, and savedFilterIds.
Note: when more than one filter sub-property is specified, the resulting document set will be the intersection of each filter.
See rows below for details on each property.
filter.entityTypes array An array of Entity Type IDs (e.g, "locations" or "ce_customEntityType"). The system will stream a document for every valid entity type specified.
Note: as a best practice, we recommend filtering by saved filter instead of by an entire entity type. Refer to the Saved Filters module for information on why they are useful.
filter.entityIds array Array of Entity IDs (e.g. "location-1"). The system will stream a record for each entity specified in this array.
filter.savedFilterIds array Array of Saved Filter IDs (e.g. "123456789"). The system will return a document for each entity that meets that saved filter’s criteria.
Refer to our Saved Filters module for more information on saved filters.
fields array Yes Array of field IDs. Use this array to define the set of fields you want captured in your Stream output documents. Include the field ID for each field you wish to include in this array. The field ID can be found at Content > Configuration > Entity Types > [Your Entity Type] > Fields.
localization object Yes Localization is used to fetch data from multiple language profiles. By default, the system will produce a Stream document for the primary language profile of the entities. To stream additional or specific language profiles, add these to the localization.
Refer to our multi-language reference article for more information.
localization.locales array Yes Array of locale codes; for each entity in your filter, the system will stream a document for each language profile specified in the locales array.
Refer to our multi-language reference article for more information.
transform object Allows you to transform certain data from Content on the server-side.
transform.replaceOptionValuesWithDisplayNames array For option-select fields in Yext Content, this transform returns the display name of your option values instead of the IDs.
We always recommend using this transform when returning any option-select fields in your fields array.
transform.expandOptionFields array For Option-select fields, this transform returns all possible options for that field, as opposed to only the selected option.
slugField string The field to use as the slug for dynamic dev mode.


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 Content as a Site entity type. Learn more about global data .

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.

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
Note
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 will not reflect any other changes to entities in Content. 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.

book
Note
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. TemplateProps interface definition here .

Return value:

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

    export const getPath: GetPath<TemplateProps> = ({ document }) => {
      return document.slug;
    };

In order to test your production paths during local development, it is required that you return the slug in your getPath function. Slug is a built-in field in Content that automatically converts text into a URL-safe string. Refer to the Paths and Slugs reference article for more information.

Do not prefix your return values from getPath with a forward slash. For example, if you want the page to live at your-domain.com/search just return search and not /search from your getPath export.

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 if transformProps is included.
  • If transformProps is not included in your file, the props are an instance of TemplateRenderProps with the document object as the direct output from the stream configuration. Interface definition for TemplateRenderProps here .
  • 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
Note
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.

We always recommend setting the viewport to device-width (as in the example below) to ensure mobile responsiveness.

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 which will redirect to the path created by getPath. You can think of this as a way to configure “aliases” for your getPath value.

export const getRedirects: GetRedirects<TemplateProps> = ({ document }) => {
  return [`${document.c_alias}`, `another-alias-${document.id}`];
};

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 generation 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-Yext platform) source. See example.

import {
  TransformProps,
  TemplateProps,
  // other imports from @yext/pages
} from "@yext/pages";
import { fetch } from "@yext/pages/util";

export const transformProps: TransformProps<TemplateProps> = async (data) => {
  const response = await fetch("https://dummyjson.com/products/1")
  const product = await response.json()
  return {
    ...data,
    document: { ...data.document, product },
  };
};

Return values:

  • Any valid Javascript object, which is then passed as props to the default export (template)
light bulb
Note
  • 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.
  • To utilize fetch(), you must include the following import statement in your code: import { fetch } from "@yext/pages/util"; this is a poly-filled version of the fetch API for PagesJS, and allows it to work server-side.

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.

slugField: string

If you want to use a different entity field than slug for generating your local development page paths, you will need to add the slugField to your config with a field specified in the stream.fields array. See this section in Paths & Slugs to learn more.


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.