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:
- Imports
- Named Exports
- 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.
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 here.
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<TemplateProps>
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<TemplateRenderProps>
*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
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<TemplateRenderProps>
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 pop
ulate 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 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 stream-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<TemplateProps>
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}`];
};
You can learn more about redirects here.
transformProps: TransformProps<TemplateProps>
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)
- 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
.
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 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.