Skip to content

Pages

A store needs prose the catalogue does not hold: a returns policy, shipping information, contact terms. Consumer-protection rules in several jurisdictions and Stripe’s own account requirements expect a shop to publish them.

Pages in the admin writes them. Each has a title, a slug, and a body in Markdown. A page is a draft until it is published, and can optionally be linked in the storefront menu.

The menu flag decides whether a page is also in the header. It does not decide whether the page can be found. A returns policy left out of the menu is still one link from every page of the shop, which is the point of publishing it. Keep that behaviour if you rewrite the footer.

Bodies are stored as Markdown and rendered to HTML on the server every time they are read, never stored as HTML. Two things follow. Tightening the sanitiser applies retroactively to every page already written. And no Markdown parser reaches a shopper’s bundle, which is why the admin’s preview asks the server to render it (POST /api/admin/pages/preview): a second implementation in the browser would eventually disagree with the first about what is safe.

The allow-list is prose and nothing else: headings, paragraphs, lists, links, emphasis, code, quotes, rules. No scripts, no styles, no frames, no event handlers. External links carry rel="nofollow noopener noreferrer".

shop, cart, confirm, product, collection, about, admin, setup and account are refused with a message naming the conflict. A page is served from /:slug, registered last so it cannot shadow a static route, but a page at /cart would simply never render, with nothing to say why.

An existing store’s aboutText became an About page the first time the pages migration ran. The column is deprecated and stays for one release so an install can roll back.

/api/store carries page summaries only, since the header needs titles on first paint and a store with ten long policies should not put all of that in every shopper’s initial payload. GET /api/pages/:slug fetches a body.