Captioned Images in Expression­Engine

Captioned images. They seem like they should be so easy. Drop an image into a content field and add a caption, and you're done, right?

But they never are easy, are they? I can't even count the number of times a client has requested an easy way to add captioned images to a content area on a site.

What do you do? Have the client learn HTML and edit the view source? No, they're not interested in that. Hack something together with shortcodes and a grid? It works, but it isn't very client-friendly.

Now there's another option: Blocks. Creating captioned images with Blocks will leave you in control of the markup, but provide a client-friendly way for you clients to create captioned images.

This article will walk you though setting up Blocks to enable captioned images from start to finish.

Install Blocks

The first thing you need to do is get Blocks. It's available on Devot-ee here.

If you're familiar with installing ExpressionEngine add-ons, Blocks will be no different. Just follow the directions in the readme.

Define the blocks

The first thing we need to do is define the blocks that we'll use in our field. A block is a chunk of content that is made up of one or more fields. Hang with me, that will make more sense in a little bit.

Using the menu, go to Add-ons → Modules and click on Blocks. We haven't defined any blocks yet, so this will be mostly empty. Let's change that. We'll need to define a "Captioned Image" block.

A blank slate: after selecting 'Blocks' from the modules options, the types of blocks we can use is only limited by our imagination

Click on Create block type. The name will be "Captioned Image", and we'll leave the short name as "captioned_image". Then we'll need three fields for this captioned image: the image itself, the alt text for the image, and the caption. I used simple Text field types for the latter two.

Defining a block by combining a handful of field types

We're done, so hit Save. Boom: we've created our first block type.

We'd be set if all we wanted was for our client to create captioned images all day. But what the client needs is to create content interspersed with the occassional captioned image. In fact, the content is actually more important than the captioned images, but I thought we should start with the fun one.

So, click on Create block type again. This time, the name will be the oh-so-original "Content", and in this case we'll make the short name "simple_content". A single Wygwam field will complete this block type; call it "content".

Create field

With our blocks defined, we can now create the field. Most of this is pretty straightforward: I've created a field, changes its type to "Blocks", and given it the name "page_content".

We're setting up our field! What block types should we allow the editor access to?

The final thing we need to do here is choose which block types are relevant to this field. On some larger sites we could have a variety of block types that we want to mix and match across different fields, but today, for our purposes, we want both of the block types that we've defined. So, check them both.

The order is important here. We want to make sure that "Content" is first because when an editor creates a new entry, the first block type will be shown, ready to take the editor's input. And our editor is far more likely to start entering content than they are creating a captioned image.

Edit page

Everything on the back end is set up! We're ready to enter content.

When we create a new page, we're presented with the usual elements. A title. A URL title. Any other fields you may have set up. And we can also see our new Content field. It's ready and waiting for us with a content block already created, so let's fill it out.

But this article is about captioned images, so if we click the captioned image button, we will get a captioned image block ready to populate.

An entire article on the publish side. Or an except from 'Alice in Wonderland' and a 'Picture of the Day' off of Wikipedia.

And let's finish off with some more content. Our editors will of course be free to mix and match blocks however they want, but for this example will stick with three: content-image-content.

Change template

One of the great things about Blocks is that we as developers can control what markup gets generated. Let's edit our template.

Open up your template — either in the control panel or in your editor, however you normally do it. With a regular content field, you may be used to seeing something like the following code block.

{exp:channel:entries channel="page"}
  <div class="content">
    {!-- Not yet blockified --}

That's exactly where we're going to start. Except we now also need to template out each of our block types.

To do that, we need to change {page_content} into a tag pair. Within that tag pair, we write a tag pair for each block type, and use the field names to include the content.

{exp:channel:entries channel="page"}
  <div class="content">
    {!-- tag pair now --}

        {!-- for basic content, just
             output the content --}

      <figure class="captioned-image">
        <img src="{image}" alt="{alt}">
        {if caption}


Using the templating system, we are able to give the captioned image the exact markup we want. Here, we created an outer wrapper so we can style this with CSS. We can drop the <figcaption> element if we don't need it. If this markup doesn't work in the future, we can change it from the template instead of updating every content field.

Also note that we put our {simple_content} block before our {captioned_image} block. This was entirely arbitrary. The order of the blocks that get outputted is determined by the publish screen, so in our case the blocks will get outputted content-image-content.


Success! Captioned images that your client can create and maintain on their own. Add a little bit of style with CSS and they fit the design of the site perfectly.