How Re-Produce Wordpress Short Code Functionality for Dynamic Content

22 replies
Last updated: May 4, 2020
I’m looking to re-produce something similar to the Wordpress short code functionality so my client can make page content dynamic (more on use case in 🧵). Should I use “Annotations” or a “Block” for this? I like how the Annotations are inline by default, which I’ll need. However, a “short code” block would be similar to the Gutenberg Short Code Block. However, I’ve never seen a block be placed inline with a paragraph (hence the name block).
May 2, 2020, 6:30 PM
You can add custom objects to the block type too. Just add
of: [{type: “aType”}]
to it. https://www.sanity.io/docs/block-type#of-d0f97ffa1dd9
May 2, 2020, 6:32 PM
Use Case: For each blog post category, there is a dynamic paragraph that gets displayed on each individual post page. This paragraph is different for each category and sources it’s dynamic data from the fields on the post page. Example:
Hello, welcome to my post called [postTitle]. It was created on [postPublishDate] by [postAuthor].
May 2, 2020, 6:33 PM
user Y
Yes, I have a lot of custom blocks already, that’s the “Block” solution I was referring to as one of my options. However, that will display the full width block in the editor which makes reading the inline content much harder, which was making me lean towards using “Annotations” (like external links are handled). Any down/upside to using one over the other in this case?
May 2, 2020, 6:47 PM
If I use an annotation, I would likely use the text being annotated as the reference value to the dynamic data. Re: use case above,
postTitle
=
props.postTitle
on the single blog post page.
May 2, 2020, 6:48 PM
Check the doc link ;) it’s not for the array, but for the
block
. This should’ve been covered better in the documentation because it’s pretty powerful
May 2, 2020, 6:51 PM
Thanks
user Y
, not sure if there is something else I’m missing but I am implementing custom blocks already.I got this (almost) working using a custom annotation called “DynamicContent”. I pass the page props down to the
BaseBlockContent
which uses a custom serializer. From there, I am matching the
props.children
value with the dynamic page data (page props).
This is working for shallow props, but nested props are returning undefined. Know why?


const content = props.data[props.children]
May 3, 2020, 3:04 PM
This is custom inline blocks.

{
	name: "content",
	type:"array",
	of: [
		{
			type: "block",
			of:[{type: "attributeSelection"}]
		}
	]
}
May 3, 2020, 3:08 PM
And then you could make a simple object, with predefined strings that you can use to target document fields in the frontend.

{
	name:"attributeSelection",
	type:"object",
	fields:[{
		name:"attribute",
		type:"string",
		options:{
			list:[
			{title:"Publish date", value: "publishedAt"},
			//... and so on
			]
		}
	}]
}
May 3, 2020, 3:11 PM
Yup, I have all of that functioning, here is my custom block.
export default {
    title: 'Block Content',
    name: 'blockContent',
    type: 'array',
    of: [
      {
        title: 'Block',
        type: 'block',
        styles: [
          //...
        ],
        lists: [
          //...
        ],
        marks: {
          decorators: [
            //...
          ],
          annotations: [
            //...
            {
              title: 'Dynamic Content',
              name: 'dynamicContent',
              type: 'object',
            }
          ]
        }
      },
    //...
    ]
  }
My challenge is compiling the prop reference from another prop.
May 3, 2020, 3:15 PM
Example:
// "postTitle" works and returns "My Post Title successfully
{
  children: [
    0: "postTitle",
  ],
  data: {
    postTitle: "My Post Title",
    category: {
      title: "The Category Title",
    }
  }
}
However:

// "category.title" returns undefined
{
  children: [
    0: "category.title",
  ],
  data: {
    postTitle: "My Post Title",
    category: {
      title: "The Category Title",
    }
  }
}
May 3, 2020, 3:17 PM
For some reason, this code doesn’t work with nested objects…
const content = props.data[props.children]
May 3, 2020, 3:17 PM
I’m pretty sure that you want to take a closer look at my example :) isn’t this the thing you really want?
May 3, 2020, 3:30 PM
Not exactly. You are explicitly referencing another data set, in my case, the reference is dynamic based on the context (props) of the page. This is why I have to use the
children
value and a reference to fetch the pages
props
.
May 3, 2020, 3:36 PM
Ok. Let me take a closer look when I’m on my laptop
May 3, 2020, 3:38 PM
Use Case:A content section that will display on every blog post page but is unique for each category. Admin can manage this content at the category level, customize the content/paragraph but include dynamic data from the single blog post.


Example:On the single blog post page, you have content that reads:
“Welcome to my post
A Blog Post Title written by Knut Melvæ. Get all your latest news here.”
That post belongs to a category called “News” and in Sanity the field content for the News category is:
“Welcome to my post [TITLE] written by
[AUTHOR]. Get all your latest news here.”
May 3, 2020, 3:46 PM
So this isn't the authoring experience you're after
user T
?
May 3, 2020, 5:37 PM
And then you could do something like this: https://codesandbox.io/s/winter-sunset-rfebx?file=/src/App.js
May 3, 2020, 5:47 PM
Yes, that authoring experience would be fantastic. The key difference is that the dynamic content attribute reference is sourced from another record, not the current document. My body content would be on the Category record and it would be pulling in dynamic content from the blog post record under that category.
May 3, 2020, 6:14 PM
Your solution above is similar to what I’ve posted, however it only works on first level object references, you can’t reference anything nested in the props object. I’ve forked your CodeSandbox with an example trying to display a
category.title
. https://codesandbox.io/s/jovial-hypatia-e7s3q?file=/src/App.js:340-389
May 3, 2020, 6:34 PM
I was able to get the nested attributes to work by using
eval()
(I’d like feedback if that’s not wise and there are other alternatives). I’ve updated the CodeSandbox above with the new method and it demonstrates the functionality well.
May 4, 2020, 1:16 AM
user Y
Thanks for you help, one last point of clarify. In your latest screen recording, how did you configure the “Dynamic content” “Attribute” field reference? Is “Attribute” another document you created just to populate the dropdown and control those options?
May 4, 2020, 1:28 AM

Sanity.io – build remarkable experiences at scale

Sanity is a customizable solution that treats content as data to power your digital business. Free to get started, and pay-as-you-go on all plans.

Categorized in