Pages and blocks

Every page on your site is a stack of blocks. A block is one piece of a page: some text, a picture, a button, a map, a pair of columns. You build a page by adding blocks in the page editor and filling in their fields. No code is needed.

This chapter takes you from an empty page to a published one. By the end you can add a page, lay blocks out side by side, put a code sample on a page, change the header and menu that every page shares, and decide who may open a page.

Add a page

  1. Open Content → Pages and press New page.
  2. Type a Title. The URL is filled in from the title, and you can change it. Leave Parent page on “None (top level)” unless the page belongs under another one.
  3. Press Create. The page editor opens on the empty page.
  4. Drag a block from the Blocks list on the left into the preview in the middle.
  5. Click the block in the preview. Its fields open on the right, and the preview changes as you edit them.
  6. Press Save draft, then Preview to see the saved draft in a new tab.
  7. Press Publish. The badge at the top changes from Draft to Live.

A page has two versions. Saving a draft changes only the draft, so visitors keep seeing the live page until you publish. History lists every saved version, newest first. Restore on an older one publishes it again and makes it your draft.

Find your way around the editor

The left side has the Blocks list, where you pick blocks to add, and an Outline tab that shows the page as a tree, which helps when blocks sit inside other blocks. The middle is a preview of the page. The right side shows the fields of the block you have selected.

The bar along the top has the page's name and address, then ← Pages (back to the list), a Live or Draft badge, Settings, History, Preview, Save draft and Publish.

A block's fields are grouped. Content holds what the block says or shows. Settings holds how it looks and behaves. Spacing, width and other size fields sit in groups of their own that start closed. Show on and Show to come last.

Two things are called settings. The Settings button in the top bar opens the page's title, parent page and URL, with Delete and Unpublish. Clicking an empty part of the preview, outside every block, opens the page's own fields on the right: Layout, Page title, Meta description, Social image URL, Search engines and Access. The four in the middle are explained in Search engines.

Choose a block

The Blocks list is grouped by what the blocks are for. Most of what you need is under Elements:

To do thisUse
Write text, headings and listsText
Show a pictureImage
Show a set of photographs that open largerGallery
Add a button or a plain linkButton, Link
Put blocks side by sideColumns
Put small items on one line, such as iconsInline
Stack blocks with an even gapRows
Give part of the page its own background and widthSection
Add empty space or a lineSpacer, Divider
Add a symbol or a social media markIcon
Show places on a mapMap (Maps)
Questions and answersFAQ for text answers, Accordion for panels that hold any blocks
A few cards, each with a title, text, picture and linkCard grid
Prices or opening timesTable
Logos of sponsors or partnersLogo strip
Commands and configurationCode

The other groups are Hero (the opening band of a page), Site (Navigation, Footer and Page content, which belong in layouts), Lists (the “List of…” block and the blocks that show one field or link of an item), and Users (sign-in, registration and password forms, and the account icon). Forms, Polls, Questionnaires, Commerce, Posts and Appointments appear only on a site that has that feature (Try it on your computer shows how to add one). Custom holds blocks your own site adds.

Lay a page out

Some blocks hold other blocks. The place inside a block where other blocks go is called a slot. A Section has one slot and Columns has one for each column. A block inside a slot can have slots of its own.

Put blocks side by side

  1. Drag a Columns block onto the page.
  2. Set Columns to 2, 3 or 4. The block shows a slot for each: Column 1, Column 2 and so on.
  3. Drag a block into each slot. A Text block in one and an Image in the other is the usual pair.
  4. With two columns, Ratio (two columns) sets their widths: 1:1, 2:1, 1:2, auto:fill or fill:auto. The last two size one column to its content, such as a logo, and give the rest of the width to the other.
  5. Set Stack below. On screens narrower than this the columns pile up into one. The sizes md and lg are the theme's breakpoints, 48rem and 64rem unless you changed them. Choose never to keep the columns side by side at every width.

Order when stacked can be Reversed, which puts the last column first once they pile up. That suits a picture beside text when you want the text on top on a phone.

Give part of the page its own background

A Section is a band across the full width of the page. Put one around each part of a page and drag that part's blocks into its slot. A Section has a Background and Text colour, Top & bottom padding and Side padding, Corners, a Width (how wide the content may grow before it is centred) and Text alignment.

Stack blocks, or keep them on one line

Rows stacks two to six slots with an even gap and can draw a rule between them. Inline puts small blocks on one line, each at its own width: a few social icons, or a button beside a line of text. Its slot is called Items. Drop a new block between two others, or drag an item sideways to move it. Align offers Left, Centre, Right and Spread out. Setting Wrap onto more lines to No keeps the row on one line and shrinks its items instead.

Space things out

You never type a padding or a colour as a number. Each field offers the sizes, colours and text styles the theme defines. These named choices are the theme's tokens. They keep every page consistent, and a change in Settings → Theme restyles the whole site.

