# Trying to understand routes using filter, data, and template together

**URL:** <https://forum.ghost.org/t/trying-to-understand-routes-using-filter-data-and-template-together/16918>\
**Category:** Themes\
**Created:** [September 5, 2020, 3:16am UTC](https://forum.ghost.org/t/trying-to-understand-routes-using-filter-data-and-template-together/16918 "2020-09-05T03:16:33Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![calvinpark](https://sea1.discourse-cdn.com/flex015/user_avatar/forum.ghost.org/calvinpark/32/4882_2.png) [@calvinpark](https://forum.ghost.org/u/calvinpark)\
**Post date:** [September 5, 2020, 3:16am UTC](https://forum.ghost.org/t/trying-to-understand-routes-using-filter-data-and-template-together/16918/1 "2020-09-05T03:16:33Z")

</div>

In the example given in this doc, I’m having trouble understanding how `filter` and `data` work together.

> **[Ghost Themes - Dynamic URLs & Routing](https://ghost.org/docs/themes/routing/)**
>
> Build dedicated URL structures using Ghost's dynamic routing system, for custom homepages, podcasts, categories and more.

```auto
collections:
  /portfolio/:
    permalink: /work/{slug}/
    template: work
    filter: primary_tag:work
    data: tag.work

```

Isn’t `filter` providing all posts with `primary_tag:work` to `work.hbs`?  
But at the same time, `data` is also providing all posts with `work` tag.  
Aren’t they both doing the same things?

---

<div class="post-metadata">

**Author:** ![vikaspotluri123](https://sea1.discourse-cdn.com/flex015/user_avatar/forum.ghost.org/vikaspotluri123/32/24_2.png) [@vikaspotluri123](https://forum.ghost.org/u/vikaspotluri123)\
**Post date:** [September 5, 2020, 5:31am UTC](https://forum.ghost.org/t/trying-to-understand-routes-using-filter-data-and-template-together/16918/2 "2020-09-05T05:31:26Z")

</div>

It’s been a while since I’ve done routing, but if memory serves

**Filter** - tells Ghost what posts belong to this collection. Also, the post data will be available in the `{{#post}}` context (I think) in the `work.hbs` template

**Data** - additional data to be fetched when rendering - in the case of `tag.work`, the `{{#tag}}` context will contain the `work` tag data

---

<div class="post-metadata">

**Author:** ![jeff](https://sea1.discourse-cdn.com/flex015/user_avatar/forum.ghost.org/jeff/32/4900_2.png) [@jeff](https://forum.ghost.org/u/jeff)\
**Post date:** [September 5, 2020, 6:16am UTC](https://forum.ghost.org/t/trying-to-understand-routes-using-filter-data-and-template-together/16918/3 "2020-09-05T06:16:50Z")

</div>

If you look in your Ghost admin at the **TAG** pages, you will see one for `work` - You can edit this page - e.g. setting a **feature\_image** , **meta-data** etc.

The in the `routes.yaml` you tell Ghost to use the `data` from this `tag.work` page for the collection index.

In your template, you can access the details from the tag.work page using the `{{#tag}} ` helper, for example:

```
{{!-- custom-work.hbs --}}
{{#tag}}
<header class="site-archive-header">
  {{> header-background background=feature_image }}

```

The info from the page specified in `data:` is available - i.e. the feature\_image comes from that page. As you used `tag.work` you have to use the `{{#tag}}` helper - if you chose a page or post you would use the `{{#post}}` helper.

Clear as mud? 🙂

---

<div class="post-metadata">

**Author:** ![jeff](https://sea1.discourse-cdn.com/flex015/user_avatar/forum.ghost.org/jeff/32/4900_2.png) [@jeff](https://forum.ghost.org/u/jeff)\
**Post date:** [September 5, 2020, 6:20am UTC](https://forum.ghost.org/t/trying-to-understand-routes-using-filter-data-and-template-together/16918/4 "2020-09-05T06:20:31Z")

</div>

another way to think about it:

1. the `data: tag.work` supplies the information for the collection index page itself
2. the `filter: primary_tag:work` supplies the list of articles in this collection

---

<div class="post-metadata">

**Author:** ![calvinpark](https://sea1.discourse-cdn.com/flex015/user_avatar/forum.ghost.org/calvinpark/32/4882_2.png) [@calvinpark](https://forum.ghost.org/u/calvinpark)\
**Post date:** [September 5, 2020, 7:03pm UTC](https://forum.ghost.org/t/trying-to-understand-routes-using-filter-data-and-template-together/16918/5 "2020-09-05T19:03:31Z")

</div>

I think this data model is poorly structured and causing confusions. Proposed solution at the bottom.

Here’s what I’ve figured out after a full day of research.

1. A dynamic page needs a `template` rather than a `post` or a `page` which are typically static (though they too can be made dynamic)

2. A template needs  
a. title  
b. header image  
c. excerpt  
d. posts to display

3. So this may make sense

4. An equivalent model using a variable would be

5. A disadvantage of this model is that the template data is stored in `routes.yaml` which isn’t easy to modify. Where should the template data be stored that’s easy to modify?

6. What’s worse, the existing `post`, `page`, and `tag` objects do not have fields for storing `posts_filter` or `posts_count` information, which are needed in a template. As a result, now we specify the `data` which points to a new variant of `post`, `page`, or a `tag` **AND** posts filter **AND** posts count.

7. And to make it even more confusing, `data`, `filter`, and `limit` are on the same level as `template`, arriving at

I’m not mad, I’m just disappointed.

**Here is the proposed solution.**

1. Create a new type called `list` which holds

2. `routes.yaml` should use it like this

This way, `routes.yaml` focuses on routing and `list` object focuses on generating a dynamic page.

Thank you for attending my TEDx.

---

<div class="post-metadata">

**Author:** ![jeff](https://sea1.discourse-cdn.com/flex015/user_avatar/forum.ghost.org/jeff/32/4900_2.png) [@jeff](https://forum.ghost.org/u/jeff)\
**Post date:** [September 6, 2020, 3:15am UTC](https://forum.ghost.org/t/trying-to-understand-routes-using-filter-data-and-template-together/16918/6 "2020-09-06T03:15:42Z")

</div>

Firstly, some taxonomy :)

- Your analysis appears to use `Template` when, perhaps you mean `Index` (aka `Archive` in some places in Ghost).
- For me:
  - `Template` - a Handlebar template `.hbs` - used to render a Route Index, a Collection Index, a single Post, a Tag Index etc.
  - `Index` - a page that fronts a set of [Post|Page|Tag|Author] - needs a Template to render
    - an Index needs it’s own attributes as well as a Paginated set of children

  - `Route` - a set of Articles, fronted by a paginated Index.
    - Articles can appear in many Routes

  - `Collection` - a set of Articles, fronted by a paginated Index.
    - Collections are a structural / semantic sub-division of Articles
    - Articles only appear in ONE Collection
    - The Filter on a Collection is a first-order attribute - it _defines_ the Collection

So - I actually prefer the way is currently is because (IMHO):

- KISS - current approach is simple to understand and use.
- it fits the style/approach/philosophy/tools used to write Ghost - it’s important that Ghost has an internally consistent approach.
- Collections are a Very-Special-Case - used to create structural semantic divisions in Articles. I think this is why the rules are simple (and hence restrictive).
- You have moved the Collection-Filter into Data, but:
  - Filter _defines_ the Collection - it is a material first-order attribute of the Collection - it’s not data to be tweaked, like Title, Accent Colour etc.
  - Permalink is also structural - remember that song? Don’t go breaking my Links
  - If you want to use Filter in a Template - you should probably be using a Route.

- Safety - I want to be able to change the Title, Image etc. safely using the Admin editor
  - this limits the scope of the change
  - routes.yaml is sensitive and affects the entire Site.
  - Much safer for users of a custom Theme

My main bugbear with the setup is the inconsistency between the Admin editor and the model - for example:

- create a Template called custom-authors.hbs
- create a static Page called /authors/ - and select the custom-authors.hbs as the template
- on the /authors/ Page set the Title → you can now render this {{title}} in your Template.
- on the /authors/ Page add some Content - you might think that you can use `{{content}}` - the Content is actually in `{{excerpt}}` - meh… this is just something you have to discover.

These pages are a great resource - you gotta love the Ghost docs! [Context Overview: Ghost Themes - Documentation](https://ghost.org/docs/api/v3/handlebars-themes/context/)

---

<div class="post-metadata">

**Author:** ![calvinpark](https://sea1.discourse-cdn.com/flex015/user_avatar/forum.ghost.org/calvinpark/32/4882_2.png) [@calvinpark](https://forum.ghost.org/u/calvinpark)\
**Post date:** [September 6, 2020, 4:28am UTC](https://forum.ghost.org/t/trying-to-understand-routes-using-filter-data-and-template-together/16918/7 "2020-09-06T04:28:26Z")

</div>

Thank you for the detailed response. I really appreciate it.

`Index` does sound like what I’m looking for. I saw the doc for it but I don’t understand the “Contexts & Templates” section. It talks about how to detect it, but how do I have a custom URL to be an index? For example, how do I set `/archive/` to use `Index` context?

> [@jeff](#):
>
> Filter _defines_ the Collection - it is a material first-order attribute of the Collection - it’s not data to be tweaked, like Title, Accent Colour etc.

I can see that logic for `collections` because as you noted `collections` are a Very-Special-Case.

But how about the same syntax used in `routes`? Deciding the `filter` or `limit` currently requires editing `routes.yaml`.

```auto
routes:
  /newsletter/:
    controller: channel
    data: page.newsletter
    filter: tag:newsletter
    limit: 20
    template: newsletter

```

It would be more flexible and safer to edit if it had pointed to an object which holds the data.

```auto
routes:
  /newsletter/:
    controller: channel
    list: newsletter

```

`routes.yaml` should be about routing. I think it should simply point to a resource which has more information, rather than have content information. Imagine our wifi routers caring about the content - doesn’t seem right.

> [@jeff](#):
>
> it’s important that Ghost has an internally consistent approach

I agree and this is why I think it’s bad to repurpose `post`, `page`, and `tag` as a template data storage.

1. It breaks the paradigm that _`post` and `page` should be displayed on the screen_, as now they must also act as a template data storage. Ghost haphazardly patches this issue by redirecting the slug of the data source to the `route`, so that the undisplayable `post` and `page` can’t be displayed.

2. Because `post`, `page`, `tag` aren’t designed to be a template data storage, it doesn’t have a place to store `filter` and `limit` which causes the fields to spill over into `routes.yaml`

3. _you might think that you can use `{{content}}` - the Content is actually in `{{excerpt}}`_ IMO this is again exactly because `page` is reused to be a template data storage. It’s not made to be one, and now it has to act as one, therefore the abstraction becomes inconsistent.

---

<div class="post-metadata">

**Author:** ![jeff](https://sea1.discourse-cdn.com/flex015/user_avatar/forum.ghost.org/jeff/32/4900_2.png) [@jeff](https://forum.ghost.org/u/jeff)\
**Post date:** [September 6, 2020, 5:40am UTC](https://forum.ghost.org/t/trying-to-understand-routes-using-filter-data-and-template-together/16918/8 "2020-09-06T05:40:56Z")

</div>

I kind of look at it the other way up :)

I see Posts, Pages, Tags as _first-order information schema_, rather than as mere holding places for Template display.

I see the Templates as secondary helpers that transform the structured information into a suitable format for a particular rendering surface - HTML, Print-language, RSS feed etc.

I view Routes as a convenient, permanent search of Articles, for Consumers.

I view Collections as fundamental structural sub-divisions of Articles.

That’s why I quite like the information being stored in Pages, Posts, Tags - my niggles are all small trivial implementation details.

The Context stuff is all the juicy goodness that the Ghost developers have given us to make life much easier in Template land - think of them as straight-forward interfaces to the structured data in your Articles (Posts) and using a consistent approach, also to the extra structured data you need to implement a site - Pages and Tags.

The easiest way to grasp it is to play with it all e.g. a series of small features like:

1. implement a Tags Index - [How to Create a Tags List Page in Your Ghost Theme | Aspire Themes](https://aspirethemes.com/blog/ghost-tags-page)
2. implement your own Authors Index using the same approach
3. implement a convenient Route: search e.g. /linux/ → all posts with any of [linux, kernel, linus, debian] tags
4. implement a /tweets/ Collection that is distinct and separate from your usual Article stream, using primary\_tag:#tweet

---

<div class="post-metadata">

**Author:** ![calvinpark](https://sea1.discourse-cdn.com/flex015/user_avatar/forum.ghost.org/calvinpark/32/4882_2.png) [@calvinpark](https://forum.ghost.org/u/calvinpark)\
**Post date:** [September 7, 2020, 12:04am UTC](https://forum.ghost.org/t/trying-to-understand-routes-using-filter-data-and-template-together/16918/9 "2020-09-07T00:04:52Z")

</div>

> [@jeff](#):
>
> implement a convenient Route: search e.g. /linux/ → all posts with any of [linux, kernel, linus, debian] tags

What I don’t understand is how to apply `Index` context to `/linux/`. I understand how to use `{{#get}}` to query the posts and show it. However, on an `Index` context, pagination is baked into the URL `/linux/:num/` based on the data from `package.json`.

Given a page with a list using `index.hbs` template, how do I use `Index` context on it?

---

<div class="post-metadata">

**Author:** ![jeff](https://sea1.discourse-cdn.com/flex015/user_avatar/forum.ghost.org/jeff/32/4900_2.png) [@jeff](https://forum.ghost.org/u/jeff)\
**Post date:** [September 7, 2020, 2:27am UTC](https://forum.ghost.org/t/trying-to-understand-routes-using-filter-data-and-template-together/16918/10 "2020-09-07T02:27:17Z")

</div>

Pagination seems to be work automatically… you just have to include the `{{pagination}}` control somewhere in your custom- template.

The only thing that’s a pain is that I haven’t found a way to say

```
{{#gt ../pagination.pages 1 }} {{pagination}} {{/gt}}

```

because the `{{#gt...}} {{#lt..}} {{#eq..}}` helpers haven’t been set.

It should be noted that I just hack Casper templates - I haven’t tried to add custom Handlebar helpers.

The multi-page URLs look like this: `/linux/page/2/` etc.

---

<div class="post-metadata">

**Author:** ![calvinpark](https://sea1.discourse-cdn.com/flex015/user_avatar/forum.ghost.org/calvinpark/32/4882_2.png) [@calvinpark](https://forum.ghost.org/u/calvinpark)\
**Post date:** [September 8, 2020, 11:29pm UTC](https://forum.ghost.org/t/trying-to-understand-routes-using-filter-data-and-template-together/16918/11 "2020-09-08T23:29:17Z")

</div>

> [@jeff](#):
>
> The multi-page URLs look like this: `/linux/page/2/` etc.

Oh my god that never occurred to me. Thank you! Based on this

> The index context is present on both the root URL of the site, e.g. `/` and also on subsequent pages of the post list, which live at `/page/:num/` .

I would have never guessed that `/linux/page/2/` was a possibility.

Hopefully a final question… As a test in `package.json` I’ve set

```auto
"config": {
    "posts_per_page": 3,

```

and iterate through them using

```auto
{{!< default}}

<div id="main-content" class="extreme-container u-marginTop20">
    <div class="row">
        <div class="col col-left story-feed">
            <div class="story-feed-content">
                {{#foreach posts visibility="all"}}
                    {{!-- Story - partials/story/story-list.hbs --}}
                    {{> "story/story-list"}}
                {{/foreach}}
            </div>
        </div>
        {{!-- Sidebar - partials/sidebar.hbs --}}
        {{> "sidebar"}}
    </div>
</div>

{{pagination}}

```

I’m finding that each page starts with post 1, 4, 7, etc. but each page is also showing _all_ posts, just starting at 1, 4, 7, etc… For example,

- page 1 shows posts 1, 2, 3, 4, 5, …, n
- page 2 shows posts 4, 5, 6, 7, 8, …, n
- page 3 shows posts 7, 8, 9, 10, 11, …, n

I’ve tried setting `limit=3` to `{{#foreach}}` but it still shows all posts. Any idea what I should be doing differently?

Thank you for all your help

---

<div class="post-metadata">

**Author:** ![calvinpark](https://sea1.discourse-cdn.com/flex015/user_avatar/forum.ghost.org/calvinpark/32/4882_2.png) [@calvinpark](https://forum.ghost.org/u/calvinpark)\
**Post date:** [September 8, 2020, 11:34pm UTC](https://forum.ghost.org/t/trying-to-understand-routes-using-filter-data-and-template-together/16918/12 "2020-09-08T23:34:04Z")

</div>

It turned out that the theme had an infinite scroll enabled which was causing to load all articles. Turning it off stopped showing all the articles.
