27

In my index of blog posts I'd like to grab the first image from the post to display it in the index using just liquid so it works on github pages.

I have a feeling split is the way to go, but I'm not good with liquid.

I'd like to be able to get the image url and put it in a variable to style it.

The ideal solution would be something like:

{% for post in site.posts %}
    <li>
      <a href="{{ post.url }}">{{post.content | first_image}}</a>
    </li>
  {% endfor %}

Any ideas?

David Silva Smith
  • 11,498
  • 11
  • 67
  • 91

5 Answers5

28

You can define a custom variable to your Front Matter called "image", so it's going to work like Wordpress's posts Featured Image:

---
image: featured-image.jpg
---

Note to remember where is your image saved. In my case, I created a directory called "imagens" (PT-BR here). Then, go to your index.html and add the image to your template, wherever you want. In my site it looks like this:

<ul class="post-list">
    {% for post in site.posts %}
      <li>
        <h2>
          <a class="post-link" href="{{ post.url | prepend: site.baseurl }}">{{ post.title }}</a>
        </h2>
        <span class="post-meta">{{ post.date | date: "%b %-d, %Y" }},</span>
        <span class="post-meta">por</span>
        <span class="post-meta">{{ post.author }}</span>
      </li>

      //IMAGE
      <img src="{{ site.baseurl }}/imagens/{{ post.image }}">
      //IMAGE


      {{ post.excerpt }}
      <a class="btn btn-default" href="{{ post.url | prepend: site.baseurl }}">Continuar lendo</a>
    {% endfor %}
  </ul>

That's it.

Júlio Maia
  • 381
  • 3
  • 4
19

Some solutions to your problem :

1 - Use the Post Excerpt tag Documentation is here

Your post :

---
layout: post
title: Testing images
---
## Title
Intro Text
![Image alt]({{ site.baseurl }}/assets/img/image.jpg "image title")
More intro text

Some more text blah !

Your template :

<ul>
  {% for post in site.posts %}
    <li>
      <a href="{{ post.url }}">{{ post.title }}</a>
      {{ post.excerpt }}
    </li>
  {% endfor %}
</ul>

As your image tag appears before the excerpt_separator (\n\n = two newlines) it will be in the post excerpt.

2 - Use your post's Yaml front matter to store your image's datas

Post :

---
layout: post
title: Testing images

images:

  - url: /assets/img/cypripedium-calceolum.jpg
    alt: Cypripedium Calceolum
    title: Cypripedium Calceolum

  - url: /assets/img/hello-bumblebee.jpg
    alt: Hello bumblebee !
    title: Hello bumblebee !
---

Intro here yo ! <-- THIS IS THE EXCERPT

Post body begin, and first image not in excerpt
{% assign image = page.images[0] %} <-- first element of the array is zero
{% include image.html image=image %}

Some more text blah !
{% assign image = page.images[1] %}
{% include image.html image=image %}

_includes/image.html (centralized in an include for standardization, but can be in the template) :

<img src="{{ site.baseurl }}{{ include.image.url }}" alt="{{ include.image.alt }}" title="{{ include.image.title }}">

The index page :

<ul class="posts">
  {% for post in site.posts %}
    <li>
      <span class="post-date">{{ post.date | date: "%b %-d, %Y" }}</span>
      <a class="post-link" href="{{ post.url | prepend: site.baseurl }}">{{ post.title }}</a>
      {{ post.excerpt }}
      {% assign image = post.images[0] %}
      {% include image.html image=image %}
    </li>
  {% endfor %}
</ul>
David Jacquel
  • 51,670
  • 6
  • 121
  • 147
  • Thanks for the comprehensive answer. I should have mentioned I'm aware of excerpt and as you pointed out that works if the image comes within the excerpt. Your second suggestion answers the question, but I'm hoping for something I can put in the template that pulls out the first image. – David Silva Smith Aug 23 '14 at 20:16
17

Got it to work. Not sure how it will scale, but this liquid code loops through all the posts and grabs the source for the first image from a post and displays that post. I tested it with multiple images and it works as expected.

<ul>
  {% for post in site.posts %}
    <li>

      {% assign foundImage = 0 %}
      {% assign images = post.content | split:"<img " %}
      {% for image in images %}
        {% if image contains 'src' %}

            {% if foundImage == 0 %}
                {% assign html = image | split:"/>" | first %}
                <img {{ html }} />
                {% assign foundImage = 1 %}
            {% endif %}
        {% endif %}
      {% endfor %}

      <a href="{{ post.url }}">{{ post.title }}</a>
    </li>
  {% endfor %}
</ul>
David Silva Smith
  • 11,498
  • 11
  • 67
  • 91
  • 5
    This is nice, thanks. I used it in my site but replaced the `foundImage` logic with a `{% break %}` from the loop. Saves some iterations for posts with many images. – jarvisteve Mar 21 '15 at 17:52
  • thanks for the tip! I didn't know about [break](http://www.rubydoc.info/github/Shopify/liquid/Liquid/Break). – David Silva Smith Mar 23 '15 at 00:49
  • This approach worked for me as well except I'd prefer to take only the `src` attribute of the image rather than all the attributes. How would I do that rather than just ``? – user1475412 Feb 15 '16 at 21:59
  • 1
    I ended up not using this code and going with image: xxx.jpg in the frontmatter. But if you use the code above essentially you'd do another split to look for src=" and then grab the text to the other " by piping it to first like in the {% assign html = image | split:"/>" | first %} case. – David Silva Smith Feb 16 '16 at 02:06
  • Very nice tip, this is exactly what was needed. – Hesham Eraqi Jan 15 '19 at 20:41
4

If you just need the image URL instead of the whole thing in img tag, you can use the following method.

Install Liquid filter match_regex:

gem install match_regex

Then add it to your Jekyll config:

plugins:
  - match_regex

Create a capture snippet in your template:

{% capture post_first_image %}
  {% assign hero_image = page.content | match_regex: '<img .*?src="([^"]+)"' %}
  {% if hero_image == nil %}
    {% assign hero_image = "/placeholder-image.png" | prepend: site_base %}
  {% endif %}
  {{ hero_image }}
{% endcapture %}

Template:

<meta property="og:image" content="{{ post_first_image | strip }}">

You can simply remove the if condition if you don't need placeholder image.

sparanoid
  • 1,438
  • 1
  • 12
  • 10
  • Nice. It works in my ubuntu but not in github. Not sure why. – ihsanberahim Nov 16 '20 at 09:27
  • 1
    @ihsanberahim plugins supported by GitHub Pages are whitelisted: https://docs.github.com/en/github/working-with-github-pages/about-github-pages-and-jekyll#plugins – sparanoid Mar 26 '21 at 06:19
2

I've taken David's answer and found a way to get just the src attribute from the img tag.

    {% assign foundImage = 0 %}
    {% assign images = post.content | split:"<img " %}
    {% for image in images %}
      {% if image contains 'src' %}

          {% if foundImage == 0 %}
              {% assign html = image | split:"/>" | first %}
              {% assign tags = html | split:" " %}
              {% for tag in tags %}
                {% if tag contains 'src' %}
                  <img {{ tag }} />
                {% endif %}
              {% endfor %}
              {% assign foundImage = 1 %}
          {% endif %}
      {% endif %}
    {% endfor %}
Alex Palcuie
  • 4,434
  • 3
  • 22
  • 27