Write your own local plugin

You don't need to publish to npm to extend Nera. Any module at src/plugins/<name>/index.js is discovered and applied automatically. Let's build a real one: a plugin that collects every tutorial into a list for an index page — the exact plugin that powers this site.

The contract

A plugin exports either or both of two hooks:

export function getAppData({ app, pagesData }) {
    // must return a plain object — becomes the new `app`
}

export function getMetaData({ app, pagesData }) {
    // must return an array — becomes the new `pagesData`
}

getAppData runs first; getMetaData sees the app it returned. Keep them synchronous — an async hook can silently wipe app on older generator versions.

Build it

Create src/plugins/tutorials-list/index.js:

export function getAppData({ app, pagesData }) {
    if (!app || typeof app !== 'object') return app
    if (!Array.isArray(pagesData)) return app

    const tutorials = pagesData
        .filter(({ meta }) => {
            const href = meta?.href || ''
            return href.startsWith('/tutorials/') && href !== '/tutorials/index.html'
        })
        .map(({ meta }) => ({
            href: meta.href,
            title: meta.title || meta.href,
            description: meta.description || '',
            createdAt: meta.createdAt,
        }))
        .sort((a, b) => {
            const da = new Date(a.createdAt).getTime()
            const db = new Date(b.createdAt).getTime()
            if (!Number.isNaN(da) && !Number.isNaN(db) && da !== db) return db - da
            return String(a.href).localeCompare(String(b.href))
        })

    return { ...app, tutorials }
}

That's the whole plugin. It reads every page, keeps the ones under /tutorials/, and returns a new app with a sorted tutorials array.

Use it in a layout

app.tutorials is now available in every view:

if app.tutorials && app.tutorials.length
  ul.tutorials__list
    each post in app.tutorials
      li.tutorials__item
        a.tutorials__link(href=post.href)
          h2 #{ post.title }
          p #{ post.description }

Why return a new object

Nera threads each hook's return value into the next. Returning { ...app, tutorials } (rather than mutating app) keeps that data flow predictable — the same reason the built-in plugins do it. From here, the same pattern builds related-posts lists, author pages, "recently updated" widgets — anything derived from your content.