DFP Ads within structured text

We are rendering structured text on our react frontend. We need to push DFP ads within the body content. One ad after first two paragraphs, second ad after first 6 paragraphs and so on. Is it possible to push DFP ads while we are rendering with StructuredText?

We tried -

customRules={[
            renderRule(
              isParagraph,
              ({ adapter: { renderNode }, node, children, key, ancestors }) => {
                if (isRoot(ancestors[0])) {
                  return renderNode('p', { key, className: 'top-level-p-tag' }, children);
                } else {
                  return renderNode('p', { key }, children);
                }
              }
            )
          ]}

But issue is that there are other top level elements too like blockqoute, div etc and also we have custom renderBlock for videos, codeblock etc.

What you could do is to “inject” some additional nodes into the ST document. Example:

const structuredText = graphqlResponse.data.blogPost.content.value;
const topLevelElements = structuredText.document.children;

const dfpNode = { type: 'dfp' };

if (topLevelElements.length >= 2) {
  topLevelElements.splice(2, 0, dfpNode);

  for (let i=7; i < topLevelElements.length; i += 5) {
    topLevelElements.splice(i, 0, dfpNode);
  }
}

Then you can add a render rule for the “virtual” dfp node we injected:

customRules={[
  renderRule(
    (node) => node.type === 'dfp',
    ({ adapter: { renderNode }, node, children, key, ancestors }) => {
      return <div>PLACE THE CODE FOR YOUR DFP HERE</div>;
    }
  )
]}
2 Likes

How to make this work with TypeScript?

You can do something like this, using a forked version of our Next.js marketing starter’s Post page as an example:

import {type StructuredTextDocument} from 'react-datocms'; // Stronger typing from our helper lib
import {PostQuery} from "@/graphql/types/graphql"; // Generated types from this project's graphql-codegen, specifically for this project's Structured Text Schema

/** First we define our custom types */

// Create a new type for our injected node
type DFPNode = { type: 'dfp' }

// We'll extend StructuredTextDocument from react-datocms
interface StructuredTextDocumentWithDFP extends StructuredTextDocument {
    document: {
        children: (StructuredTextDocument['document']['children'] | DFPNode)[]
    } & StructuredTextDocument['document']
}

// We'll also extend the generated type from this project's graphql-typegen
interface PostQueryWithDFP extends PostQuery {
    post: {
        content: StructuredTextDocumentWithDFP
    } & PostQuery['post']
}

/** Then we'll inject the nodes */
const originalStructuredText = data.post.content // Equal to PostQuery.post.content
const originalStructuredTextTopLevelNodes = originalStructuredText.value as StructuredTextDocument // This imported typedef is better than what graphql-typegen gives us
const dfpNode = {type: 'dfp'} as DFPNode; // Our new placeholder node to be injected

// Copying the children into our new type
let newChildren = [...originalStructuredTextTopLevelNodes.document.children] as StructuredTextDocumentWithDFP['document']['children']

// Modify the array in-place
if (newChildren.length >= 2) {
    newChildren.splice(2, 0, dfpNode);

    for (let i = 7; i < newChildren.length; i += 5) {
        newChildren.splice(i, 0, dfpNode);
    }
}

// Make a new top-level hierarchy of nodes
const newStructuredTextToplevelNodes = {
    ...originalStructuredTextTopLevelNodes,
    document: {
        ...originalStructuredTextTopLevelNodes.document,
        children: newChildren,
    }
} as StructuredTextDocumentWithDFP

// Build a new structured text object with our new type
const newStructuredText = {
    ...originalStructuredText,
    value: newStructuredTextToplevelNodes
} as PostQueryWithDFP['post']['content']

// Define a new render rule
const dfpNodeRule = renderNodeRule(
    // @ts-expect-error The node renderer doesn't understand our custom (fake) node type, but that's OK
    (node) => node.type === 'dfp',
    () => {
        return <div style={{
            width: '100px',
            height: '100px',
            background: 'orange',
            padding: '10px',
            margin: "20px auto",
            textAlign: "center",
            display: 'flex',
            alignItems: 'center',
        }}>AD GOES HERE</div>;
    }
)

// Add it to the existing list of render rules
const nodeRulesWithDFP = [...originalNodeRules, dfpNodeRule]

Then we use it in the JSX like this:

<StructuredText
  data={newStructuredText}
  customNodeRules={nodeRulesWithDFP}
  // other props here
/>

That gets you something like this example:

1 Like