Text Regions

A text region is any HTML element that editors can click to edit. You mark elements as editable by adding a data-cms-region or data-cms-global attribute.

Page-specific regions

Use data-cms-region for content that is unique to a single page:

<h1 data-cms-region="hero-title">Welcome to our site</h1>

<div data-cms-region="about-body">
  <p>We build websites for small businesses.</p>
</div>

The attribute value is the region name — a string that identifies this region on this page. It can be anything you like, but should be descriptive. Region names must be unique within a page.

The initial content inside the element becomes the default content. Once an editor saves a change, the saved content is loaded from the database on every subsequent request.

Global regions

Use data-cms-global for content that should be the same on every page — things like a phone number in the footer, a company address, or a site-wide promotional banner:

<footer>
  <p>Call us: <span data-cms-global="contact-phone">01234 567890</span></p>
  <p data-cms-global="footer-address">123 High Street, London</p>
</footer>

When an editor saves a global region, the change applies everywhere that region name appears across the whole site. This is ideal for shared information that needs to stay consistent.

Global regions inside partials: When you put a region inside a partial (using {cms:partial:slug}), it's automatically treated as global — even if you use data-cms-region. RuntCMS rewrites it to data-cms-global during partial processing.

Choosing the right container element

The region element is replaced by the editor's HTML when saved. Here are some guidelines:

Use div for multi-paragraph content

<div data-cms-region="about-text">
  <p>First paragraph.</p>
  <p>Second paragraph.</p>
</div>

Tiptap (the editor RuntCMS uses) wraps output in block elements. Using a <div> as the container works cleanly.

Avoid p as the outer container

Using <p> as the region container can cause issues because Tiptap outputs block elements (like another <p>) inside your <p>. Use <div>, <section>, or <span> depending on your needs.

Inline regions

For short inline text like a phone number or a tagline, use a <span>:

<span data-cms-global="tagline">Building better websites</span>

Region names

Region names are arbitrary strings. Follow these conventions:

  • Use lowercase with hyphens: hero-title, about-body
  • Be descriptive — the name shows up in the admin dashboard's recent edits list
  • Names must be unique within a single page (but can repeat across different pages)
  • Global region names must be unique across the whole site

Template tags inside region content

A small number of template tags work inside region and global region content. Most do not — tags like {cms:region}, {cms:global}, and {cms:image} are only processed in the page template file itself, not in saved region content. If you put them inside a region they will render as literal text.

Tags that do work inside region content:

  • {cms:login_link} — renders a login link for visitors, hidden when already logged in. Useful in a global footer region so it appears site-wide without adding it to every template.
  • {cms:partial:slug} — partials are resolved before region content is output, so a partial reference inside a region will work. Useful for injecting shared snippets.

Tags that do not work inside region content:

  • {cms:system:runtbar} — should appear exactly once per page, just before </body>, in the template file. Placing it inside a region would inject duplicate toolbars on every page that region appears on.
  • {cms:region}, {cms:global}, {cms:image}, {cms:bg-image}, {cms:page:*}, {cms:seo:*} — these are template-level tags, resolved before the page is rendered. They are not re-processed inside saved content.

Default content

The HTML inside the region element is the default content — what's shown before any editor has saved a change. Once saved, the database content takes over and the default content is ignored.

Default content is shown to visitors too, so make sure it's real content rather than a placeholder.

HTML sanitization

When content is saved, RuntCMS sanitizes the HTML. The following tags are allowed:

p, br, strong, em, s, u, h2, h3, h4, ul, ol, li, a, img, blockquote, code, pre, table, thead, tbody, tr, th, td, div, span

Script and style tags are stripped entirely. On <a> tags, only href, target, and rel are allowed. JavaScript URLs (href="javascript:...") are blocked.

This protects against XSS even if an editor's session is compromised.

Example page

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>{cms:page:title} — Acme Co</title>
  <meta name="description" content="{cms:page:description}">
  {cms:site:favicon}
  <link rel="stylesheet" href="{cms:site:path}/cms/css/site.css">
</head>
<body class="page-{cms:page:slug}">

  <header>
    <nav>{cms:nav:pages}</nav>
  </header>

  <section class="hero">
    <h1 data-cms-region="hero-title">We make great things</h1>
    <div data-cms-region="hero-copy">
      <p>Acme makes world-class widgets. Order yours today.</p>
    </div>
  </section>

  <footer>
    <p data-cms-global="footer-contact">hello@acme.example</p>
    <p>&copy; 2025 Acme Co {cms:login_link}</p>
  </footer>

  {cms:system:runtbar}
</body>
</html>

RuntCMS 0.9 Documentation