Blog templates

Last updated:
APPLICABLE PRODUCTS
  • Marketing Hub
    • Professional or Enterprise
  • CMS Hub
    • Starter, Professional, or Enterprise

HubSpot blogs consist of blog listing pages and the individual blog posts. In addition to listing the individual blog posts, the blog listing template is also used for rendering the author and tag listing pages. You can either create a single template to render all listing and blog post pages, or you can create two separate templates. 

Below, learn about blog template markup, template components, and customization options.

Create a shared template for the listing and post pages

To create one template that renders the listing and post pages, add the templateType: blog annotation to the top of your template file. When using one template to render both, you'll use an if statement that evaluates whether the user is looking at a listing page or an individual post. If you are using the drag and drop design manager layouts, this if statement is built into the UI of blog content module buttons.

By using the if is_listing_view statement, you can write your post and listing code separately.

{% if is_listing_view %} Markup for blog listing template {% else %} Markup for blog post template {% endif %}

Create separate listing and post templates

Alternatively, you can choose to have separate templates for blog post and listing pages which can help make your code cleaner and easier to read as a developer, while making the templates easier to select for content creators. Rather than using the templateType: blog annotation at the top of the one template, include the following annotations at the top of your two templates:

  • Blog post template: templateType: blog_post
  • Blog listing template: templateType: blog_listing
template-annotation-blog-listing

When building separate post and listing templates, the is_listing_view check is not required. Instead, you'll manually select separate templates within the account's blog settings.

You can also migrate an existing unified blog template to be either a blog post template or blog listing template.

Listing page templates

The templateType: blog_listing annotation makes the template available for selection under blog settings specifically for the listing view. With this template type, content creators can also edit the listing page within the page editor. By also including drag and drop areas in the template, modules can be added and removed in the page editor like they can for other CMS pages. Check out the CMS boilerplate blog templates to see examples of including drag and drop areas.

The listing of posts is generated by a for loop that iterates through your blog posts. contents is a predefined sequence of content that contains all the posts contained in that blog.

{% for content in contents %} <div class="post-item"> Post item markup that renders with each iteration. </div> {% endfor %}

It's recommended to make all text strings on your blog listing template controlled by fields. This makes it easier to create multilingual blogs and gives content creators more control.

Create a blog listing module

You can enable content creators to place modules on the perimeter of the blog listing content, such as on the sides, or above and below. To enable this, it's recommended to create a blog listing module that uses a blog listing for loop. Check out the CMS boilerplate blog listing module for an example. 

While HubSpot provides blog settings for showing summaries and using featured images, you can also build these features into your module. This enables content creators to set these features within the page editor, rather than blog settings.

Blog author, tag, and simple listing pages

In additional to the blog post and blog listing pages, HubSpot blogs also have pages for blog authors, blog post tags, and simple listing pages. These additional pages use the same template as the blog listing page to render their content.

Because the listing page template is also shared by the blog author, tag, and simple listing page, updates published to the template will also apply to those pages.

To configure the layout of these pages individually, you can use if statements to conditionally render content for each type of page.

If blog_author

Within the standard HubSpot blog listing markup, there is an if blog_author statement. This statement evaluates as true when viewing an author's page, which lists the posts published by the author. The boilerplate template includes the author's name, bio, and social media accounts.

{% if blog_author %} <div class="blog-header"> <div class="blog-header__inner"> {% if blog_author.avatar %} <div class="blog-header__author-avatar" style="background-image: url('{{ blog_author.avatar }}');"></div> {% endif %} <h1 class="blog-header__title">{{ blog_author.display_name }}</h1> <h4 class="blog-header__subtitle">{{ blog_author.bio }}</h4> {% if blog_author.has_social_profiles %} <div class="blog-header__author-social-links"> {% if blog_author.website %} <a href="{{ blog_author.website }}" target="_blank"> {% icon name="link" style="SOLID" width="10" %} </a> {% endif %} {% if blog_author.facebook %} <a href="{{ blog_author.facebook }}" target="_blank"> {% icon name="facebook-f" style="SOLID" width="10" %} </a> {% endif %} {% if blog_author.linkedin %} <a href="{{ blog_author.linkedin }}" target="_blank"> {% icon name="linkedin-in" style="SOLID" width="10" %} </a> {% endif %} {% if blog_author.twitter %} <a href="{{ blog_author.twitter }}" target="_blank"> {% icon name="twitter" style="SOLID" width="10" %} </a> {% endif %} </div> {% endif %} </div> </div> {% else %}

If tag

You can use an if tag statement to only render code on a blog topic listing page, which visitors can see when clicking a blog topic on your site. The example below is a snippet that uses the page title variable to automatically print the tag name at the top of a tag listing page.

{% if tag %} <h3>Posts about {{ page_meta.html_title|split(" | ")|last }}</h3> {% endif %}

If not simple_list_page

There are two types of blog listing pages that can be rendered to display blog post listings: the regular listing page, and a simple listing page:

  • The regular listing iterates through the number of posts specified by the post listing blog setting and paginates accordingly.
  • A simple listing is a listing of all your posts and does not support pagination. The simple listing is not affected by the post limit blog setting and generally just contains links to the most recent 200 blog posts. The address of your simple listing page is the URL for your blog with /all added to the end of the path.

You can use an if not simple_list_page statement to determine what to render in a simple versus regular listing. A simplified version of this statement is shown below.

Note that the if statement uses reverse logic, which means that the else defines the simple listing view. Optionally, you could use an unless statement instead.
{% if not simple_list_page %} Iterated post markup for regular listing {% else %} <h2 class="post-listing-simple"><a href="{{content.absolute_url}}">{{ content.name }}</a></h2> {% endif %}

