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:
- Imports
- Named exports
- 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.
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;
Exports
Templates are structured with multiple exports that specify data inputs and configuration.
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 fromtransformProps
iftransformProps
is included. - If
transformProps
is not included in your file, the props are an instance ofTemplateRenderProps
with the document object as the direct output from the stream configuration. Interface definition forTemplateRenderProps
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.
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 thehead
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 profilesprimary
- 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)
- 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
.
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.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.