Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/alineacms/alinea/llms.txt

Use this file to discover all available pages before exploring further.

Alinea content is organised as a tree. Every entry has a parent (except root-level entries), an ordered list of siblings, and zero or more children. Multi-locale setups add a translation dimension: the same content can exist in parallel under different locale values. The Query namespace exported from alinea provides a set of graph query helpers that let you traverse these relationships inside a single select object. This means you can fetch a page together with its breadcrumb trail, its child pages, or its sibling navigation items in one round-trip to the in-memory database.

Built-in entry field expressions

Every field in the Query namespace is a typed Expr that you can place directly inside a select object:
Using built-in Query fields
import {Query} from 'alinea'
import {cms, BlogPost} from '@/cms'

const post = await cms.get({
  type: BlogPost,
  filter: {slug: {is: params.slug}},
  select: {
    id: Query.id,           // string — unique entry id
    title: Query.title,     // string — entry title
    type: Query.type,       // string — content type name
    path: Query.path,       // string — path segment
    url: Query.url,         // string — full URL
    locale: Query.locale,   // string | null — active locale
    status: Query.status,   // 'draft' | 'published' | 'archived'
    workspace: Query.workspace, // string — workspace name
    root: Query.root,           // string — root name
    index: Query.index,         // string — fractional sort index
    parentId: Query.parentId    // string | null — direct parent id
  }
})
These expressions map directly to the Entry record fields in the underlying database. They are always available regardless of which type you query.

Graph traversal functions

All graph query functions are imported from alinea and used inside the select of any query. Each function returns an edge descriptor that Alinea resolves as a sub-query while processing the parent query.

Query.children(query)

Returns the direct children of each result entry. Pass depth to retrieve multiple levels of descendants.
query.type
Type | Array<Type>
Constrain children to a specific content type.
query.depth
number
Number of levels to descend. Defaults to 1 (direct children only).
query.select
SelectObject
Fields to project for each child.
query.orderBy
Order | Array<Order>
Sort children before returning them.
Fetching a section with its children
import {Query} from 'alinea'
import {cms, BlogIndex, BlogPost} from '@/cms'

const section = await cms.get({
  type: BlogIndex,
  filter: {path: {is: 'blog'}},
  select: {
    title: Query.title,
    url: Query.url,
    posts: Query.children({
      type: BlogPost,
      orderBy: {desc: BlogPost.publishDate},
      select: {
        title: BlogPost.title,
        url: Query.url,
        excerpt: BlogPost.excerpt,
        publishDate: BlogPost.publishDate
      }
    })
  }
})
// section.posts is Array<{ title: string; url: string; excerpt: string; publishDate: string }>

Query.parents(query)

Returns all ancestor entries, ordered from the root down to the immediate parent. Useful for building breadcrumb navigation.
query.depth
number
Maximum number of ancestor levels to include.
Breadcrumb navigation
import {Query} from 'alinea'
import {cms, BlogPost} from '@/cms'

const post = await cms.get({
  type: BlogPost,
  filter: {slug: {is: params.slug}},
  select: {
    title: BlogPost.title,
    breadcrumbs: Query.parents({
      select: {
        title: Query.title,
        url: Query.url
      }
    })
  }
})
// post.breadcrumbs is Array<{ title: string; url: string }>
// ordered from the root ancestor down to the immediate parent

Query.parent(query)

Returns the single direct parent entry, or null if the entry is at the root level.
Fetching the parent
import {Query} from 'alinea'
import {cms, BlogPost} from '@/cms'

const post = await cms.get({
  type: BlogPost,
  id: params.id,
  select: {
    title: BlogPost.title,
    section: Query.parent({
      select: {
        title: Query.title,
        url: Query.url
      }
    })
  }
})
// post.section is { title: string; url: string } | null

Query.siblings(query)

Returns entries at the same tree level (i.e., sharing the same parent). By default the current entry is excluded.
query.includeSelf
boolean
When true, the current entry is included in the result. Defaults to false.
Sibling navigation
import {Query} from 'alinea'
import {cms, BlogPost} from '@/cms'

const post = await cms.get({
  type: BlogPost,
  filter: {slug: {is: params.slug}},
  select: {
    title: BlogPost.title,
    related: Query.siblings({
      type: BlogPost,
      orderBy: {asc: Query.index},
      select: {
        title: BlogPost.title,
        url: Query.url
      }
    })
  }
})

Query.next(query) and Query.previous(query)

Returns the immediately next or previous sibling according to the current sort order. Returns null when the current entry is the last or first in its parent.
Previous / next post links
import {Query} from 'alinea'
import {cms, BlogPost} from '@/cms'

const post = await cms.get({
  type: BlogPost,
  filter: {slug: {is: params.slug}},
  select: {
    title: BlogPost.title,
    body: BlogPost.body,
    prev: Query.previous({
      type: BlogPost,
      select: {title: BlogPost.title, url: Query.url}
    }),
    next: Query.next({
      type: BlogPost,
      select: {title: BlogPost.title, url: Query.url}
    })
  }
})
// post.prev and post.next are { title: string; url: string } | null

Query.translations(query)

Returns the same entry in other locales. Use this to build language-switcher links.
query.includeSelf
boolean
When true, the current locale is included in the result array. Defaults to false.
Language switcher
import {Query} from 'alinea'
import {cms} from '@/cms'

const page = await cms.get({
  id: params.id,
  locale: params.locale,
  select: {
    title: Query.title,
    translations: Query.translations({
      includeSelf: true,
      select: {
        locale: Query.locale,
        url: Query.url,
        title: Query.title
      }
    })
  }
})
// page.translations is Array<{ locale: string | null; url: string; title: string }>

Complete example: page with children and breadcrumbs

The following example fetches a documentation page together with its full breadcrumb trail and its immediate child pages — all in a single cms.get() call.
app/docs/[...slug]/page.tsx
import {Query} from 'alinea'
import {cms, DocPage} from '@/cms'

export default async function Page({params}: {params: {slug: string[]}}) {
  const url = '/' + params.slug.join('/')

  const page = await cms.get({
    url,
    type: DocPage,
    select: {
      // Own fields
      title: DocPage.title,
      body: DocPage.body,

      // Breadcrumb trail (root → … → immediate parent)
      breadcrumbs: Query.parents({
        select: {
          title: Query.title,
          url: Query.url
        }
      }),

      // Child pages for a "sub-navigation" sidebar
      children: Query.children({
        type: DocPage,
        orderBy: {asc: Query.index},
        select: {
          title: DocPage.title,
          url: Query.url
        }
      }),

      // Previous / next within the same parent
      prev: Query.previous({
        type: DocPage,
        select: {title: DocPage.title, url: Query.url}
      }),
      next: Query.next({
        type: DocPage,
        select: {title: DocPage.title, url: Query.url}
      })
    }
  })

  return (
    <article>
      <nav aria-label="Breadcrumb">
        {page.breadcrumbs.map(crumb => (
          <a key={crumb.url} href={crumb.url}>{crumb.title}</a>
        ))}
      </nav>
      <h1>{page.title}</h1>
      {/* render body */}
    </article>
  )
}
Graph query helpers can be nested — for example, you can include Query.children inside the result of a Query.children query to fetch a two-level tree. Use the depth option as a simpler alternative when you want a uniform selection at every level.

Build docs developers (and LLMs) love