Listing pagination

Blog listing pages have auto-generated pagination. Your listing template can include logic to allow visitors to easily pages through your blog posts. The boilerplate blog accomplishes simple, number pagination through the following markup:

{% if contents.total_page_count > 1 %} <div class="blog-pagination"> {% set page_list = [-2, -1, 0, 1, 2] %} {% if contents.total_page_count - current_page_num == 1 %}{% set offset = -1 %} {% elif contents.total_page_count - current_page_num == 0 %}{% set offset = -2 %} {% elif current_page_num == 2 %}{% set offset = 1 %} {% elif current_page_num == 1 %}{% set offset = 2 %} {% else %}{% set offset = 0 %}{% endif %} <a class="blog-pagination__link blog-pagination__prev-link {{ "blog-pagination__prev-link--disabled" if !last_page_num }}" href="{{ blog_page_link(last_page_num) }}"> {% icon name="chevron-left" style="SOLID", width="13", no_wrapper=True %} Prev </a> {% for page in page_list %} {% set this_page = current_page_num + page + offset %} {% if this_page > 0 and this_page <= contents.total_page_count %} <a class="blog-pagination__link blog-pagination__number-link {{ "blog-pagination__link--active" if this_page == current_page_num }}" href="{{ blog_page_link(this_page) }}">{{ this_page }}</a> {% endif %} {% endfor %} <a class="blog-pagination__link blog-pagination__next-link {{ "blog-pagination__next-link--disabled" if !next_page_num }}" href="{{ blog_page_link(current_page_num + 1) }}"> Next {% icon name="chevron-right" style="SOLID", width="13", no_wrapper=True %} </a> </div> {% endif %}

Boilerplate markup

Below, view the boilerplate markup for the blog post and blog listing page templates. You can also view this markup in the CMS boilerplate on GitHub, as listed in each section.

Post template markup

All blog posts in a blog are generated by a single blog template. Content is a predefined object of data that contains information about the requested blog post. Boilerplate posts are rendered with the following markup:

<div class="content-wrapper"> <div class="blog-post"> <h1>{{ content.name }}</h1> <div class="blog-post__meta"> <a href="{{ blog_author_url(group.id, content.blog_post_author.slug) }}"> {{ content.blog_post_author.display_name }} </a> <div class="blog-post__timestamp"> {{ content.publish_date_localized }} </div> </div> <div class="blog-post__body"> {{ content.post_body }} </div> {% if content.tag_list %} <div class="blog-post__tags"> {% icon name="tag" style="SOLID" %} {% for tag in content.tag_list %} <a class="blog-post__tag-link" href="{{ blog_tag_url(group.id, tag.slug) }}">{{ tag.name }}</a>{% if not loop.last %},{% endif %} {% endfor %} </div> {% endif %} </div> <div class="blog-comments"> {% module "blog_comments" path="@hubspot/blog_comments", label="Blog Comments" %} </div> </div>

Blog post author information is also available within the content data.

<img alt="{{ content.blog_post_author.display_name }}" src="{{ content.blog_post_author.avatar }}"> <h3>Written by <a class="author-link" href="{{ blog_author_url(group.id, content.blog_post_author.slug) }}">{{ content.blog_post_author.display_name }}</a></h3> <p>{{ content.blog_post_author.bio }}</p> {% if content.blog_post_author.has_social_profiles %} <div class="hs-author-social-section"> <div class="hs-author-social-links"> {% if content.blog_post_author.facebook %} <a href="{{ content.blog_post_author.facebook }}" target="_blank" class="hs-author-social-link hs-social-facebook">Facebook</a> {% endif %} {% if content.blog_post_author.linkedin %} <a href="{{ content.blog_post_author.linkedin }}" target="_blank" class="hs-author-social-link hs-social-linkedin">LinkedIn</a> {% endif %} {% if content.blog_post_author.twitter %} <a href="{{ content.blog_post_author.twitter }}" target="_blank" class="hs-author-social-link hs-social-twitter">Twitter</a> {% endif %} {% if content.blog_post_author.google_plus %} <a href="{{ content.blog_post_author.google_plus }}?rel=author" target="_blank" class="hs-author-social-link hs-social-google-plus">Google+</a> {% endif %} </div> </div> {% endif %}

Listing template markup

The boilerplate blog listing page contents for loop is rendered with the following markup:

{% for content in contents %} {# On the blog homepage the first post will be featured above older posts #} {% if (loop.first && current_page_num == 1 && !topic) %} <div class="blog-index__post blog-index__post--large"> <a class="blog-index__post-image blog-index__post-image--large" {% if content.featured_image %} style="background-image: url('{{ content.featured_image }}')"; {% endif %} href="{{ content.absolute_url }}"></a> <div class="blog-index__post-content blog-index__post-content--large"> <h2><a href="{{ content.absolute_url }}">{{ content.name }}</a></h2> {{ content.post_list_content }} </div> </div> {% else %} <div class="blog-index__post blog-index__post--small"> <a class="blog-index__post-image blog-index__post-image--small" {% if content.featured_image %} style="background-image: url('{{ content.featured_image }}')"; {% endif %} href="{{ content.absolute_url }}"></a> <div class="blog-index__post-content blog-index__post-content--small"> <h2><a href="{{ content.absolute_url }}">{{ content.name }}</a></h2> {{ content.post_list_content|truncatehtml(100) }} </div> </div> {% endif %} {% endfor %}

Was this article helpful?
This form is used for documentation feedback only. Learn how to get help with HubSpot.