Last reviewed: February 10, 2026

Integrating Content Publisher with any web framework


Content Publisher provides integration for WordPress, Drupal and Next.js websites but it is also possible to use Content Publisher with other website technology. Pantheon won’t provide the turnkey solution but can guide a development team for the integration.

Using Content Publisher with a Javascript based Website stack

If the target system is using React and Javascript, Pantheon provides an SDK that greatly facilitates the integration.

Integration of the real time preview

Configure the SDK at /api/pantheoncloud to receive requests from the Content Publisher system. Real-time preview starts with a request to /api/pantheoncloud/document/{documentId} which is processed by the SDK and redirected to the actual page where it should render. On the rendering page, the SDK uses a short-lived JWT token to receive real-time updates with GQL; re-rendering the article content as changes are made.

Handling of published and draft documents

NextJS retrieves documents from Content Publisher’s graphql backend on an as-needed basis (or published documents are retrieved during build time in the case of static site generation).

Integration of search

Published documents are indexed by default, and indexed per collection. Searches can be made either with the SDK, or directly to the GraphQL endpoint.

Content Structure

Collection Managers and Admins can configure a content structure for a collection, which is a hierarchy of articles. UI described at https://docs.content.pantheon.io/content-tree

It can be retrieved through GQL with a query such as

query GetSiteContentStructure($id: String!) {

 site(id: $id) {

   id

   contentStructure

 }

}

Or as a property in the Site object. Typed as following (contentStructure is a SerializedContentStructure object).

export type ArticleTreeItem = {

type: 'article';

name: string;

 published: boolean;

 slug: string | undefined;

};

export type CategoryTreeItem = {

type: 'category';

name: string;

};

export type TreeItem = {

 id: string;

 children?: TreeItem[];

 isHidden?: boolean;

} & (ArticleTreeItem | CategoryTreeItem);

export type SerializedContentStructure = {

 active: TreeItem[];

 uncategorized: TreeItem[];

};

Smart Components

These are reusable React components which content editors can insert directly into Google Docs. The schema and rendering of these components is handled by the developer, while the attributes of each component is configured by the content editor.

SDK

Searches can be done with the SDK, for example:

getAllArticlesWithSummary is typed as follows

async function getAllArticlesWithSummary(

args?: Parameters<typeof getArticles>[1],

options?: Parameters<typeof getArticles>[2],

withSummary?: boolean,

)

The summary is an LLM answer to your search query, using the search results as context. An example from the starter kit is:

await PCCConvenienceFunctions.getAllArticlesWithSummary(

   {

     publishingLevel: "PRODUCTION",

   },

   searchParams.q

     ? {

         bodyContains: searchParams.q,

       }

     : undefined,

   true,

);

GQL

Or directly to GQL. Here is an example of searching across multiple collections (more in docs)

query {

articlesv3 (

   siteIds:["loAWY0YB0HTHexSzw3Z1", "lqAWY0YC0HTHexSzw1Z" ],

   filter: {

     body: {

       contains: "your search query"

     }

   }) {

   articles {

     id

     title

     siteId

   }

}

}

Using Content Publisher with another technology stack

If the target system can’t use the Content Publisher SDK, content can still be delivered and integrated relying on Content Publisher GraphQL delivery API and our Webhook support. This is the way our WordPress and Drupal integration work.

Integration of the real time preview