Text, Image, Button, Link, Spacer, Divider and Inline have a Spacing field: padding around the block's own box, for each side a theme size or a pixel value. Icon, Map and Code have the same field, called Padding. Use it for a one-off gap. To space every block in a group evenly, use the gap of the Columns, Rows or Inline block that holds them, or add a Spacer.

Change a value for larger screens

Padding, gaps, widths and heights can differ by screen size. Such a field has a Default, which applies from the smallest screens up, and one entry for each larger size, labelled “≥ md (48rem)” and so on. Leave an entry on Unchanged to keep the value from the size below it.

Show a block on some screens only

Every block has a Show on field with a tick box for each screen size: Phone, Tablet, Laptop and Desktop, each with the widths it covers. A block that is not ticked for a size is hidden at that size. Each column of a Columns block has its own, under Show columns, and so does each row of a Rows block. In the editor a hidden block stays in the preview, dimmed, so you can still select it.

Show code on a page

The Code block, in Elements, shows commands, configuration files and snippets. It colours the code for its language, can number the lines, mark some of them, and gives visitors a button that copies all of it. Use it instead of a preformatted paragraph in a Text block, which stays plain.

Add a code sample

  1. Drag a Code block onto the page.
  2. Click it and paste your code into the Code box. Paste rather than type: the box leaves the Tab key alone, so Tab moves you to the next field instead of indenting.
  3. Choose the Language: Plain text, Bash / shell, YAML, JSON, TypeScript, JavaScript, TSX, JSX, HTML, CSS, Dockerfile, INI, .env, SQL, Diff, Markdown or Caddyfile. Plain text is not coloured.
  4. Type a File name or caption if you want one. It shows in a bar above the code, so compose.yaml tells the reader which file the lines belong to. Leave it empty for no bar.
  5. Press Save draft and check the preview.

Options

FieldWhat it does
ColoursSite default follows the colour scheme chosen in Settings → Theme. Pick one of the eight schemes to give this block its own (see Theme).
Line numbersYes draws a number beside each line. The numbers are not copied with the code.
Highlight linesGives some lines a lighter background. Write line numbers separated by commas, and a range with a hyphen: 2, 5-7 marks line 2 and lines 5 to 7. Lines are counted from the first line of the code, whether or not numbers show. Anything else in the box is ignored.
Wrap long linesYes folds a long line onto the next. No, the default, keeps each line whole and lets the code scroll sideways.
Copy buttonYes, the default, shows a button that copies all the code.
Copy button textThe button's words. The default is Copy.
Text after copyingWhat the button says for two seconds after a click. The default is Copied.
PaddingSpace around the block. By default it has some above and below.

The two button texts appear only while Copy button is Yes. They are fields so that you can write them in your visitors' language.

The copy button sits in the top right corner of the block, or at the end of the file name bar when there is one. It appears once the page has loaded in the visitor's browser, so nobody sees a button that does nothing. In the editor it is drawn but does not copy.

Icons

The Icon block opens a picker with a tab for each set of icons. Lucide is always there: about 1,850 symbols, picked by name. Brands comes with the cms and holds brand:facebook, brand:instagram, brand:x, brand:linkedin and brand:bluesky. Use a brand mark to link to your own page on that service, and give the icon a label that says where it goes, because the label is all a screen reader has. The marks are their owners' trademarks.

A developer can add a tab of the site's own icons. An icon is path data, not an image file, so there is nothing to upload. Define the set in the site's block package, blocks/index.ts, and list it under icons, keeping the rest of the file as it is. A style of 'fill', the default, draws a solid shape. A style of 'stroke' draws a line like the Lucide icons, and the block's Stroke width applies.

blocks/index.ts
import { defineBlockPackage, defineIconSet } from '@jamiemcconnell/cms/blocks';

const ourIcons = defineIconSet({
  name: 'site',
  label: 'Our icons',
  icons: {
    chevron: { title: 'Chevron', paths: ['M6 4l8 8-8 8'], style: 'stroke' },
  },
});

export default defineBlockPackage({
  name: 'site',
  blocks: [
    // Your own blocks stay here, with any collections and styles you have.
  ],
  icons: [ourIcons],
});

The new tab is called Our icons, and the icon is picked as site:chevron.

The header and footer: layouts

A layout is the frame around every page: the header at the top and the footer at the bottom. You edit it once and every page changes. The layout that every page uses unless it says otherwise is called default.

  1. Open Content → Layouts and open default.
  2. Edit it like a page: header blocks at the top, a Page content block in the middle, footer blocks at the bottom. Page content marks where each page's own blocks appear, so leave it in place.
  3. Press Publish. Every page that uses the layout changes.

