Use EdgePress for writing and Pages
Most recent changes
Previously
I use EdgePress to keep writing and layout in ordinary files. Articles are Markdown. Homepages and project pages are YAML layouts. The build produces static HTML and local assets in dist/; there is no site database to maintain for publishing an article.
Now
I use EdgePress to keep writing and layout in ordinary files. Articles are Markdown. Homepages and project pages are JSON layouts. The build produces static HTML and local assets in dist/; there is no site database to maintain for publishing an article.
Previously
For a new site using the published npm package, initialize an empty directory instead:
Now
For a new site, install the published GitHub source commit used by this site and initialize an empty directory:
Previously
npm install edgepress
Now
npm install edgepress@https://codeload.github.com/jsw-teams/edgepress/tar.gz/ac42e0046d022a318e4eef21e56e2e459584b2a5 --save-exact
Previously
content/pages//en.md (page metadata + rows and elements)
Now
content/pages//en.json (page metadata + rows and elements)
Previously
Set your title, canonical URL, navigation, footer and privacy details in config.yml. Enable languages and change build paths in edgepress.config.mjs. A locale filename is lowercase, such as en.md or zh-cn.md, and its lang must match an enabled locale. Each article or page gets a directory containing its translations.
Now
Set your title, canonical URL, navigation, footer and privacy details in config.yml. Enable languages and change build paths in edgepress.config.mjs. A locale filename is lowercase: Pages use en.json or zh-cn.json, while articles use en.md or zh-cn.md. Its lang must match an enabled locale. Each article or page gets a directory containing its translations.
Previously
---
title: Project notes
lang: en
slug: notes
blocks: [ # page (rows)
{ columns: 2, cells: [ # row (two cells)
[ # left cell (heading, text)
{ type: heading, level: 2, text: "What I am making" },
{ type: text, text: "A small tool for everyday writing." }
],
[ # right cell (articles)
{ type: post-list, title: "Notes", category: edgepress, count: 3 }
]
] }
]
---
Now
{
"title": "Project notes",
"lang": "en",
"slug": "notes",
"blocks": [
{
"columns": 2,
"cells": [
[
{
"type": "heading",
"level": 2,
"text": "What I am making"
},
{
"type": "text",
"text": "A small tool for everyday writing."
}
],
[
{
"type": "post-list",
"title": "Notes",
"category": "edgepress",
"count": 3
}
]
]
}
]
}
Previously
Save it as content/pages/notes/en.md and leave the body after the front matter empty. The outer blocks list contains rows. columns: 2 requires two inner lists in cells. The left cell contains a heading followed by text; the right contains a project article list. Lists use [], objects use {}, and adjacent entries need commas. These are standard YAML delimiters; explanatory parentheses are not another configuration language.
The indented form remains valid. For long text and code, use ordinary YAML text: | and code: | fields. A section element's blocks contains child elements rather than another row of cells. Use heading for a separate title, text or paragraphs for prose, and the corresponding elements for lists and tables.
Now
Save it as content/pages/notes/en.json. Metadata and blocks share one JSON object; there are no YAML delimiters or Markdown body. VS Code can identify and fold blocks, each row, cells, each cell and its elements. The local $schema reference supplies field help and checks the structure. columns: 2 needs two cell arrays; their elements render in order. A section’s blocks contain child elements. Use paragraphs for separate prose paragraphs and \n inside code strings for line breaks. Legacy Markdown/YAML Pages still build, but keep one format per page language.
I use EdgePress to keep writing and layout in ordinary files. Articles are Markdown. Homepages and project pages are JSON layouts. The build produces static HTML and local assets in dist/; there is no site database to maintain for publishing an article.
Get a site running
For the current repository checkout, use Node.js 22.12 or newer and run these commands from the project root:
npm ci
npm run new -- post "A quiet afternoon"
npm run dev
The new-post command prints the path of the file it created. Open that file, keep the metadata between the two --- lines, and write below the second line. The preview rebuilds when source files change. Create an article with the CLI once; subsequent edits happen in the editor.
For a new site, install the published GitHub source commit used by this site and initialize an empty directory:
mkdir my-site
cd my-site
npm install edgepress@https://codeload.github.com/jsw-teams/edgepress/tar.gz/ac42e0046d022a318e4eef21e56e2e459584b2a5 --save-exact
npx edgepress init
npm install
The npm release and the latest GitHub checkout can differ. Keep the site's dependency version and lockfile together. A site that already depends on EdgePress should use its existing package scripts rather than install a global copy.
Know which file to edit
config.yml (site, theme, privacy, consent services)
edgepress.config.mjs (paths, locales, permalink, build plugins)
content/posts/<post-id>/en.md (article metadata + Markdown)
content/pages/<page-id>/en.json (page metadata + rows and elements)
content/assets/images/walk.jpg (published as /images/walk.jpg)
themes/<name>/ (shared layouts and visual styles)
dist/ (generated output)
Set your title, canonical URL, navigation, footer and privacy details in config.yml. Enable languages and change build paths in edgepress.config.mjs. A locale filename is lowercase: Pages use en.json or zh-cn.json, while articles use en.md or zh-cn.md. Its lang must match an enabled locale. Each article or page gets a directory containing its translations.
Write the article
---
title: A quiet afternoon
date: 2026-10-11T10:00:00+08:00
lang: en
category: blog
tags: [walk, photography]
---
We stopped by the water.
## A small detail

The CLI supplies the current timestamp and site language; the example above shows the shape of the file. The theme renders the article title, so use ## for sections rather than repeating the title as an H1. Copy the photograph to content/assets/images/walk.jpg before building.
One category chooses the article's section. blog and uncategorized belong to this site's everyday observations; edgepress, ishare, iask and media-viewer belong to product notes. Tags describe the article and do not select its homepage list.
Read a page from the outside in
Page (rows) → row (columns, cells) → cell (ordered elements) → element (type, content). Here is a complete two-column page:
{
"title": "Project notes",
"lang": "en",
"slug": "notes",
"blocks": [
{
"columns": 2,
"cells": [
[
{
"type": "heading",
"level": 2,
"text": "What I am making"
},
{
"type": "text",
"text": "A small tool for everyday writing."
}
],
[
{
"type": "post-list",
"title": "Notes",
"category": "edgepress",
"count": 3
}
]
]
}
]
}
Save it as content/pages/notes/en.json. Metadata and blocks share one JSON object; there are no YAML delimiters or Markdown body. VS Code can identify and fold blocks, each row, cells, each cell and its elements. The local $schema reference supplies field help and checks the structure. columns: 2 needs two cell arrays; their elements render in order. A section’s blocks contain child elements. Use paragraphs for separate prose paragraphs and \n inside code strings for line breaks. Legacy Markdown/YAML Pages still build, but keep one format per page language.
Preview, then publish the generated files
Run npm run build to validate and generate the website; npm run check writes the page report under tools/reports/. Inspect the homepage, article, language links and mobile layout locally. Publish dist/ through your existing host and deployment configuration. Generated HTML is output, so make corrections in the source files.
Theme development · Optional text view · Project and quick start