# CMS content pages in Go (GoCommerce module)

> Add about, terms and delivery pages to a GoCommerce store: one page per slug and language, draft or published, served at /x/cms/pages/{slug}.

- Canonical: https://kitcommerce.store/integrations/cms/
- Last updated: 2026-10-30

---

Platform

## CMS content pages in Go

A Go package that gives a GoCommerce store its written pages: about, terms, delivery, a landing page. Each page is a slug, a title, a body and a language, draft until it is published. The storefront reads it at /x/cms/pages/{slug}, and operators write it on the admin’s Pages screen.

- **Platform** module
- **1** setting
- **10** tests
- **7** API operations
- **ext/cms**

### What it does

In the package’s own words it adds “content pages — about, terms, delivery, a landing page — to a gocommerce store”. It is a module rather than part of the engine because a store can sell without it. It owns one table, cms_pages, mounts its own routes, and never touches a commerce table.

A page has a slug, a title, a body, an optional excerpt for a listing page, a status of draft or published, a language, and a set of SEO name and value pairs. The slug is unique per language, so the same page can be written once in each of the store’s languages under one address.

The storefront asks for a page by slug. The module answers in the request’s language, taken from ?lang or Accept-Language, and when that page has not been written it falls back through the store’s languages, main first, rather than answer 404. Drafts never reach the public routes.

### Configuration

One setting, with a default, and nothing to set in the panel. Pages are not configuration: they are rows an operator writes on the Pages screen or through the API.

*Content pages module settings — 1 setting, 0 required*

| Setting | Environment variable | Required | What it does |
| --- | --- | --- | --- |
| `PublishedOnly` | — | No | Hides draft pages from the public routes. True when left nil; in the package’s words there is no good reason to turn it off outside a preview environment. Go only; not in the panel. |

The module reads its Config struct, not the environment. The variable names are the ones the package’s own example or the reference binary uses; in your own `main()` you choose where each value comes from. Where a setting has a panel label, the admin’s settings drawer can hold it too, and a value typed there wins over Config.

### Setting it up

1. **Install the module** — Pass cms.New to gocommerce.New, as below, or start the reference binary with -cms. It adds the cms_pages table and the Pages screen to the admin’s main nav.
2. **Write a page** — On the Pages screen, or POST /api/admin/x/cms/pages with a title and a slug. It is a draft unless the status says published, and in the store’s main language unless the request names one.
3. **Publish it** — Set the status to published. The first publish stamps published_at, and publishing again does not move that date.
4. **Read it from the storefront** — GET /x/cms/pages/{slug} answers with the page in the shopper’s language, and GET /x/cms/pages lists the published pages in that language, newest first.

main.go

```
import (
	"github.com/itswadesh/gocommerce/core"
	"github.com/itswadesh/gocommerce/ext/cms"
)

app, err := gocommerce.New(cfg, cms.New(cms.Config{}))
```

The package doc’s own example, with its imports; cfg is your gocommerce.Config. The reference binary’s -cms flag and examples/store/main.go install it the same way. Import path `github.com/itswadesh/gocommerce/ext/cms`.

### How it works

- **One page per slug and language**

  A unique key on slug and language lets “about” exist once in each of the store’s languages without colliding. Creating a page at a slug already taken in that language, or renaming one onto it, answers 409 with the slug.

- **A fallback, not a 404**

  A shopper whose browser asks for a translation nobody has written gets the page in the store’s other languages, main first. Only a slug with no published page in any of the store’s languages is a 404.

- **Draft or published**

  The status is one of the two, checked on create, on update and on the admin list’s filter. Drafts show on the admin routes and never on the public ones.

- **SEO as name and value pairs**

  Each page carries a free set of string pairs, stored as JSON and handed back as they were. The module does not read or check them; the storefront decides what each name means.

- **Rights of its own**

  Reading pages in the admin needs pages.read, held by managers and staff by default. Writing, publishing and deleting need pages.write, held by managers.

- **Sample pages**

  The sample shop adds three published pages, About us, Shipping and delivery, and Returns, in the store’s first language. A slug the store already uses is skipped, and erasing the sample shop removes only the pages it added.

### What it does not do

Read these before an order depends on it. No store is known to run GoCommerce in production yet, so these come from the code, not from anyone’s experience.

- **Language is fixed at creation** — An update cannot change a page’s language. To write a page in another language, create a new one with the same slug.
- **The body is kept as sent** — The admin edits the body in a rich-text editor. The module stores and serves it as it arrived; it does not render, clean or check it.
- **No history** — An update overwrites the page. There are no revisions to compare or restore, and a deleted page is gone.
- **No scheduled publishing** — A page is draft or published from the moment its status is set. There is no publish date in the future.
- **Flat pages** — A page has no parent and no position. The public list is newest first; linking pages into the storefront’s header or footer is the menus’ job.
- **No switch in the Plugins screen** — The module registers no plugin. Once installed, the public routes answer for every published page.

FAQ

### Questions about the Content pages module

**Where does the storefront read a page?**

GET /x/cms/pages/{slug} on the host that serves your GoCommerce API, which answers with the published page in the request’s language. GET /x/cms/pages lists the published pages in that language, a page at a time.

**What happens when a page has no translation?**

The module tries the store’s other languages, main first, and answers with the first published page it finds at that slug. A shopper reading in a language nobody has written the page in sees it in the main language rather than a 404.

**Who can edit pages?**

An operator with pages.write, which managers hold by default; an owner holds every right. Staff hold pages.read, so they can see pages in the admin but not change them. Either right can be granted to another role on the Roles screen.

**Can I preview a draft on the storefront?**

Not on a normal install. The public routes serve published pages only, unless Config.PublishedOnly is set to false, which the package keeps for a preview environment. The admin route GET /api/admin/x/cms/pages/{id} returns drafts.

### Related modules

Each is a directory under `ext/`, imported the same way. Where this one fits in the store: [How modules extend GoCommerce](https://kitcommerce.store/features/development/).

- [Contact form API in Go A GoCommerce contact form and product enquiries in one inbox: announced by email, answered from the admin, rate-limited, with a honeypot against bots.](https://kitcommerce.store/integrations/contact/)
- [Ecommerce FAQ API in Go Give a GoCommerce store a page of questions and answers: grouped into sections, ordered by hand, draft or published, served whole at /x/faq.](https://kitcommerce.store/integrations/faq/)
- [Customer accounts and login in Go Add shopper accounts to a GoCommerce store: sign-up, sign-in, saved addresses, password reset and order history by claim. Guest checkout keeps working.](https://kitcommerce.store/integrations/identity/)
- [Order and GST tax invoices in Go A numbered invoice for every paid GoCommerce order and, for Indian stores, a GST tax invoice with HSN codes and the tax split, exported to TallyPrime.](https://kitcommerce.store/integrations/invoices/)

### Source

Everything on this page is read from [`ext/cms`](https://github.com/itswadesh/gocommerce/tree/main/ext/cms) in the GoCommerce repository, MIT licensed. When this page and the code disagree, the code is right and this page is out of date.

- [ext/cms on GitHub](https://github.com/itswadesh/gocommerce/tree/main/ext/cms)
- [All GoCommerce modules](https://kitcommerce.store/integrations/)

### Try it against a store of your own

The backend + admin quick start runs GoCommerce’s API and admin on your own machine, without a storefront. Add this module to it and try it on test orders before a real one depends on it.

[Backend + admin quick start](https://kitcommerce.store/#one-command) · [Read the module](https://github.com/itswadesh/gocommerce/tree/main/ext/cms) · [All integrations](https://kitcommerce.store/integrations/)