To use another layout on one page, click an empty part of the page's preview and change its Layout field: Default, another named layout, or None, which gives the page no header and footer so you can build your own. Make a new layout with New layout on Content → Layouts and choose Site layout as its Type. Email layouts are covered in Emails.

Menus

A menu is an ordered list of links. The Navigation block shows one, usually in the header of a layout.

  1. Open Content → Menus and press New menu. Give it a Name. The Key is how blocks refer to it, and it is filled in from the name.
  2. Press Add link. Type a Label and either a Link or, under Page, pick one of your pages.
  3. Use the arrows to move a link up or down. Nest under the link above indents a link, and nested links open as a drop-down under their parent. Leave a link empty to make a heading that only opens its nested links.
  4. Press Save.
  5. Select the Navigation block and set Links from to “Menu: your menu's name”.

Links from has two other choices. Pages lists your pages as they are nested; to nest a page under another, set its Parent page in the Settings button, as these docs are nested under Docs. Custom links lets the block carry a list of its own. The Who sees it column on a menu link limits that link to some visitors, in the same way as Access below.

On small screens the block folds into a menu button that opens a panel, unless On small screens is set to inline, which keeps the links on show. The slot at the end, After the links, takes things like an account icon, a basket icon or a button.

Heroes

A hero is the large band at the top of a page. There are four, under Hero. Simple hero is a band of colour. Image hero puts text over a photo, with an overlay to keep the text readable. Split hero puts text beside a picture. Full-screen hero is a photo behind a Centre slot that takes any blocks, such as a logo, a heading and a tagline, optionally on a tinted or solid panel.

The simple, image and split heroes share their copy fields: Eyebrow (a short line above the heading), Heading, Kicker lines, Text, an Accent rule and up to three Calls to action, each with a Label, Link, Style and Size. Content width sets how wide that copy may be, and a slot below holds anything else you drop in. Every hero has the same frame fields: Padding, Width, Alignment, Vertical position, Height, Background and Text colour.

Height offers Auto (the content's own height, the default), Medium, Large, Half screen, Fill screen below nav and Full screen, and it can differ by screen size. Fill screen below nav ends the band exactly at the bottom of the window, whatever height the header is, which suits the opening band of a home page. In the editor's preview it shows as a full screen.

To make a slideshow, add two or more pictures under Photos (Pictures, in a Split hero). More fields appear: Transition, Seconds per photo, Controls and a Pause button. Keep the pause button on, because moving pictures need a way to stop them.

A developer can add a hero of the site's own with pnpm cms generate hero Splash. It is listed under Hero as “Custom: Splash”.

Lists

A list repeats one design for many items: posts, products, your orders, or the items of a collection you define yourself.

  1. Drag a List of… block onto the page and choose its Source. Posts and Products appear only on a site that has those features.
  2. Narrow it with Filter, pick an Order, and set How many, up to 48. Skip the first leaves out the first few.
  3. Choose a Layout: Columns, Rows or Slider. Columns and Slider have Columns per row, from 1 to 8, which can differ by screen size.
  4. Build one item in the Item slot from ordinary blocks. In a Text, Image, Button, Link or Icon block, open Connect to item and pick the field to show. The post, product and order blocks already read the current item.
  5. Set Link the whole item to Yes to turn each item into a link to its own page.

On the live site the list shows the current items. In the editor you see one sample item. A Slider can move by itself: Auto slide every sets the seconds between moves, 0 turns it off. It waits while a mouse is over it or keyboard focus is inside it, and its Pause button, on by default, lets a visitor stop it. The arrows can be restyled: Arrow style, shape, size, colour, icon, and position under the track or at its sides. Page through the items adds previous and next links under Columns and Rows lists.

A developer can add a source of the site's own, such as a team read from a JSON file, with pnpm cms generate collection team.

Who can open a page

Click an empty part of the preview and use the Access field. The choices are Everyone, Signed in, Editors and admins, and Admins. A site with membership also offers “Only these groups”. The checkbox “Signed out visitors only” shows a page only to people who are not signed in. Access takes effect when you publish.

A visitor who is not signed in is sent to the sign-in page and brought back afterwards. A signed-in visitor whose role falls short sees a notice inside the site's layout in place of the page. Pages and posts can be locked. System pages, layouts, templates and emails cannot.

On a page, Also protect every page under this one locks the pages nested beneath it too, now and in future. Without it, each page keeps its own Access. A locked page is never listed for anyone: it stays out of the page menu, the sitemap, post lists and the public API. Link to it from a menu, the account page or a members page of your own, and let the page itself do the gating.

Individual blocks can be limited as well, with the Show to field at the end of a block's fields. Accounts and sign-in covers who can sign in and how roles work.

Where to go next

Theme changes colours, fonts and spacing for the whole site. Search engines covers titles, descriptions and the sitemap. Collections lets you keep lists of your own, and Your own blocks is for developers who want blocks that do not exist yet.