Wagtail Tutorial #11: How to use StreamField in Wagtail

Michael Yin

Full Stack Developer

Last updated on Jan 02 2021

Table of Contents


Many people who are interested in StreamField of Wagtail do not know how to import it into their Wagtail projects, in this Wagtail tutorial, I will talk about the StreamField feature of Wagtail in detail and I must say StreamField is one of the most amazing features in Wagtail. This tutorial is the #11 of my Wagtail Tutorial Series.

Note: You can get the project which contains the source code of this tutorial at the end of this post, I recommend you to check it out instead of copying code directly from this tutorial.

What is the difference between RichText, Markdown, and StreamField

Right now most CMS system would like to use HTML editor or Markdown editor to let the user write and edit. If you are using Markdown, you will find it hard to extend the generated style. (I have talked about how to use Markdown to publish content in Wagtail in this article Add Markdown Support In Wagtail Blog ) If you are using HTML editor (RichText in wagtail), you will see it is tedious to edit the HTML and you want a more efficient way to increase your productivity.

Think about how to finish tasks below.

  • Create a page which has two columns and each column have different content. For example, one has image and the other has text

  • Insert a Signup Form, Subscription Form, Data Chart to your page quickly.

StreamField is more like a tech which stands between Richtext and Markdown. It works like Django template, you can predefine some content blocks when you create a page, you can choose a block, set the value to blocks and then render it to raw HTML.

For example, to make editor quickly insert subscription form in my Wagtail project, first, I can define a custom content block, second, in admin page editors can choose mailing list to make this job done quickly. No need to touch HTML.

Basic concepts you should know

Before we start to write code, I think it is better to talk about some basic concepts here so you can have a better understanding. This is a necessary step if you want people can easily get what you are talking about.

StreamField is actually a list which contains the name and block type of specific block. You can use the built-in block shipped with Wagtail or you can create your own block. One of the powerful features here is that it is possible to define new block type which contains sub-blocks, which means you can create nested structure. I will talk about this in a bit.

Block here means different data types, when you define your own block, you can create a template for it, which means you can control the HTML it is rendered.

Get started

Now I will show you how to create a simple landing page using StreamField.

