Wagtail StreamField for React

Stacking Tabs
5 min readDec 8, 2022
Wagtail StreamField for React

TLDR I don’t want to waste time

Yes, this is the code.

Github: https://github.com/prolenodev/wagtail-streamfield-and-react.git

Code

Wagtail StreamField

Wagtail StreamField explained in one picture
This fancy editing model in action

As you can see, you can organise heading, paragraph, image and video blocks of the body. React StreamField provides a nice interface for you to add, reorder, delete these blocks.

“StreamField provides a content editing model suitable for pages that do not follow a fixed structure — such as blog posts or news stories — where the text may be interspersed with subheadings, images, pull quotes and video. It’s also suitable for more specialised content types, such as maps and charts (or, for a programming blog, code snippets). In this model, these different content types are represented as a sequence of ‘blocks’, which can be repeated and arranged in any order.” — Wagtail Documentation

Purpose

In this post, I will walk through how I find my way around using Wagtail StreamField for React.
Perhaps you will save time or if you come up with better solution, please share it. 😊

My aim is to display the body streamfield filled with paragraphs, images, etc. in React.
This means the data (the paragraphs, images in body streamfield) has to be exposed over Wagtail v2 API and then consumed by React.

Problem

Now, the problem for me is that the API in JSON looks like this.

Problem I : Paragraph

If you do {body.value}, the value includes html tags.

Problem II : Image

You cannot just do {body.value}, because for “type”: “image”, the value is 12. If you do {body.value}, the value 12 will be displayed, not your image.

Problem III : Different types

Paragraph and image require different ‘treatment’, and the order of types can be different. Perhaps for the next blog post it is image first, then image again, then paragraph, then video, yada yada. So you cannot target order, expecting the first one to be paragraph, and just do body[0].value also.

You have to take the image value which is 12 as the parameter of your url, to retrieve download_url.

From

http://localhost:8000/api/v2/pages/?type=blog.Blog&fields=description,thumbnail,body&id=11

to

http://localhost:8000/api/v2/images/12/

Models.py

An almost-barebone example of models.py

Other examples can be found in Wagtail docs.

The content_panels is for the admin interface. Remember to add api_fields to expose the StreamField data over API. If you need to connect Wagtail and React from scratch, you may find my past post helpful.

The Only Hint

Fortunately, I know this is not impossible because we have one hint on Github. At least now I know I will not be running around like a headless wagtail. This example uses mock data, so cannot just copy. We will have to fetch data, I use Axios.

https://github.com/AccordBox/wagtail-react-blog/blob/master/frontend/src/components/StreamField/StreamField.js

Applicable Jargons

The chicken and egg problem. To know how to find your way, there is a catch: you need to know the keyword. These may be helpful if you need to read around:

  • data types (arrays, objects)
  • nested api (objects in array)
  • useEffect, setState
  • axios + try/catch + then/finally block vs async/await
  • inject html using .push()
  • dangerouslySetInnerHtml
  • local vs global scope
  • for loop

Code Explained

1. Line 21: Loading set to true
2. Line 22: Get data from api/v2/pages/…
3. Line 22: Await until all data has arrived
4. Lines 24, 25: Only then load data into Data using setData and Body using setBody

There are two ways, try then/finally block or async/await, both of which work.

For more, see https://stackoverflow.com/questions/70996079/async-await-on-axios

Method 1:

Method 2:

5. Line 26: Loading set to false
6. Lines 28, 29: Set html to empty array and loop.
a. In this case, body.length is 2.
b. How to read REST API?
c. How to access body’s value?
i. How to access “body”? Topic: Nested API
ii. Nested object: Access object within object {}
iii. Nested array: Access object within array []
iv. Because body is an array of objects, body[0].value is used and not body.value

7. Line 32: Then use the data in body, to get data from api/v2/images/…
8. Lines 33, 34 : Set image with the download_url data from api/v2/images/…

9. Line 76: Use this download_url as image source location.
10. Lines 38–44: The value of <p> is in html format.
• DangerouslySetInnerHTML
- Inject HTML using html.push()
- Sanitise using DOMPurify.sanitize()
- Install using npm i dompurify
- DOMPurify.sanitize()
- Eg. <div dangerouslySetInnerHTML={{ __html: DOMPurify.sanitize(body[i].value) }} />
11. Lines 86–92: Now paragraph is without html tags.
12. Lines 78–84: Just title and description from setData(result.data.items) from Line 24.
13. Lines 20, 49: Try-catch any error in case try part does not work.
14. Line 55: useEffect

That is all from me. As I am only an internet-taught googling my way, this post is intended to thank those who share their knowledge on the internet. There is no one else to proof read or guide me, I appreciate any correction or feedback. Leave a comment or two so others can see too, I thank you in advance.

Useful References

https://docs.wagtail.org/en/stable/topics/streamfield.html

https://github.com/AccordBox/wagtail-react-blog/blob/master/frontend/src/components/StreamField/StreamField.js

https://stackoverflow.com/questions/70996079/async-await-on-axios

https://dev.to/mitchelln11/undefined-nested-object-in-rest-api-with-react-hooks-3j02

https://medium.com/@viacheslavlushchinskiy/javascript-objects-and-arrays-manipulation-for-rest-api-b3b59a73b618

--

--

Stacking Tabs

My learning playground. Coding. Korean. German? History. Many interests. Food. E-commerce? https://withkoji.com/@stackingtabs