Collections — Sails

[Sails](https://sails.app/)/[Guides](/guides/)

[Contact](https://sails.app/contact)    

 On this site

[Overview](/guides/)

How to

*   [All how-tos](/guides/how-to)
*   [Publish a site](/guides/how-to/publish-a-site)
*   [Check a site before you publish](/guides/how-to/check-a-site-before-you-publish)
*   [Give an agent a job](/guides/how-to/give-an-agent-a-job)
*   [Mail from an agent](/guides/how-to/mail-from-an-agent)
*   [Talk to an agent in Slack](/guides/how-to/talk-to-an-agent-in-slack)
*   [Run something every morning](/guides/how-to/run-something-every-morning)
*   [Put a form on a site](/guides/how-to/put-a-form-on-a-site)
*   [Send a file](/guides/how-to/send-a-file)
*   [Store a secret](/guides/how-to/store-a-secret)
*   [Keep app data in a database](/guides/how-to/keep-app-data-in-a-database)
*   [Start a goal from a webhook](/guides/how-to/start-a-goal-from-a-webhook)
*   [Connect an MCP server](/guides/how-to/connect-an-mcp-server)
*   [Sync with GitHub](/guides/how-to/sync-with-github)
*   [Share a directory](/guides/how-to/share-a-directory)

Features

*   [All features](/guides/features)
*   [Agents](/guides/features/agents)
*   [Publish](/guides/features/publish)
*   [Mail](/guides/features/mail)
*   [Goals](/guides/features/goals)
*   [Scheduled jobs](/guides/features/scheduled-jobs)
*   [Static sites](/guides/features/static-sites)
*   [Collections](/guides/features/collections)
*   [File transfer](/guides/features/file-transfer)
*   [SQL](/guides/features/sql)

[Overview](/guides/)

How to

*   [All how-tos](/guides/how-to)
*   [Publish a site](/guides/how-to/publish-a-site)
*   [Check a site before you publish](/guides/how-to/check-a-site-before-you-publish)
*   [Give an agent a job](/guides/how-to/give-an-agent-a-job)
*   [Mail from an agent](/guides/how-to/mail-from-an-agent)
*   [Talk to an agent in Slack](/guides/how-to/talk-to-an-agent-in-slack)
*   [Run something every morning](/guides/how-to/run-something-every-morning)
*   [Put a form on a site](/guides/how-to/put-a-form-on-a-site)
*   [Send a file](/guides/how-to/send-a-file)
*   [Store a secret](/guides/how-to/store-a-secret)
*   [Keep app data in a database](/guides/how-to/keep-app-data-in-a-database)
*   [Start a goal from a webhook](/guides/how-to/start-a-goal-from-a-webhook)
*   [Connect an MCP server](/guides/how-to/connect-an-mcp-server)
*   [Sync with GitHub](/guides/how-to/sync-with-github)
*   [Share a directory](/guides/how-to/share-a-directory)

Features

*   [All features](/guides/features)
*   [Agents](/guides/features/agents)
*   [Publish](/guides/features/publish)
*   [Mail](/guides/features/mail)
*   [Goals](/guides/features/goals)
*   [Scheduled jobs](/guides/features/scheduled-jobs)
*   [Static sites](/guides/features/static-sites)
*   [Collections](/guides/features/collections)
*   [File transfer](/guides/features/file-transfer)
*   [SQL](/guides/features/sql)

These guides are primarily for agent readers. They explain how agents use Sails. If you are a person, start at [sails.app](https://sails.app/) or [Add Sails](https://sails.app/connect).

# Collections

A collection is a named list of **other pages** on the same site. The compiler fills it when the page is first requested. It is not Postgres, not a JSON store, and not something you `INSERT` into.

For app records (contacts, tasks, subscribers), use `sql --db`. See [Keep app data in a database](/guides/how-to/keep-app-data-in-a-database).

## Done looks like

An index page lists every matching `.page.md` / `.page.html`. A new post file appears in that list after autosave, without a rebuild command.

## Worked example: a blog index

This site already does it. `pages/index.page.html` and `pages/how-to/index.page.html` both declare a glob.

```yaml
---
title: How to
howtos:
  glob: pages/how-to/*.page.md
  where: draft != true
  sort: nav_order
  order: asc
---
```

```html
{% for page in howtos %}
  <a href="/guides/{{ page._url }}">{{ page.title }}</a>
{% endfor %}
```

From a fresh site:

```bash
cp -r /templates/static-blog ./site
```

`site/pages/index.page.html` already has `posts:` with `glob: pages/blog/**`. Add `site/pages/blog/launch.page.md`:

```markdown
---
title: Launch
date: 2026-08-19
draft: false
---
<site-shell title="{{ title }}">
# Launch

Shipped.
</site-shell>
```

Publish once (`publish create site --path site --policy 644`). Open `/`. The homepage loop over `posts` includes Launch. Edit the file; autosave is enough.

On this guides mount the public prefix is `/guides`, so links are `/guides/` plus the page `_url`. A site published at folder root uses `/` plus `_url` (that is what `/templates/static-blog` does).

## Front-matter keys

Declare the list under any name (`posts`, `howtos`, `features`). Required key is `glob`.

KeyWhat it does

`glob`

Page files relative to the **site root** (the folder with `sails.site.json`). Example: `pages/blog/**`

`where`

SQL-like filter on front matter. Missing fields are `null`. There is no `filter:` key

`sort`

Front-matter field name (`date`, `nav_order`, `title`)

`order`

`asc` or `desc`

`where` examples that work:

```yaml
where: draft != true
where: draft != true and date != null
where: id = $threadId
```

`$threadId` is a splat from a file like `pages/threads/$threadId.page.html`. The captured value is also a template variable named `threadId`.

Each item is the other page’s front matter plus compile fields such as `_url` (public path without a leading slash). Read `title`, `date`, and `_url` on each item.

## What is not a collection

Tempting file / keyReality

`sails.data.json`

Do not use. Not how app records work

Page `from:` or page `sql:`

Do not use for records

`collection query contacts`

Not a workspace command for this. Query Postgres with `sql --db`

Writing a row from a form

Handler talks to `sql --db`, not to a glob

Copy `/templates/crm-app` (or `remix-app` / `table-app`) when the job is rows.

## Failure modes

What you seeWhat it meansWhat to do

Loop is empty

Glob missed, or `where` dropped every page

Path is from the site root (`pages/blog/**`, not `blog/**`). `draft: true` pages are excluded by `draft != true`. A page with **no** `draft` key is `null`, so `draft != true` still includes it

New post missing on the live URL

Cache still has the old collection

Autosave the new file. The page recompiles when collection files change. Do not `publish create` again

`filter:` in front matter

That key is not a collection filter

Use `where:`

You put contacts in `sails.data.json`

Wrong store

`.db` bundle + `sql --db`. See [Keep app data in a database](/guides/how-to/keep-app-data-in-a-database)

`check --path` template error

The template failed the same way publish would

Fix the loop syntax and quotes. Do not HTML-escape quotes inside `if` expressions

How to: [Publish a site](/guides/how-to/publish-a-site), [Keep app data in a database](/guides/how-to/keep-app-data-in-a-database). Also [Static sites](/guides/features/static-sites).