class LandingPage(Page):
    body = StreamField([
        ('heading', blocks.CharBlock(classname="full title")),
        ('paragraph', blocks.RichTextBlock()),
        ('image', ImageChooserBlock(icon="image")),
        ('two_columns', TwoColumnBlock()),
        ('embedded_video', EmbedBlock(icon="media")),

    content_panels = Page.content_panels + [

Let's see the template for Landingpage, create blog/templates/blog/landing_page.html.

{% extends "blog/base.html" %}
{% load static wagtailcore_tags wagtailimages_tags %}

{% block content %}

{% with blocks=self.body %}

    {% for block in blocks %}
        {% if block.block_type == 'heading' %}
            <h2>{{ block.value }}</h2>
        {% else %}
           <section class="block-{{ block.block_type }}">
               {{ block }}
        {% endif %}
    {% endfor %}

{% endwith %}

{% endblock %}

As I said above, StreamField is a list which contains blocks, so here we use for loop to iterate the data. For more control of specific block such as heading here, you can use block_type. Here, I use block_type help me figure out the heading so I can use h2 tag to display it. Next, I will talk about the amazing part of StreamField, please be ready!

Custom Block

If you check the fields of LandingPage in the code above carefully, you will see heading, paragraph, image, embedded_video are all built-in blocks of Wagtail, I assume you can figure out its meaning. But I want to talk about more about TwoColumnBlock here, and I will show you how to create custom blocks, which is a powerful feature of StreamField.

Because I want to create a two-column page in LandingPage, so I create a custom block named TwoColumnBlock. Let's create a file blog/blocks.py, I recommend you to add your custom blocks in this file to keep your projects clean.

from wagtail.admin.edit_handlers import (FieldPanel, FieldRowPanel,
                                         InlinePanel, MultiFieldPanel,
                                         PageChooserPanel, StreamFieldPanel)
from wagtail.core import blocks
from wagtail.core.fields import StreamField
from wagtail.embeds.blocks import EmbedBlock
from wagtail.images.blocks import ImageChooserBlock

class ColumnBlock(blocks.StreamBlock):
    heading = blocks.CharBlock(classname="full title")
    paragraph = blocks.RichTextBlock()
    image = ImageChooserBlock()

    class Meta:
        template = 'blog/blocks/column.html'

class TwoColumnBlock(blocks.StructBlock):

    left_column = ColumnBlock(icon='arrow-right', label='Left column content')
    right_column = ColumnBlock(icon='arrow-right', label='Right column content')

    class Meta:
        template = 'blog/blocks/two_column_block.html'
        icon = 'placeholder'
        label = 'Two Columns'

blocks.StructBlock is a block which contains a fixed group of sub-blocks. Here TwoColumnBlock have only left and right columns. So we make it a subclass of blocks.StructBlock.

blocks.StreamBlock is a block which contains a sequence of different block types, which can be mixed and reordered. So we use it to set the left_column and right_column.

Difference between blocks.StructBlock and blocks.StreamBlock is that you can add more than one heading in blocks.StreamBlock here but you can have one left_column, because blocks.StructBlock is fixed.

We can control how this block is rendered by setting template in class Meta.

Let's create template blog/templates/blog/blocks/two_column_block.html

<div class="row">

    <div class="col-md-6">
           {{ self.left_column }}
      <div class="col-md-6">
           {{ self.right_column }}


We use two_column_block.html to control how two_columns in body of landingpage is displayed. Since I use Bootstrap theme in my blog, so class='col-md-6' is to split the row. When Wagtail try to display the left_column and right_column, it will find the template of ColumnBlock and use it to render HTML.

Here is the content of blog/templates/blog/blocks/column.html, we set it in class Meta of ColumnBlock above:

{% load wagtailcore_tags wagtailimages_tags %}

{% if blocks %}

    {% for block in blocks %}
        {% if block.block_type == 'heading' %}
            <h2>{{ block.value }}</h2>
        {% elif block.block_type == 'image' %}
            {% image block.value width-900 class="img-responsive" %}
        {% else %}
           <section class="block-{{ block.block_type }}">
               {{ block }}
        {% endif %}
    {% endfor %}

{% endif %}

After all job done, remember to migrate your db.

python manage.py makemigrations blog
python manage.py migrate blog

Create page using streamfield

Let's create a new landingpage as a child of our blog page in

You can edit content in admin as you like. Here is the screenshot.

You can add content in left column and right column, after you are done, remember to publish the page. In this tutorial I create a simple about page. If you are running my wagtail_tuto project, just check this URL, below is the screenshot of the about page created.

To make you better understand the structure of Streamfield, here is the data of the page I created.

page data:

    title = "about"
    body = [
       two_columns = [
          left_column = [
              heading = "left header",
              paragraph = "My name is Michael Yin....",
          right_column = [
              heading = "right header",
              image = image file uploaded,
              paragraph = "If you want to know more about this book...."
       heading = "Table of contents",
       paragraph = "Python CMS Framework Review....."

As you can see, you can create structured data with StreamField and it can help people publish easier than before.

Here is a video from Wagtail team which can show you how to quickly use Streamfield to edit page. You can also take a look at it.

Wagtail streamfield resources

Below are some good resources if you want to know more about what Streamfield can do

  1. torchbox-wagtail
  2. wagtail freelancer Use StreamField to build a landing page


In this tutorial, I talked about the detail of Wagtail StreamField feature, it is a brand new and powerful solution in CMS world. Its content block model make it super easy to be extended to fit your demand.

The source code of this Wagtail tutorial is available on Github, you can get it here wagtail-bootstrap-blog, and I would appreciate that if you could star my repo.

You can also check the full list of my wagtail tutorial here wagtail tutorial series

Michael Yin

Michael is a Full Stack Developer from China who loves writing code, tutorials about Django, Wagtail CMS and React.

He has published some ebooks on leanpub and tech course on testdriven.io.

He is also the founder of the AccordBox which provides the web development services.

Table of Contents

This book will teach you how to build a modern blog with Wagtail CMS

Read More


Get notified about new great Web Development Tutorial