skier. v1.0.0

generatePaginatedItemsTask

Generate multiple HTML pages from a data array with built-in pagination controls.


When to Use

✅ Use generatePaginatedItemsTask when you need:

  • Paginated blog archives
  • Activity timelines split across pages
  • Any large dataset that should be chunked

❌ Use generateItemsTask instead when:

  • Each item needs its own detail page
  • Data comes from Markdown files

Quick Start

generatePaginatedItemsTask({
  dataVar: '${posts}',
  itemsPerPage: 10,
  template: 'src/pages/blog.html',
  partialsDir: 'src/partials',
  outDir: 'public',
  basePath: '/blog',
})

Output files:

public/
├── blog.html           # Page 1
└── blog/
    └── page/
        ├── 2.html      # Page 2
        └── 3.html      # Page 3

Configuration

Option Type Required Description
dataFile string ⚡ Path to JSON file
dataVar string ⚡ Pipeline variable using ${varName} syntax
dataKey string Key to extract array from JSON object
itemsPerPage number ✅ Items per page
template string ✅ Handlebars template path
partialsDir string ✅ Partials directory
outDir string ✅ Output directory
basePath string ✅ URL base path (for links)
outputVar string Template variable name for items (default: 'items')
paginationVar string Template variable for pagination (default: 'pagination')
itemTransformFn function Transform each item
additionalVarsFn function Add extra template variables

⚡ Exactly one of dataFile or dataVar is required.


Type signature

The exact GeneratePaginatedItemsConfig interface, transcluded from source so it stays in step with the shipping code:

export interface GeneratePaginatedItemsConfig {
  /**
   * Path to a JSON file containing the data.
   * Mutually exclusive with dataVar.
   */
  dataFile?: string;

/**

  • Variable name from globals to use as data source.
  • Use ${varName} syntax (e.g., '${thoughtsList}').
  • Mutually exclusive with dataFile. */ dataVar?: string;

/**

  • Optional key within the JSON data to read the array from.
  • E.g., 'timeline' to read from data.timeline. */ dataKey?: string;

/** Number of items per page */ itemsPerPage: number;

/** Path to the Handlebars template for each page */ template: string;

/** Directory containing Handlebars partials */ partialsDir: string;

/** Output directory for generated pages */ outDir: string;

/**

  • Base path for URLs and output files.
  • E.g., '/life-fitness' results in:
    • /life-fitness.html (page 1)
    • /life-fitness/page/2.html (page 2) */ basePath: string;

/**

  • Variable name for the items array in templates.
  • Defaults to 'items'. */ outputVar?: string;

/**

  • Variable name for the pagination metadata in templates.
  • Defaults to 'pagination'. */ paginationVar?: string;

/**

  • Optional function to transform each item before passing to the template.
  • Receives the item and its index. */ itemTransformFn?: (item: unknown, index: number) => unknown;

/**

  • Optional function to inject additional variables per page.
  • Receives page context including pageNumber, totalPages, and items. */ additionalVarsFn?: (args: { pageNumber: number; totalPages: number; items: unknown[]; }) => Record<string, unknown> | Promise<Record<string, unknown>>; }

Every task's config type together: API Reference → Config Interfaces.


Data Sources

From pipeline globals (recommended):

generateItemsTask({ outputVar: 'posts' }),
generatePaginatedItemsTask({
  dataVar: '${posts}',
  // ...
})

From JSON file:

generatePaginatedItemsTask({
  dataFile: './data/activities.json',
  dataKey: 'timeline',  // Extract data.timeline
  // ...
})

Pagination Object

Your template receives a pagination object:

{
  currentPage: 2,
  totalPages: 5,
  totalItems: 47,
  itemsPerPage: 10,

  hasNext: true,
  hasPrev: true,

  nextUrl: '/blog/page/3',
  prevUrl: '/blog',          // Page 1 uses base path
  firstUrl: '/blog',
  lastUrl: '/blog/page/5',

  pages: [
    { number: 1, url: '/blog', isCurrent: false },
    { number: 2, url: '/blog/page/2', isCurrent: true },
    { number: 3, url: '/blog/page/3', isCurrent: false },
    // ...
  ]
}

Template Example

<h1>Blog Archive</h1>

{{#each items}}
  <article>
    <h2><a href="{{this.link}}">{{this.title}}</a></h2>
    <time>{{this.formattedDate}}</time>
    <p>{{this.excerpt}}</p>
  </article>
{{/each}}

{{#if pagination.totalPages}}
<nav class="pagination">
  {{#if pagination.hasPrev}}
    <a href="{{pagination.prevUrl}}" rel="prev">← Newer</a>
  {{/if}}

  {{#each pagination.pages}}
    {{#if this.isCurrent}}
      <span class="current">{{this.number}}</span>
    {{else}}
      <a href="{{this.url}}">{{this.number}}</a>
    {{/if}}
  {{/each}}

  {{#if pagination.hasNext}}
    <a href="{{pagination.nextUrl}}" rel="next">Older →</a>
  {{/if}}
</nav>
{{/if}}

Transforming Items

Format data before rendering:

generatePaginatedItemsTask({
  // ...
  itemTransformFn: (item) => ({
    ...item,
    formattedDate: new Date(item.date).toLocaleDateString('en-GB', {
      day: 'numeric',
      month: 'long',
      year: 'numeric',
    }),
  }),
})

Additional Page Variables

Inject extra data per page:

generatePaginatedItemsTask({
  // ...
  additionalVarsFn: ({ pageNumber, totalPages, items }) => ({
    pageTitle: pageNumber === 1
      ? 'Blog'
      : `Blog - Page ${pageNumber} of ${totalPages}`,
    isFirstPage: pageNumber === 1,
  }),
})

Real-World Example

Activity timeline with date grouping:

generatePaginatedItemsTask({
  dataVar: '${activities}',
  itemsPerPage: 20,
  template: 'src/pages/activity-timeline.html',
  partialsDir: 'src/partials',
  outDir: 'public',
  basePath: '/activity',
  outputVar: 'activities',

  itemTransformFn: (activity) => ({
    ...activity,
    icon: getActivityIcon(activity.type),
    formattedDate: formatDate(activity.date),
  }),

  additionalVarsFn: ({ pageNumber, items }) => {
    // Group by month for this page
    const byMonth = items.reduce((acc, item) => {
      const month = item.date.slice(0, 7);
      (acc[month] = acc[month] || []).push(item);
      return acc;
    }, {});

    return {
      pageTitle: `Activity${pageNumber > 1 ? ` - Page ${pageNumber}` : ''}`,
      monthGroups: Object.entries(byMonth),
    };
  },
})

Edge Cases

Situation Behavior
0 items Generates page 1 with empty items array
Items ≤ itemsPerPage Single page, hasNext: false, hasPrev: false
Missing dataVar Empty array, logs warning
Missing dataKey Empty array if key not found in JSON

Common Mistakes

❌ Confusing basePath and outDir:

{
  outDir: 'public',        // Filesystem path
  basePath: '/blog',       // URL path (for links!)
}

❌ Expecting sorted data: Items are NOT sorted automatically. Sort in dataFile, via itemTransformFn, or in a prior task.

❌ Using wrong variable syntax:

dataVar: 'posts',      // ❌ Wrong
dataVar: '${posts}',   // ✅ Correct