Add slots in the post content after every second paragraph

May I know if I can add Slots in the post content using Slot and Fill feature ( Slot and Fill) ?

Yeah, you could do that! :slight_smile:

You would then need to add a processor that converts some of the html elements in your content into Slots. So you’d have to settle on a convention for that for your application.

Assuming this is your post content coming from WP:

<div class="slot">
  ...some default content if there is no fill provided
</div>

Your processor should look something like:

const slotProcessor = {
  test: (node) =>
    node.type === "element" &&
    node.props.className === "slot"
  process: (node) => {
    node.props.name = "Inside Content"
    node.component = Slot
    return node;
  }
}

You should then add this processor in the index.js file at the root of your theme like:

const myTheme = {
  state: {},
  actions: {},
  roots: RootComponent,
  libraries: {
    html2react: {
      processors: [slotProcessor],
    },
  },
};
1 Like

@mmczaplinski Thanks a lot for your prompt response!

I can see how your approach can work for a new blogpost (yet to be created), where we can add an element just so it could be processed by slotProcessor, and later used as slot. Can your approach be used for already existing blogposts? Is yes then how and if no, then what could be an alternative approach?

We are trying to move 5000 blogs from an existing website, to a new website powered by Frontity.

Hey @sarang !

Yes, it can be used with existing blog posts of course! In fact you can use it exactly the way that I’ve outlined above. The processors will work exactly the same way for “new” posts as well as for the “old” ones :slight_smile:

The HTML that is returned from the WordPress REST API is “processed” by the processors at runtime !

I’d encourage you to make a quick demo using the above approach to see for yourself that this is the case. We ll be happy to help if you have further questions!

How can I add div with classname slot, to an existing post.content?

<div class="slot"></div>

Finally, got it running.

const Slot = {
    name: "slot",
    priority: 1,
    test: (motherShip) => motherShip.node === motherShip.root[0],
    processor: (motherShip) => {
        let temp = cloneDeep(motherShip.node)
        temp.children = [];
        temp.component = null;
        temp.props = {};
        temp.props.id = "XXXXXXX";
        temp.component = Adsense;
        temp.props.className = "_ap_apex_ad";

        motherShip.root.filter((element) => element.component === "p").map((component, index) => {
            if (index % 2 === 0) {
                component.children.push(temp)
            }
        })

        return node;
    }
};

export default Slot;

Is there a better way?

I believe that you could simplify it a bit by doing just :

let temp = cloneDeep(motherShip.node)
temp.children = [];
temp.props = { id: "XXXXXXX", className:  "_ap_apex_ad" };
temp.component = Adsense;

Also, I think you should be fine with just the default priority of 10 unless you have other processors that would definitely require this processor to run first.

2 Likes

@mmczaplinski
Please correct me if I am wrong, but your solution is based on this test

 test: (node) =>
    node.type === "element" &&
    node.props.className === "slot"

But in my situation, I don’t have a div in post.content that has a classname slot and I don’t know how to add it. If you know, please do share.

My exact requirement is that I need to display an ad after every second p tag in post.content.

Hey @sarang,

My (hypothetical) example was assuming that you have access to the content and you can insert the slots yourself in the block editor :slight_smile:

If that’s not the case, you can use the approach that you have used previously (inserting a component as a child if it’s index is even). So in answer to your question:

I think this is the right way to do it given your requirements :slight_smile:

1 Like

if i am making new website and adding all new blog then which is right way?

We’ve also used this approach with success in the past:

let count = 1;

const Paragraph = ({ children, count, ...props }) => (
  <>
    <p {...props}>{children}</p>
    <Slot name={`After the paragraph ${count}`} />
  </>
);

export const reset = {
  name: "reset-count",
  priority: 1,
  test: ({ node, root }) => root[0] === node,
  processor: ({ node }) => {
    count = 1;
    return node;
  },
};

export const adsProc = {
  name: "slot-after-paragraph",
  priority: 9,
  test: ({ node }) => !node.parent && node.component === "p",
  processor: ({ node }) => {
    count += 1;
    if (count % 2 === 0) {
      node.props.count = count;
      node.component = Paragraph;
    }
    return node;
  },
};

Instead of inserting the <Slot> component, you could insert your Adsense component, of course.

1 Like

What @mmczaplinski described is a good way to insert slots using Gutenberg.

In the future, once we release the Frontity WordPress plugin, we can add a new Slot block to WordPress so it’s simpler to add Slots among your content :slightly_smiling_face: