Post Basics

Blogs, news, events, portfolios. When you have a set of pages with the same design you need posts.

Posts are collections of pages that share the same template. Build the page once and future posts use it as a template. This page is known as the detail template. Future changes to the design of the detail template will change the design of all your posts.

Pages vs. Posts

Pages are one-off content with unique layouts:

  • Homepage
  • About page
  • Contact page
  • Landing pages

Posts are repeating content with shared structure:

  • Blog articles (all look similar)
  • News updates (same format)
  • Events (same template, different data)
  • Portfolio projects (consistent layout)

If you're creating many items with the same structure, use posts.

Creating a Post Type

To create a new post go to the content area and click "Create Post Type" at the bottom of the sidebar. Posts require a paid plan and creating them needs the Developer or Owner role.

The first thing you should do after creating your post type is to edit its detail page. Click the edit link next to the post title. All posts are rendered based on this page. It's ok to change it in the future but you need to take care with content changes. Removing a field from your post detail removes that content from every post.

Post URLs

Each post gets a unique URL: /blog/my-post-title

The title becomes the slug automatically. Rename the post to change its URL (there's no separate slug field). If you need to redirect previous post URLs you can use the redirects tool.

Displaying Posts

Use the {{posts}} helper in any block template.

{{#each (posts "blog" 5)}}
  <article>
    <h2><a href="{{url}}">{{headline}}</a></h2>
    <time>{{formatDate publishedDate "short"}}</time>
    <p>{{summary}}</p>
  </article>
{{/each}}

Which Fields Can I Use?

Two kinds. The five built-in fields every post has: title, id, url, slug, and publishedDate (camelCase). And your own fields from the detail page blocks, in the usual kebab-case.

The examples here assume you added a Headline and a Summary field, so they use {{headline}} and {{summary}}.

Syntax: {{posts "postTypeName" limit}}

  • First parameter: your post type name (case-insensitive)
  • Second parameter (optional): number of posts to show

Show all posts by omitting the limit: {{posts "blog"}}

Filtering & Sorting Posts

The {{posts}} helper supports server-side filtering, sorting, and limiting. This is useful for:

  • Showing related posts on a detail page

  • Creating filtered category pages (e.g. /tutorials/ showing only tutorial posts)

  • Featuring specific posts on the homepage

Filter by Field

Filter posts where a field equals a specific value:

{{!-- Show only posts where category is "tutorials" --}}
{{#each (posts "blog" category="tutorials")}}
  <article>{{title}}</article>
{{/each}}

{{!-- Multiple filters: category AND author --}}
{{#each (posts "blog" category="tutorials" author="Tom")}}
  <article>{{title}}</article>
{{/each}}

Filtering is case-insensitive for text fields.

Sort Posts

Sort by any field in ascending or descending order:

{{!-- Newest first --}}
{{#each (posts "blog" sort="publishedDate:desc")}}

{{!-- Oldest first --}}
{{#each (posts "blog" sort="publishedDate:asc")}}

{{!-- Alphabetical by title --}}
{{#each (posts "blog" sort="title:asc")}}

sort="fieldName:direction" where direction is asc or desc. Sorting automatically detects field types (dates, numbers, text).

Limit Results

Use the limit option (or the legacy positional argument):

{{!-- These are equivalent --}}
{{#each (posts "blog" limit=5)}}
{{#each (posts "blog" 5)}}

Exclude Posts

Exclude a specific post by ID, useful for related posts:

{{!-- On a blog detail page, show related posts but exclude the current one --}}
{{#each (posts "blog" exclude=post.id limit=3)}}
  <a href="{{url}}">{{title}}</a>
{{/each}}

Combining Options

Mix and match any options:

On a post's detail page the injected post context only exposes title, id, and slug, so exclude=post.id works, but to filter by a field like category you pass a literal value (category="news").

{{!-- Related posts: exclude the current one, newest first, max 3 --}}
{{#each (posts "blog" exclude=post.id sort="publishedDate:desc" limit=3)}}
  <article>
    <a href="{{url}}">{{title}}</a>
  </article>
{{/each}}

Example: Category Page

Create a page at /tutorials/ that shows only tutorial posts:

<section>
  <h1>Tutorials</h1>

  <div class="grid grid-cols-3 gap-6">
    {{#each (posts "blog" category="tutorials" sort="publishedDate:desc")}}
      <article>
        <img src="{{featured-image}}" alt="">
        <h2><a href="{{url}}">{{title}}</a></h2>
        <p>{{summary}}</p>
      </article>
    {{/each}}
  </div>
</section>

This is a regular page, not a post type index. The posts are filtered at build time.

Example: Featured Posts on Homepage

Add a "featured" field (Toggle or text) to your blog posts and set it to "true" for posts you want to highlight.

<section>
  <h2>Featured Articles</h2>

  {{#each (posts "blog" featured="true" sort="publishedDate:desc" limit=3)}}
    <article>
      <h3><a href="{{url}}">{{title}}</a></h3>
      <p>{{summary}}</p>
    </article>
  {{/each}}
</section>

Automatic Post Data

Every post automatically has:

  • title: post title

  • url: full URL to the post

  • slug: URL-friendly slug

  • id: the post's unique id (used with exclude)

  • publishedDate: publication date

Plus any fields from your detail page blocks.

Multiple Post Types

Use different post types on the same page:

<section>
  <h2>Latest Articles</h2>
  {{#each (posts "blog" 3)}}
    <article>{{headline}}</article>
  {{/each}}
</section>

<section>
  <h2>Upcoming Events</h2>
  {{#each (posts "events" 5)}}
    <article>{{headline}}</article>
  {{/each}}
</section>

Post Index

In addition to the detail template posts have an index page. This page will be available at yoursite.gpto.site/<post-type>/ (for example, /blog/). This page can be paginated, filtered and searched. Learn more in Posts Index.

Thanks — your message has been sent.