In my pages, I have a number of custom blocks that allow you to select a page. In the save function, I save the pages URL.

    registerBlockType("djave/blockwithlink", {
        attributes: {
            page: {
                // { id: 1, url: "http://site.dev/page-url", title: "Page title"}
                type: "object",
                default: {},
          return (<div>
            <a href={attributes.link.url}>{attributes.link.title}</a>

This is great, when I save the block I get:

  <a href="http://site.dev/page-url">Page title</a>

But if the page is edited to http://site.dev/new-page-url, saved block has the out dated URL.

Is there a way to put a permalink to an ID into a save() function?

The other option I have is that I return null from the save function and instead create the template as PHP and use something like I'm used to from PHP templating like get_permalink($attributes['link']['id']).


  1. have lots of blocks that follow the same pattern which would need to be extracted to PHP.
  2. I'm really enjoying using the save function, and don't like the idea of half of my blocks being JavaScript saved and half being PHP rendered, it seems wrong.

This feels like it must be a "solved" problem. Is there a Gutenberg way of doing this?

  • "don't like the idea of half of my blocks being JavaScript saved and half being PHP rendered, it seems wrong" - that's just how dynamic blocks work, and if you want a guaranteed fresh permalink even if the post slug or permalink structure is changed later (outside or when not using the block editor), then a dynamic block would be what you need.
    – Sally CJ
    Mar 31, 2022 at 3:04
  • But if just want your save() to always use the fresh post permalink, then just save the post ID as part of the attributes object and use the REST API to fetch the post permalink. And actually, I noticed an id in the page attribute, so you could just make use of that property.
    – Sally CJ
    Mar 31, 2022 at 3:07

1 Answer 1


I think the issue you will run into here is that markup from the save function is created at post edit. The attributes like that link.url you reference will be correct at time of save - which is where the JS runs - but not necessarily at render if the post attributes have been changed.

The only way I can think of to change this at block render without PHP render would be to store your ID as a data-attribute against the anchor. Something like:

<a href="#" data-post-id="234" data-rest-route="posts">Link Content</a>

Where data-post-id is obviously the post ID and data-rest-route is the rest route or the parameter from the rest_base when you set up the posts with register_post_type() This you can parse from the initial post query return (the bit where you get the slug in your un-posted code to add to the .link.url on the block)

You can then run a secondary JS script on page load that leverages the REST API to get the correct slug at time of render.

Your URL path will be something like

/wp-json/wp/v2/{post_type}/{ID} or to match the above example /wp-json/wp/v2/posts/234

Then take the slug from the JSON object on page load to inject into the link href.

It's a bit of a workaround... And I wouldn't use this in public plugin code, as you have no idea how or if the rest API has been allowed or modified on custom post types for other setups.

  • 1
    You could also do a ?p=234 link, WP will automatically rewrite to the correct URL and no need for the replacing magic. However, this will potentially lose some "link juice"..
    – kero
    Mar 28, 2022 at 10:07
  • Yep - hadn't actually thought of that - SEO implications, an extra redirect and won't look pretty. Would work though
    – Bysander
    Mar 28, 2022 at 10:27
  • I think I asked an impossible question of how do you make static html dynamic. I think the answer is just PHP. It is definitely something the Gutenberg team could look into with Tokens, as I find the idea of using a CMS without dynamic content crazy. I like your ideas of the data attributes, however as touched on by yourself I'm interested in SEO first solutions. I like the use of progressive enhancement, but the idea that wrong URL is present onload doesn't sit correctly with me. Thanks
    – Djave
    Apr 1, 2022 at 16:46
  • Morning @Djave - as the URLs are written on page load most SEO crawlers (all that I know of) will read the final result - the ?p=123 solution isn't optimal as it's never shown the version with "final" paths. Think of it like the behaviour of a single page application - the final result is read in SEO mapping. But - yeah.... it is not straight-forward to get static content to show dynamically! :-)
    – Bysander
    Apr 4, 2022 at 8:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.