Posted by

Show Latest Blog Posts on Any HubSpot Page with HubL blog_recent_posts

A common request on HubSpot sites: "show our three latest articles on the homepage." You do not need a third-party widget or a JavaScript call to the API. HubL can read your blog directly at render time, so the list is server-rendered, fast, and indexable.

The basic idea

HubL provides blog functions that return posts from a HubSpot blog. The most useful one for this job is blog_recent_posts. You pass the blog's ID (or the name default for the primary blog) and a limit, then loop over the result like any other list.

{% set posts = blog_recent_posts('default', 3) %}

{% for post in posts %}
  <article class="post-card">
    <h3><a href="{{ post.absolute_url }}">{{ post.name }}</a></h3>
    <p>{{ post.post_summary|striptags|truncatewords(25) }}</p>
  </article>
{% endfor %}

Check the exact function signature and the post properties available in HubSpot's HubL reference before you ship, because HubSpot adds and renames properties over time. If you use a blog other than the default one, find its numeric ID in the blog's settings and pass that instead of 'default'.

Put it in a custom module

Hard-coding the blog ID and the count in a template makes the block hard to reuse. A small custom module with three fields (heading, number of posts, blog) keeps editors in control.

fields.json

[
  {
    "name": "heading",
    "label": "Heading",
    "type": "text",
    "default": "Latest articles"
  },
  {
    "name": "post_count",
    "label": "Number of posts",
    "type": "number",
    "default": 3,
    "min": 1,
    "max": 12
  },
  {
    "name": "blog",
    "label": "Blog",
    "type": "blog"
  }
]

module.html

{% set blog_id = module.blog | default('default') %}
{% set posts = blog_recent_posts(blog_id, module.post_count) %}

<section class="latest-posts">
  <h2>{{ module.heading }}</h2>

  {% if posts %}
    <ul class="latest-posts__list">
      {% for post in posts %}
        <li class="latest-posts__item">
          <a href="{{ post.absolute_url }}">{{ post.name }}</a>
          <time datetime="{{ post.publish_date|format_datetime('yyyy-MM-dd') }}">
            {{ post.publish_date|format_date('medium') }}
          </time>
        </li>
      {% endfor %}
    </ul>
  {% else %}
    <p>No articles yet.</p>
  {% endif %}
</section>

Test the blog field's returned value in your own portal (for example with {{ module.blog }} in the editor preview), since field value shapes can differ from what you expect. The fallback to 'default' keeps the module working if the field is left empty.

Practical tips

  • Always handle the empty case. A new blog or a wrong ID returns no posts. An {% if posts %} check avoids an empty heading with nothing below it.
  • Use a real heading level. Match the heading to the page outline (the module above uses h2). Do not skip levels just for styling.
  • Add the date with a time element. It gives browsers and search engines a machine-readable date.
  • Keep the limit small. Each extra post adds render work and page weight. Three to six is usually enough for a teaser block.
  • Mind the cache. HubSpot caches rendered pages, so a newly published post can take a short time to appear on pages that embed the list.
  • Look for the tag-based variant. HubSpot also documents a related function for posts by tag, handy for "related reading" blocks.

Debugging when nothing shows

  1. Print the blog ID you pass: {{ blog_id }}.
  2. Print the result with {{ posts|pprint }} in the editor preview (remove it afterwards).
  3. Confirm the posts are published, not drafts or scheduled for later.
  4. Confirm you are previewing the page on a domain connected to the portal.

Summary

blog_recent_posts lets you reuse your blog content anywhere on a HubSpot site without extra scripts. Wrap it in a custom module with a heading, a count and a blog field, guard against empty results, and output only the fields you need. Always verify function names and post properties against HubSpot's current HubL documentation, because they can change.