This example uses public rendered page HTML, one route family, one Eleventy loader, and one pagination template. The domain is fictitious; replace it and provide environment variables before building.
This page is an optional JavaScript library example. For installing the WordPress plugin and testing staging before production, start with WP Awesome deployment. Code blocks on this page can be copied when JavaScript is enabled.
Project tree
beautiful-site/
├── .eleventy.cjs
├── package.json
├── site/
│ └── wordpress-data.cjs
└── src/
├── pages.njk
└── _includes/
└── layouts/
└── page.njk
package.json
Use the published package version in the consumer manifest:
{
"private": true,
"scripts": {
"build": "eleventy",
"serve": "eleventy --serve",
"test": "node --test"
},
"dependencies": {
"wp-awesome": "^0.3.0"
},
"devDependencies": {
"@11ty/eleventy": "^3.1.6"
}
}
For unreleased changes, install the local checkout instead of that registry dependency:
bun add /absolute/path/to/wp-awesome
bun add --dev @11ty/eleventy@^3.1.6
site/wordpress-data.cjs
The loader selects published pages, requests embedded records, normalizes their content and routes, and rejects output collisions before Eleventy renders anything.
'use strict'
const {
assertNoWordPressRouteCollisions,
createWordPressRestClient,
createWordPressRouteResolver,
normalizeWordPressRecord,
} = require('wp-awesome')
const wordpressOrigin = process.env.WORDPRESS_ORIGIN
const siteUrl = process.env.SITE_URL
if (!wordpressOrigin || !siteUrl) {
throw new Error('WORDPRESS_ORIGIN and SITE_URL are required.')
}
const rest = createWordPressRestClient({
baseUrl: `${wordpressOrigin}/wp-json/`,
perPage: 100,
retries: 3,
timeoutMs: 20_000,
})
const resolveRoute = createWordPressRouteResolver({
siteUrl,
routes: { page: '/pages/{slug}/' },
})
module.exports = async function loadWordPressData() {
const sourcePages = await rest.getCollection('wp/v2/pages', {
params: { status: 'publish', _embed: 1 },
maxPages: 30,
})
const pages = sourcePages.map((sourcePage) => normalizeWordPressRecord(sourcePage, {
type: 'page',
routeResolver: resolveRoute,
customFields: ['subtitle'],
contentPolicy: { mode: 'rendered' },
}))
assertNoWordPressRouteCollisions(pages)
return { pages }
}
.eleventy.cjs
The package's plugin registers the async loader as wordpress global data. This consumer keeps its output and includes directory configuration in its own Eleventy config.
'use strict'
const { createWordPressEleventyPlugin } = require('wp-awesome')
const loadWordPressData = require('./site/wordpress-data.cjs')
module.exports = function configureSite(eleventyConfig) {
eleventyConfig.addPlugin(createWordPressEleventyPlugin({
loadData: loadWordPressData,
}))
return {
dir: {
input: 'src',
includes: '_includes',
output: '_site',
},
templateFormats: ['md', 'njk'],
htmlTemplateEngine: 'njk',
markdownTemplateEngine: 'njk',
}
}
src/pages.njk
The pagination alias makes one Eleventy page for each normalized WordPress record. The route contract is the single source for permalink and canonical metadata.
---
pagination:
data: wordpress.pages
size: 1
alias: pageRecord
permalink: "{{ pageRecord.route.outputPath }}"
layout: layouts/page.njk
eleventyComputed:
title: "{{ pageRecord.title.text }}"
canonicalUrl: "{{ pageRecord.route.canonicalUrl }}"
---
{{ pageRecord.content.html | safe }}
src/_includes/layouts/page.njk
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ title }}</title>
<link rel="canonical" href="{{ canonicalUrl }}">
</head>
<body>
<main>
<h1>{{ title }}</h1>
{{ content | safe }}
</main>
</body>
</html>
Build commands
export WORDPRESS_ORIGIN='https://beautiful.wp.site'
export SITE_URL='https://beautiful.wp.site'
bun run build
The placeholder host will not return WordPress data. Replace it with a real site or run the consumer against a mock REST server during local development. Do not put Application Passwords in this public-content example; see install and configure if edit-context data is required.
Before using this in production
The example intentionally does not implement a site-wide sitemap inclusion policy, cache, delete/unpublish handling, redirects, SEO metadata, custom dynamic block rendering, or whole-site route audit. Those decisions belong to the consumer. Extend the loader and checks before replacing an existing public site, and keep user-specific commerce behavior connected to its live backend.