237

I am hosting a Jekyll Blog on Github and write my posts with Markdown. When I am adding images, I do it the following way:

![name of the image](http://link.com/image.jpg)

This then shows the image in the text.

However, how can I tell Markdown to add a caption which is presented below or above the image?

tanguy_k
  • 11,307
  • 6
  • 54
  • 58
orschiro
  • 19,847
  • 19
  • 64
  • 95

14 Answers14

385

I know this is an old question but I thought I'd still share my method of adding image captions. You won't be able to use the caption or figcaption tags, but this would be a simple alternative without using any plugins.

In your markdown, you can wrap your caption with the emphasis tag and put it directly underneath the image without inserting a new line like so:

![](path_to_image)
*image_caption*

This would generate the following HTML:

<p>
    <img src="path_to_image" alt>
    <em>image_caption</em>
</p>

Then in your CSS you can style it using the following selector without interfering with other em tags on the page:

img + em { }

Note that you must not have a blank line between the image and the caption because that would instead generate:

<p>
    <img src="path_to_image" alt>
</p>
<p>
    <em>image_caption</em>
</p>

You can also use whatever tag you want other than em. Just make sure there is a tag, otherwise you won't be able to style it.

Andrew Wei
  • 4,335
  • 2
  • 16
  • 7
  • 1
    Hi there! I am not quite sure where and how to put the CSS part...it would be really great if anyone could help. – ChriiSchee Apr 10 '19 at 13:50
  • 2
    @ChriiSchee Either you place it in the main CSS file, or you can create your own and link it to your default layout. For example, my default layout links to main.css file `` so I just add my custom CSS definition at the bottom of this file: `// My custom css img + em { display: block; text-align: center; } //image captions` – Jan Zavrel Sep 15 '19 at 04:01
  • It's a shame that CSS doesn't allow you to go up the chain, so you could target the

    tag here based on it having an and an . Something like `img+em < p {rules}`.

    – Codemonkey Oct 27 '20 at 07:13
  • I can't put my finger on why but I love this answer – Luis Ferrao Dec 10 '20 at 14:15
  • Given the convenience, I think this should be the accepted answer. – Omar YAYA May 23 '21 at 00:33
  • This works, but not for images that you have aligned to the right or left, Ex: `![](path_to_image){: width="400" .right} *image_caption*` Anyone have a fix for this? – Matt Popovich Jan 01 '22 at 23:12
  • 1
    This does not work properly, since can be the immediate html element after an inside a paragraph. I had the same idea than you but it doesn't work unfortunately: `We notice that *zero* (![Maya digit zero](0.png)) looks very different from any other digits. *zero* is definitely a special case.` Creates: `

    We notice that Maya digit zero looks very different from any other digits. zero is definitely a special case.

    ` So, can't use it for all cases.
    – kram08980 Feb 10 '22 at 09:28
  • For me, this created a left-flushed image with a centered caption, which looked weird. – Dr_Zaszuś Jul 07 '22 at 08:30
251

You can use table for this. It works fine.

| ![space-1.jpg](http://www.storywarren.com/wp-content/uploads/2016/09/space-1.jpg) | 
|:--:| 
| *Space* |

Result:

enter image description here

Bilal Gultekin
  • 4,831
  • 3
  • 22
  • 27
168

If you don't want to use any plugins (which means you can push it to GitHub directly without generating the site first), you can create a new file named image.html in _includes:

<figure class="image">
  <img src="{{ include.url }}" alt="{{ include.description }}">
  <figcaption>{{ include.description }}</figcaption>
</figure>

And then display the image from your markdown with:

{% include image.html url="/images/my-cat.jpg" description="My cat, Robert Downey Jr." %}
IQAndreas
  • 8,060
  • 8
  • 39
  • 74
  • 1
    That is a great idea! However, `site_root` is not accepted as a valid variable. When rendered it ends up as `src="{{ site.url_root }}...`. – orschiro Oct 14 '13 at 19:48
  • 2
    Ah, right, that is a variable added in [Octopress](http://octopress.org/). I edited it out, so the sample code just uses a relative URL to the image. – IQAndreas Oct 15 '13 at 01:31
  • 3
    Jekyll now includes a `site.url` variable. – Roy Tinker Nov 14 '14 at 00:22
  • 22
    A better markup would be: `
    {{ include.description }}
    {{ include.description }}
    `
    – Edmundo Santos Aug 06 '15 at 01:24
  • I need more information… it's possible to put more than one image without the need to repeat the `include image.html`? I'm trying with something like `{% for image in page.images %}` but no success. Can you help me? – Edmundo Santos Aug 06 '15 at 01:29
  • this is by far the best answer, because it reduces code (thus chances of error), uses the correct standard and, above all, works for more than just captions (i wanted to add a class to the image, for resizing and centering). – cregox Aug 28 '20 at 12:39
  • 2
    Is there a way to format the caption as markdown rather than just text? I want to hyperlink using markdown syntax but it doesn't work. Tried kramdown too – zeeshan khan Nov 01 '20 at 15:50
  • @zeeshankhan you can use the Liquid filter markdownify in the include as described here: https://talk.jekyllrb.com/t/rendering-markdown-inside-an-html-include/4186 – mostsignificant Oct 24 '21 at 14:45
105

The correct HTML to use for images with captions, is <figure> with <figcaption>.

There's no Markdown equivalent for this, so if you're only adding the occasional caption, I'd encourage you to just add that html into your Markdown document:

Lorem ipsum dolor sit amet, consectetur adipiscing elit...

<figure>
  <img src="{{site.url}}/assets/image.jpg" alt="my alt text"/>
  <figcaption>This is my caption text.</figcaption>
</figure>

Vestibulum eu vulputate magna...

The Markdown spec encourages you to embed HTML in cases like this, so it will display just fine. It's also a lot simpler than messing with plugins.

If you're trying to use other Markdown-y features (like tables, asterisks, etc) to produce captions, then you're just hacking around how Markdown was intended to be used.

bryanbraun
  • 3,025
  • 2
  • 26
  • 38
  • 11
    It's too bad that this answer hasn't gotten any attention--I really think it's the simplest and most semantically correct. I'm particularly distressed by all the answers suggesting formatting using tables, which just wreaks of 1990s mayhem. ;-) – sudo make install Jul 19 '17 at 22:12
  • I agree. However it seems not to be supported by Bitbucket yet. A pitty. – Boriel Nov 02 '17 at 11:07
  • 1
    I like the clever and simple answer provided by @Andrew but I have to go with this one given that is explicit, makes use of the appropriate HTML tags, and doesn't require too much typing. – Seanba Sep 11 '18 at 16:26
  • 1
    Thanks a lot, I am new to jekyll and didn't know markdown can be used with html. – Sambo Kim Feb 20 '20 at 04:59
  • I don't think `figcaption` should be the default for image captions. The definition of `figcaption` states: "While the content of the
    element is related to the main flow, its position is independent of the main flow, and if removed it should not affect the flow of the document." Which, I think, is often not the case.
    – Florian Walther Feb 28 '23 at 11:48
15

A slight riff on the top voted answer that I found to be a little more explicit is to use the jekyll syntax for adding a class to something and then style it that way.

So in the post you would have:

![My image](/images/my-image.png)

{:.image-caption}
*The caption for my image*

And then in your CSS file you can do something like this:

.image-caption {
  text-align: center;
  font-size: .8rem;
  color: lightgrey;

Comes out looking good!

koppor
  • 19,079
  • 15
  • 119
  • 161
Cory
  • 22,772
  • 19
  • 94
  • 91
10

There are two semantically correct solutions to this question:

  1. Using a plugin
  2. Creating a custom include

1. Using a plugin

I've tried a couple of plugins doing this and my favourite is jekyll-figure.

1.1. Install jekyll-figure

One way to install jekyll-figure is to add gem "jekyll-figure" to your Gemfile in your plugins group.

Then run bundle install from your terminal window.

1.2. Use jekyll-figure

Simply wrap your markdown in {% figure %} and {% endfigure %} tags.

You caption goes in the opening {% figure %} tag, and you can even style it with markdown!

Example:

{% figure caption:"Le logo de **Jekyll** et son clin d'oeil à Robert Louis Stevenson" %}
    ![Le logo de Jekyll](/assets/images/2018-08-07-jekyll-logo.png)
{% endfigure %}

1.3. Style it

Now that your images and captions are semantically correct, you can apply CSS as you wish to:

  • figure (for both image and caption)
  • figure img (for image only)
  • figcaption (for caption only)

2. Creating a custom include

You'll need to create an image.html file in your _includes folder, and include it using Liquid in Markdown.

2.1. Create _includes/image.html

Create the image.html document in your _includes folder :

<!-- _includes/image.html -->
<figure>
    {% if include.url %}
    <a href="{{ include.url }}">
    {% endif %}
    <img
        {% if include.srcabs %}
            src="{{ include.srcabs }}"
        {% else %}
            src="{{ site.baseurl }}/assets/images/{{ include.src }}"
        {% endif %}
    alt="{{ include.alt }}">
    {% if include.url %}
    </a>
    {% endif %}
    {% if include.caption %}
        <figcaption>{{ include.caption }}</figcaption>
    {% endif %}
</figure>

2.2. In Markdown, include an image using Liquid

An image in /assets/images with a caption:

This is [Jekyll](https://jekyllrb.com)'s logo :

{% include image.html
    src="jekyll-logo.png" <!-- image filename (placed in /assets/images) -->
    alt="Jekyll's logo" <!-- alt text -->
    caption="This is Jekyll's logo, featuring Dr. Jekyll's serum!" <!-- Caption -->
%}

An (external) image using an absolute URL: (change src="" to srcabs="")

This is [Jekyll](https://jekyllrb.com)'s logo :

{% include image.html
    srcabs="https://jekyllrb.com/img/logo-2x.png" <!-- absolute URL to image file -->
    alt="Jekyll's logo" <!-- alt text -->
    caption="This is Jekyll's logo, featuring Dr. Jekyll's serum!" <!-- Caption -->
%}

A clickable image: (add url="" argument)

This is [Jekyll](https://jekyllrb.com)'s logo :

{% include image.html
    src="https://jekyllrb.com/img/logo-2x.png" <!-- absolute URL to image file -->
    url="https://jekyllrb.com" <!-- destination url -->
    alt="Jekyll's logo" <!-- alt text -->
    caption="This is Jekyll's logo, featuring Dr. Jekyll's serum!" <!-- Caption -->
%}

An image without a caption:

This is [Jekyll](https://jekyllrb.com)'s logo :

{% include image.html
    src="https://jekyllrb.com/img/logo-2x.png" <!-- absolute URL to image file -->
    alt="Jekyll's logo" <!-- alt text -->
%}

2.3. Style it

Now that your images and captions are semantically correct, you can apply CSS as you wish to:

  • figure (for both image and caption)
  • figure img (for image only)
  • figcaption (for caption only)
Robin Métral
  • 3,099
  • 3
  • 17
  • 32
  • 2
    Just for completeness, if you want to use jekyll-figure you will have to add jekyll-figure to plugins in your _config.yml – Aleix Sanchis Mar 30 '20 at 00:59
3

You can try to use pandoc as your converter. Here's a jekyll plugin to implement this. Pandoc will be able to add a figure caption the same as your alt attribute automatically.

But you have to push the compiled site because github doesn't allow plugins in Github pages for security.

Ahmad
  • 69,608
  • 17
  • 111
  • 137
Hgtcl
  • 1,587
  • 2
  • 13
  • 14
3

Andrew's @andrew-wei answer works great. You can also chain a few together, depending on what you are trying to do. This, for example, gets you an image with alt, title and caption with a line break and bold and italics in different parts of the caption:

img + br + strong {margin-top: 5px; margin-bottom: 7px; font-style:italic; font-size: 12px; }
img + br + strong + em {margin-top: 5px; margin-bottom: 7px; font-size: 12px; font-style:italic;}

With the following <img> markdown:

![description](https://img.jpg "description")
***Image:*** *description*
Matthew Bennett
  • 303
  • 2
  • 12
3
<p align="center">
  <img alt="img-name" src="/path/image-name.png" width="300">
  <br>
    <em>caption</em>
</p>

That is the basic caption use. Not necessary to use an extra plugin.

Hasan Tezcan
  • 1,116
  • 1
  • 11
  • 23
0

Here's the simplest (but not prettiest) solution: make a table around the whole thing. There are obviously scaling issues, and this is why I give my example with the HTML so that you can modify the image size easily. This worked for me.

| <img src="" alt="" style="width: 400px;"/> |
| My Caption |
ndimhypervol
  • 479
  • 1
  • 5
  • 17
0

For Kramdown, you can use {:refdef: style="text-align: center;"} to align center

{:refdef: style="text-align: center;"}
![example](https://upload.wikimedia.org/wikipedia/en/a/a9/Example.jpg){: width="50%" .shadow}
{: refdef}
{:refdef: style="text-align: center;"}
*Fig.1: This is an example image. [Source](https://upload.wikimedia.org/wikipedia/en/a/a9/Example.jpg)*
{: refdef}

You need to add {::options parse_block_html="true" /} at the beginning of the post for this to work.

zeeshan khan
  • 376
  • 4
  • 12
0

This option might seem complicated on the surface, but it is not at all and solves other problems that the Jekyll markdown renderer (Kramdown) has. Basically this option changes the renderer for one made with python that is expandable, allowing you to instally extensions (there are a ton, markdown-captions for example) and expand it (it has an extension API).

  1. The first step is to define a custom markdown processor. You will have to add markdown: CustomProcessor to the _config.yml.

  2. Then, we have to create the CustomProcessor. Create a folder called _plugins and add a file called MyConverter.rb with this content:

class Jekyll::Converters::Markdown::CustomProcessor

    def initialize(config)

    end

    def matches(ext)
        ext =~ /^\.(md|markdown)$/i
    end

    def output_ext(ext)
        ".html"
    end

    def convert(content)

      puts "EXECUTED"

      md_path = "./_plugins/temp/temp.md"
      html_path = "./_plugins/temp/temp.html"
      
      File.write(md_path, content, mode: "w")
      system("python ./_plugins/MyConverter.py")

      content = File.read(html_path)
      content
    end
end

You will also need to create a folder temp inside plugins.

All that code does is to write all the content of the file we are processing to temp.md, call a python script, wait until it finishes, read temp.html, and return it as the output of the converter.

  1. Now it is time to create our converter in python. I have choosen to use Python-Markdown. It is easy to use and has a ton of extensions. To use the converter we have to create a file called MyConverter.py inside the _plugins folder and put this content inside:
import markdown

markdown_extensions = [
    'markdown_captions',
    'def_list',
    'nl2br',
    'tables',
    'codehilite',
    'fenced_code',
    'md_in_html',
    'attr_list'
]

md_file = open("./_plugins/temp/temp.md", "r")
md_string = md_file.read()
md_file.close()

html_string = markdown.markdown(md_string, extensions=markdown_extensions, extension_configs =extension_configs)

html_file = open("./_plugins/temp/temp.html", "w")
html_file.write(html_string)
html_file.close()

That code just loads the extensions, reads temp.md file, converts it to html and writtes it to temp.html. Using all those extensions should generate a similar output to the default jekyll markdown rendere. The only extension that is not bundled with python-markdown is markdown_captions, the one that does the magic. To install the python renderer and the extension you just have to run pip install Markdown markdown-captions.

That should do it, now your markdown is being translated by Python-Markdown. Some html elements my be different now (in my experience just a few) so maybe you have to make small changes in the css.

This is the css that I am using with the camptions:

figure{
  margin: 0px;
}

figcaption { 
  color: gray;
  font-size: 0.8rem;
}

The process tries to be as simple as possible to make it easy to understand and modify. I have described the process as well as I could remember. If anybody has a problem just leave a comment and I will update the answer.

edoelas
  • 317
  • 2
  • 13
0

Add the following config in the _config.yml file

# prose.io config
prose:
  rooturl: '_posts'
  media: 'img'
  ignore:
    - 404.html
    - LICENSE
    - feed.xml
    - _config.yml
    - /_layouts
    - /_includes
    - /css
    - /img
    - /js
  metadata:
    _posts:
      - name: "layout"
        field:
          element: "hidden"
          value: "post"
      - name: "title"
        field:
          element: "text"
          label: "Post title"
          placeholder: "Title"
          alterable: true
      - name: "subtitle"
        field:
          element: "textarea"
          label: "Subtitle"
          placeholder: "A description of your post."
          alterable: true
      - name: "date"
        field:
          element: "text"
          label: "Date"
          help: "Enter date of post."
          placeholder: "yyyy-mm-dd"
          alterable: true
      - name: "image"
        field:
          element: "text"
          label: "Image"
          help: "Add a thumbnail image to your post."
          placeholder: "Thumbnail"
          alterable: true
      - name: "published"
        field:
          element: "checkbox"
          label: "Publish"
          help: "Check to publish post, uncheck to hide."
0

You can use this javascript that automatically generates a figcaption from the image's alt.

You can add some css to make the bottom text look more realistic.

The same applies to markdown. Whatever text you put ![HERE]() appears below the image.

var images = document.getElementsByTagName("img");
for (var i = 0; i < images.length; i++) {
    var altText = images[i].getAttribute("alt");
    var figcaption = document.createElement("figcaption");
    figcaption.innerHTML = altText;
    images[i].insertAdjacentElement("afterend", figcaption);
}

var images = document.getElementsByTagName("img");
for (var i = 0; i < images.length; i++) {
    var altText = images[i].getAttribute("alt");
    var figcaption = document.createElement("figcaption");
    figcaption.innerHTML = altText;
    images[i].insertAdjacentElement("afterend", figcaption);
}
<img src="https://www.w3schools.com/tags/img_girl.jpg" alt="Girl in a jacket">
Henry
  • 154
  • 10