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).
There are two types of templates you can create:
- Entity templates (aka streams templates)
- Static page templates
Streams Templates
Entity templates allow you to generate pages based on entities in your Knowledge Graph, all with the same underlying structure and styling. Each streams template is powered by a stream, which you can think of as the vehicle that delivers Knowledge Graph data to Pages in the form of JSON documents.
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 the Knowledge Graph, 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 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 the Knowledge Graph 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 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 => {
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;
The filter field allows you to specify which entity types you want to pull from the Knowledge Graph. It is most common to filter based on entity type, but you can also use saved filters or filter on specific fields.
The fields array is where you specify which fields from the chosen entity type will be made available to your template. All field names listed in that array will be passed to the default export as props on the document object — e.g., props.document.address or props.document.name. To reference fields across related entities, use dot notation: "c_relatedProducts.name", "c_relatedProducts.price".
Streams-powered pages are simple but powerful. By specifying the entity type and desired fields in config, you can generate hundreds or thousands of pages, each with a uniform structure but supplied with entity-driven data from the Knowledge Graph.
Typically, you will create a separate stream for each entity type to programmatically generate the relevant set of pages. Each stream will use its own template, tailored to the data in that stream.

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 the Knowledge Graph 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. |
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 more information. |
|
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. |
|
fields |
array | Yes | Array of field IDs. Use this array to define the set of fields you want captured in your stream output documents. The field ID can be found at Knowledge Graph > 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. 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. |
transform |
object | Allows you to transform certain data from the Knowledge Graph on the server side. | |
transform.replaceOptionValuesWithDisplayNames |
array | For option-select fields in the Yext Knowledge Graph, 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
Static pages are one-off pages which are not powered by streams, so they will not generate multiple entity-driven pages per template. You can update them like any web page using HTML, CSS, JS, and React.
Static templates can make use of global data, which lives at document._site. Global data allows you to pass information across every page on your site, and is normally managed in the Knowledge Graph 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;
Note: The key difference between static and streams 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 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. Check out the interface and type definitions on the PagesJS Github repo.
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.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 the Knowledge Graph 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.
- The props passed into
mainTemplateare the return value fromtransformPropsiftransformPropsis included. - If
transformPropsis not included, the props are an instance ofTemplateRenderPropswith the document object as the direct output from the stream configuration. - You can use different templating frameworks to stipulate the structure of your page. The example below uses React.
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.
Note: While
getHeadConfigis 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 theviewportto 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 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.-
$id- A unique identifier for the stream. This can be used to reference a single stream on multiple templates. -
fields- The fields to include on the template. To reference fields across related entities, 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",
fields: [
"id",
"uid",
"meta",
"name",
"address",
"c_featuredFAQs.question",
"c_featuredFAQs.answer"
],
filter: {
entityTypes: ["location"],
},
localization: {
locales: ["en"],
primary: true,
},
},
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 used to alter or augment props passed into the template at render time. This function runs 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, or to make computationally expensive formatting calls that you don't want running at page load time.
import {
TransformProps,
TemplateProps,
} 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).
Note:
- This function is async to permit data fetching from external APIs. 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
transformPropsin order for it to make it to the template.- To utilize
fetch(), you must include the following import statement:import { fetch } from "@yext/pages/util"— this is a poly-filled version of the fetch API for PagesJS that 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.
Recommended use cases:
-
Computationally expensive formatting - If you want to run a function that is expensive but only depends on data,
transformPropsis a great place to do that. This will only run on generation so it won't impact page load. - Enriching content with an API - If you want to enrich content via a third-party API, this is a great place to do it assuming the API response doesn't change over time (e.g., automatically adjusting the saturation of an image via an API).
slugField: string
If you want to use a different entity field than slug for generating your local development page paths, add the slugField to your config with a field specified in the stream.fields array. See Paths & Slugs for more information.
Customizing Templates
Since streams-generated page templates create pages for entities from the Yext Knowledge Graph, you'll need to update both your Knowledge Graph and the template in your repo when adding new content.
Customize Streams Templates
1. Add Content to the Knowledge Graph
A. Configure the Knowledge Graph
If the content you want to use isn't already stored in the Knowledge Graph, add a custom field to store it. For example, to add a catering phone number, you might configure:
- Field Type = number
- Field Name = "Catering Phone Number" (API name will auto-fill as
c_cateringPhoneNumber) - Field Availability = location
Note: If you want to create a field that stores related entities (for example, menu items sold at each location) and that entity type doesn't exist yet, you'll want to create a custom entity type before adding the field.
B. Fill in the Content
Once you have the field, fill in the content for each entity. You've got a few options for updating content in the Knowledge Graph. We suggest uploading a spreadsheet. You can also navigate to Knowledge Graph > Entities, filter for the relevant entity type, then click into individual entities to edit or edit in bulk.
2. Update the Stream
A. Add the Field to the Stream
In your repo, navigate to your template file and add the API name of the new field to the fields array in the stream config:
export const config: TemplateConfig = {
stream: {
$id: "my-stream-id-1",
fields: [
// ... existing fields ...
"c_cateringPhoneNumber"
],
filter: {
entityTypes: ["location"],
},
localization: {
locales: ["en"]
},
},
};
B. Pull Stream Changes into Local Data
- Navigate to your
/localDatafolder and open one of the stream JSON files. Notice it contains strictly the fields specified in your stream. - Kill your previous local dev server (CTRL + C) and run
npm run devto rebuild yourfeatures.jsonwith the new field. Your local data files will regenerate to account for this change.
You should now see the new field pulled directly into the document from your Knowledge Graph.
C. Other Stream Customizations
-
filter- Filters by entity type by default, but you can also use saved filters or filter on specific fields (e.g., a field indicating whether an entity should have a landing page). -
localization- By default, pages are generated only for theen(English) entity language profile. Update or add language profiles in thelocalizationfield to generate pages in other language profiles.
3. Update the Template
Once a field is added to the fields array, you have access to it in the default export via props.document. A simple example:
const Location: Template<TemplateRenderProps> = ({ document }) => {
const { c_cateringPhoneNumber } = document;
return (
<>
<h1>Catering Phone Number {c_cateringPhoneNumber}</h1>
</>
);
};
4. Preview Your Updated Template
To start your local dev server and preview changes, run:
npm run dev
This will spin up your site at localhost:5173. Navigate to the relevant pages to verify the new content is appearing correctly.
Customize Static Pages
Static pages can be updated like any web page using HTML, CSS, JS, and React. Navigate to the template file (e.g., src/templates/static.tsx) and modify the JSX to your needs.
Note that if the template pulls in data from an external API via transformProps, that call occurs at build time, not when the page is visited in the browser. Experiment with pulling in external data, bearing this in mind.
While Yext Pages is primarily used to create landing pages from entities in the Knowledge Graph, most websites also have a handful of static pages such as index or about pages.
Customize Components
Templates typically use custom components defined in the ./src/components folder. It's best practice to create custom components for content that is repeated across your site, like headers and footers.
If you prefer a different directory structure, you can put components wherever you like — with two rules:
- All custom files or directories must live under the
srcdirectory if you want to import them into your templates. - Any custom files or directories that are not templates must not live in
templates.