When real-time preview starts, Content Publisher will send a request to /api/pantheoncloud/document/{documentId} with query params:

  • pccGrant this is a JWT needed for real-time access
  • publishingLevel usually this will be REALTIME
  • versionId specific id of the version (you may not always receive this

For real-time updates, subscribe to GQL at this endpoint:

wss://gql.content.pantheon.io/sites/{siteId}/query

With a connection param using the realtime JWT provided (we use connectionParams from the Apollo client):

{

 "PCC-TOKEN": pccGrant

}

Exact implementation will depend on your language & library used, but the query shape is:

 subscription OnArticleUpdate(                                                                              

   $id: String!                                                                                              

   $contentType: ContentType                                                                                

   $publishingLevel: PublishingLevel                                                                        

   $versionId: String                                                                                        

 ) {                                                                                                        

   article: articleUpdate(                                                                                  

     id: $id                                                                                                

     contentType: $contentType                                                                              

     publishingLevel: $publishingLevel                                                                      

     versionId: $versionId                                                                                  

   ) {                                                                                                      

     # Returns full article fields including content                                                        

   }                                                                                                        

 }

Handling of published and draft documents

article.publish and article.unpublish events will be sent to the collection’s registered webhook. This can be used to update your own index/mirror of documents. Use the articleId and siteId to retrieve the latest version of that article from GQL.

{

 event: "article.publish"|"article.unpublish",

 payload: {

  articleId: XYZ,

  siteId: ABC,

}

Content Structure

Refer to the description of Content Structure in the next.js section above.

Smart Components

  1. Signal to Content Publisher that you support smart components by responding to /api/pantheoncloud/component_schema with the full schema (of type SmartComponentMap, which is basically a dictionary of each component type.

type SmartComponentMap = Record<string, {                                                            

   title: string;                    // Display name                                                  

   variants?: string[];              // Optional variants (e.g., ["standard", "hero"])                

   iconUrl?: string | null;          // Icon for the add-on UI                                        

   exampleImageUrl?: string | string[] | null;  // Preview images                                      

   fields: Record<string, FieldSchema>;                                                                

 }>;                                                                                                  

                                                                                                       

type FieldSchema = {                                                                                  

   type: "string" | "textarea" | "number" | "boolean" | "date" | "file" | "enum" | "object";          

   displayName: string;                                                                                

   required: boolean;                                                                                  

   defaultValue?: string | number | boolean | Array<string | number | boolean>;                        

   multiple?: boolean;                                                                                

   // For enum type:                                                                                  

   options?: Array<string | { label: string; value: string }>;                                        

   // For object type:                                                                                

   fields?: Record<string, FieldSchema>;                                                              

 };                                                                                                        

Example

 {                                                                                                    

   "MEDIA_PREVIEW": {                                                                                  

     "title": "Media Preview",                                                                        

     "iconUrl": null,                                                                                  

     "variants": ["standard", "hero"],                                                                

     "exampleImageUrl": ["https://example.com/preview.png"],                                          

     "fields": {                                                                                      

       "url": {                                                                                        

         "displayName": "URL",                                                                        

         "required": true,                                                                            

         "type": "string"                                                                              

       }                                                                                              

     }                                                                                                

   }                                                                                                  

 }

  1. Respond to /api/pantheoncloud/component_schema/{component_name} with a specific component’s schema

Example

{                                                                                  

 "title": "Media Preview",                                                                        

 "iconUrl": null,                                                                                  

 "variants": ["standard", "hero"],                                                                

 "exampleImageUrl": ["https://example.com/preview.png"],                                          

 "fields": {                                                                                      

    "url": {                                                                                        

      "displayName": "URL",                                                                        

      "required": true,                                                                            

     "type": "string"                                                                              

   }                                                                                              

 }                                                                                                 }

  1. (Optional) Show a preview of the component when requested at /api/pantheoncloud/component/{component_name}?attrs={ATTRS_BASE64}

    Example handler

export default function SmartComponentPreview() {                                                    

   const { id, attrs } = router.query;                                                                

                                                                                                       

   // Decode base64 attributes                                                                        

   const decodedAttrs = attrs && typeof attrs === "string"                                            

     ? JSON.parse(Buffer.from(attrs, "base64").toString())                                            

     : {};                                                                                            

                                                                                                       

   // Get the component from client-side mapping of name to component                                                                

   const SmartComponent = clientSmartComponentMap[id]?.reactComponent;                                

                                                                                                       

   return (                                                                                            

     <SmartComponent                                                        

       {...decodedAttrs}                                            

     />                                                                                                

   );                                                                                                  

 }    

Render the smart components from the article content
They will be inserted into the content with tag component. For example

 // 1. Check if node is a component                                                                    

 if (element.tag === "component") {                                                                    

   const componentType = element.type?.toUpperCase();                                                  

   const component = smartComponentMap?.[componentType];                                              

                                                                                                       

   if (component?.reactComponent) {                                                                    

     // 2. Render with attributes from tree                                                            

     return React.createElement(                                                                      

       component.reactComponent,                                                                      

       element.attrs as Record<string, unknown>                                                        

     );                                                                                                

   }                                                                                                  

 }

A real implementation can be found at https://github.com/pantheon-systems/content-publisher-sdk/blob/main/packages/react-sdk/src/components/ArticleRenderer/PantheonTreeV2Renderer.tsx

Integration of search

Either use the GraphQL search endpoint Implementing Preview for a new website technology or update your search index when webhook events come Implementing Preview for a new website technology

Content Publisher Approval Workflows

Described in more detail at https://docs.content.pantheon.io/approval-workflows but the short-version is:

  • It’s optional
  • When enabled, contributors will not be able to publish changes directly to live. They will enter a review queue which can be managed in the collection on the Content Publisher dashboard.
  • Approved content can be either published immediately, or approved but “pending publication”
  • Admins and collection managers can skip approval for their own content changes.

No configuration is necessary for approval workflows, in any tech stack.