Search code examples
javascriptnode.jsangularcontentful

Contentful Rich-Text-Html Embedded Assets no url


So I have been struggling to get my head around the Contentful assets e.g. images one can add as part of a single content field i.e. Description (Rich-Text)

I have a middleware NodeJS server which aggregates content from various 3rd parties. One of these 3rd parties is Contentful. But what I have noticed is that images are completely excluded from the response using the below.

I tried using the @contentful/rich-text-html-renderer server side and all I get back is only paragraphs. Nothing else and when I did manage to include BLOCKS using @contentful/rich-text-types I noticed that upon looking at the response back the embedded element does not have a image url only the image ID from Contentful.

I have tried using various sources all of them use both npms with success apparently but I am yet to see this. Here is some code snippets. As you can see I was expecting some sort of url but its not in the payload unlike uploaded image fields. Embedded Assets seems to only reference the image ID. So how does one add images in the actual rich-text field?

This is what what I have setup in Angular

_returnHtmlFromRichText(richText) {
    return documentToHtmlString(richText,
      {
        renderNode: {                
          [BLOCKS.EMBEDDED_ASSET]: (node, children) => {
            // render the EMBEDDED_ASSET as you need
            console.log(node.data)                
            //return `<img src="https://${<need url here>}"/>`
          },
        }
      }
    );
  }

This is what I see in the response of the above

{
    "target": {
        "sys": {
            "id": "ZIIO2DUlu5rFxMykjIwaX",
            "type": "Link",
            "linkType": "Asset"
        }
    }
}

Solution

  • What you currently experience is the expected behavior. Since the field type is Rich text all embedded contents(images or entries) will be linked like you have seen.

    For graphQL, you can retrieve raw and references from your rich text and use like so:

    _returnHtmlFromRichText({raw, references}) {
        return documentToHtmlString(raw,
          {
            renderNode: {                
              [BLOCKS.EMBEDDED_ASSET]: (node, children) => {
                  if (!references || !node) {
                     return null;
                }
    
            const referenceNode = references.find(
              (reference) => node.data.target.sys.id === 
                reference.contentful_id
              );
              const imageURL = 'https:' + ${referenceNode.media[0].file.url;
                             
               return `<img src="${imageURL}"/>`
              },
            }
          }
        );
      }
    

    For Rest API, you need to make an additional request to get the asset you want.

        _returnHtmlFromRichText(richText) {
                return documentToHtmlString(richText,
                  {
                    renderNode: {                
                      [BLOCKS.EMBEDDED_ASSET]: (node, children) => {
                  const response = await fetch('https://cdn.contentful.com/spaces/<YOUR_SPACE_ID>/assets/${assetId}?access_token=<YOUR_ACCESS_TOKEN>');
                  const data = await response.json();
        
                  const imageURL = 'https:' + data.fields.file.url
                                     
                       return `<img src="${imageURL}"/>`
                      },
                    }
                  }
                );
              }
    

    assetId = target.sys.id

    You can expand this logic to cater to other cases if you need to also handle other embedded contents like Entries or Inline-Entry. raw and references are basically richText.raw and richText.references