Website "Fleet" Management | Yext Hitchhikers Platform
Introduction
The Yext Platform makes it easy to manage thousands of websites (βfleetsβ) at scale. There are several key benefits to using Yextβs fleet management capabilities:
- Code Reusability: Instead of powering each website with its own set of code, an entire fleet can be powered by a single underlying GitHub repository.
- Configurability: Each website in your fleet can be customized based on inputs from the Content, to control aspects such as styling, font families, images, and the ordering of components on a page. This affords non-technical users with the ability to customize each website without touching the underlying code. If more advanced configuration is necessary, developers can easily implement customizations on a per-site basis.
- Programmatic Control: Yext sits on top of a robust suite of consumer-grade APIs, webhooks systems, and data monitoring tools, making it easy to deploy and monitor your fleetβwhether youβre working across one or multiple Yext accounts.
- Data Synchronization - Each website is powered by data stored in Yext Content, and is kept up-to-date with extremely high throughput . When used in combination with Yext Listings , your customersβ data is uniformly synchronized across the entire digital ecosystem.
Configuration Approaches
One of the main benefits of fleet management is code reusability, i.e. using a single code repository to power thousands of sites.
At the same time, each site in the fleet needs to be easily customizable; whether for a content editor using the Content, or a developer.
Yext recommends two approaches to facilitate customization, depending on your use case:
- Configuration through Environment Variables (Recommended)
- Recommended when all sites share the same underlying structure; best for light customization
- Configuration through Subfolders
- Recommended when specific sites need to deviate from the standard structure; best for heavy customization
Approach 1: Environment Variables
This approach relies on the use of environment variables. As such, please familiarize yourself with the Environment Variables reference article before diving in.
The idea behind this approach is to parameterize parts of the template code (specifically within stream configurations) such that anything site-specific can be passed to the Pages system at build time, rather than be stored directly in the code as hard-coded values.
Configuring Streams
Environment variables can be used to pass site-specific scopes to each stream configuration in your templates (read the Templates reference article for a refresher on stream templates). For example, refer to the following code example:
export const config: TemplateConfig = {
stream: {
$id: "location-stream",
filter: {
savedFilterIds: [YEXT_PUBLIC_LOCATIONS_SAVED_FILTER],
},
fields: [
"id",
"uid",
"meta",
"name",
"address",
"mainPhone"
],
localization: {
locales: ["en"]
}
},
};
In this example, environment variables allow you to pass different saved filter IDs to your template code at build time. This allows you to create multiple sites based on the same underlying repo, while dynamically passing different sets of Content to the templates.
Configuring Styling
Depending on the complexity of your use case, styling data can be stored directly in the Content and made available to your Pages via the site-stream.json
file (refer to our
Global Data
document for more details).
To summarize, a site stream can be used to βstreamβ the data from a single entity in your platform to all pages in your site; this data will be made available to each pageβs JSON document under an object called _site
.
An environment variable can be used to parameterize the specific entity ID that is passed to the site stream configuration. Refer to the example below:
{
"$id": "site-entity",
"filter": {
"entityIds": ["YEXT_PUBLIC_SITE_ENTITY_ID"]
},
"fields": [
"name",
"logo",
"c_styling"
],
"localization": {
"locales": ["en"]
}
}
site-stream.json
specifically, the environment variable reference must contain double quotes.
Per the configuration above, two fields called logo
and c_styling
(custom field) will be streamed globally to the site. In the Content, this field could look something like this:
The returned _site
object would look something like this:
"_site": {
"name": "Turtlehead Tacos Site Entity",
"logo": {
"image": {
"height": 150,
"url": "https://a.mktgcdn.com/p-sandbox/8esDUBrhKJnkaVztLihLsC3quv_5BjLFG9L6MJ0adcs/150x150.png",
"width": 150
}
},
"c_styling": {
"fontFamily": "\"Gill Sans Extrabold\", sans-serif;",
"primaryColor": "#FFA31F",
"secondaryColor": "#8946FF"
}
},
The data from the _site
object is then easily accessible for use at the template level.
For example, in the code snippet below, the primary and secondary colors from the
_site.c_styling
object can be be added to the:root
as global CSS variables.import * as React from "react"; import Header from "./Header"; import Footer from "./Footer"; type Props = { _site: any; children?: React.ReactNode; }; // Change hex color into RGB export const getRGBColor = (hex, type) => { let color = hex.replace(/#/g, "") var r = parseInt(color.substr(0, 2), 16) var g = parseInt(color.substr(2, 2), 16) var b = parseInt(color.substr(4, 2), 16) return `--color-${type}: ${r}, ${g}, ${b};` } const PageLayout = ({ _site, children }: Props) => { const primaryColor = getRGBColor(_site.c_styling?.primaryColor ?? "#000000", "primary"); const secondaryColor = getRGBColor(_site.c_styling?.secondaryColor ?? "#808080", "secondary"); return ( <> <style>:root {`{${primaryColor} ${secondaryColor}}`}</style> <div className="min-h-screen"> <Header _site={_site} /> {children} <Footer _site={_site} /> </div> </> ); }; export default PageLayout;
Local Development
During local development, you can define environment variables directly in the .env
file, which will be picked up and incorporated into your Stream configurations during the build.
Deploying to Production
In production, you can define environment variables in your site settings (see the Site Settings reference article for more details). These can also be added to your sites programmatically using the Configuration API .
Should you ever update any environment variables, you will need to initiate a new deployment in order to see your updated variables reflected in the new deployment. To accomplish this, you can click βNew Deployβ from the deploys page in the UI.
Approach 2: βSubfolderβ per Site
This approach allows you to store site-specific code within subfolders. The name of each subfolder must match the hostname at which the site will be hosted.
By using subfolders, it is possible to make each site in your fleet entirely custom; while also allowing for fleet-wide reuse of common files such as components or assets. Refer to our fleet-subfolder-starter to see an example repository.
You must include a subfolder per site in the following directories of your Pages project:
sites-config
src/templates
Refer to the example directory structure below:
.
βββ sites-config
β βββ www.example-1.com
β β βββ ci.json
β βββ www.example-2.com
β β βββ ci.json
β βββ ...
β βββ www.example-n.com
β βββ ci.json
βββ src
β βββ assets
β βββ components
β βββ styles
β βββ templates
β βββ www.example-1.com
β β βββ index.tsx
β βββ www.example-2.com
β β βββ index.tsx
β βββ ...
β βββ www.example-n.com
β βββ index.tsx
βββ package.json
βββ package-lock.json
βββ tsconfig.json
βββ vite.config.js
Setting up the sites-config Directory
In the sites-config
directory, each site in your fleet requires its own subfolder, wherein any configuration files specific to a particular website can be provided, such as ci.json
. Refer to the site-config
directory structure below:
.
βββ sites-config
β βββ www.example-1.com
β β βββ ci.json
β β βββ site-stream.json
β βββ www.example-2.com
β βββ ci.json
β βββ site-stream.json
βββ src
β βββ assets
β βββ components
β βββ styles
β βββ templates
βββ package.json
βββ package-lock.json
βββ tsconfig.json
βββ vite.config.js
Each ci.json
file should be configured to build and collect artifacts specific to each site.
Refer to the
features
andbuildCmd
properties in the example configuration file below, which tells the Pages system to run your build based on the contents of the www.example-1.com folder:// sites-config/www.example-1.com/ci.json { "artifactStructure": { "assets": [ { "root": "dist", "pattern": "assets/**/*" } ], "features": "sites-config/www.example-1.com/features.json", "plugins": [ { "pluginName": "Generator", "sourceFiles": [ { "root": "dist/plugin", "pattern": "*{.ts,.json}" }, { "root": "dist", "pattern": "assets/{server,static,renderer}/**/*{.js,.css}" } ], "event": "ON_PAGE_GENERATE", "functionName": "Generate" } ] }, "dependencies": { "installDepsCmd": "npm install", "requiredFiles": ["package.json", "package-lock.json", ".npmrc"] }, "buildArtifacts": { "buildCmd": "npx pages build --scope www.example-1.com" }, "livePreview": { "serveSetupCmd": ":" } }
Setting up the src/templates Directory
Within the src/templates
directory, the templates for each site must be stored within subfolders. This is necessary in order for each of the sites-config/ci.json
build commands to work, each of which references a particular subfolder with the --scope
flag.
Refer to the src/templates
directory structure below:
.
βββ sites-config
βββ src
β βββ assets
β βββ components
β βββ styles
β βββ templates
β βββ www.example-1.com
β β βββ index.tsx
β β βββ 404.tsx
β βββ www.example-2.com
β βββ index.tsx
β βββ 404.tsx
βββ package.json
βββ package-lock.json
βββ tsconfig.json
βββ vite.config.js
Local Development
Local development can be scoped to a particular site in your repo by utilizing the following commands (and by replacing REPLACE_ME
with the hostname of your desired site):
npx pages build --scope REPLACE_ME
yext pages generate-test-data --hostname REPLACE_ME
npx pages dev --scope REPLACE_ME
Deploying to Production
In production, each site in your fleet must be connected to a domain. Based on the domain connected to a site, the Pages system will run the build step using the corresponding subfolders from the repo.
For example, if a site is connected to www.example-1.com
, the build will run based on the configuration files specified in sites-config/www.example-1.com
.
Custom Styling per Site
Styling can also be customized on a per site basis. Refer to the example src/styles
directory structure below, which shows how multiple
TailwindCSS configuration files
can be implemented:
.
βββ sites-config
βββ src
β βββ assets
β βββ components
β βββ styles
β β βββ www.example-1.com
β β β βββ index.css
β β β βββ tailwind.config.cjs
β β βββ www.example-2.com
β β βββ index.css
β β βββ tailwind.config.cjs
β βββ templates
βββ package.json
βββ package-lock.json
βββ tsconfig.json
βββ vite.config.js
sites-config
and src/templates
directories.
In this example, each subfolder can contain its own tailwind.config.cjs
file:
// src/styles/www.example-1.com/tailwind.config.cjs
module.exports = {
content: ["./src/**/*.{html,js,jsx,ts,tsx}"],
theme: {
extend: {
colors: {
// [INSERT SITE-SPECIFIC STYLING HERE]
},
},
},
plugins: [],
};
Each .css
file can refer to its own tailwind.config.cjs
file:
/* src/styles/www.example-1.com/index.css */
@config "./tailwind.config.cjs";
Finally, at the template level, each .css
file can can be imported for site-specific styling:
// src/templates/www.example-1.com/index.tsx
import "../../styles/www.example-1.com/index.css";
Observe how the following
component
refers to bg-primary
as a background color, but renders a different color based on the site:
- https://sushi.yext.com.pagescdn.com/ny/new-york/yext-sushi-location1 (blue background)
- https://tacos.yext.com.pagescdn.com/location3-upper-west-side (orange background)
Known Limitations
- Subfolders are not supported in the
functions/http
directory. Any HTTP functions included in your repository will be made available to all sites in the repo.