# Design blocks and sections

> Plan and build the blocks editors place on pages, step by step, so every page has one main heading, headings in order, fields editors can fill in, and a website that renders exactly what SEO health checks.

Source: https://docs.bifrostorbit.com/cms/developers/design-components/

Editors build pages from blocks: a hero, a text section, a grid of cards. You define each block once as a **component**, your website draws it, and SEO health checks the pages editors make with it. This guide takes one block from plan to page, and sets up the headings so that every page editors build has one main heading and headings in order, without them having to think about it.

## Before you start

- You can open **Schema › Components** and **Schema › Content types**, which needs the right to manage the schema.
- Your website already renders pages from CMS Orbit. If not, start with [Connect a website](https://docs.bifrostorbit.com/cms/developers/connect-a-website/).
- Three words used below:
  - A **content type** is a kind of page or entry, such as a page, an article or a trip.
  - A **component** is a block editors place on a page, such as a hero or a text section.
  - The **main heading** is the page's one H1, the heading that says what the page is about.

## 1. Decide where each page's main heading comes from

Every page needs exactly one H1. Search engines read it as the page's subject, and screen reader users jump to it first. CMS Orbit can only check it if it knows which part of the page your website shows as the H1. Go through your content types and pick one of three patterns for each:

| The H1 comes from | Example | What to set |
| --- | --- | --- |
| A block at the top of the page | A hero or page header on a landing page | **Main heading of the page** on the component |
| The entry's own field, printed by your template | A trip's title, an article's headline | **Main heading of the page** on the content type |
| A block that is the title only sometimes | A text block with a "use as page title" switch, for pages without a hero | **Main heading of the page** and **Only when** on the component |

Pick one pattern per content type. If a trip's template prints the title as the H1, don't also allow a hero on trips, or the page gets two.

## 2. Plan the heading levels

Headings below the H1 should go down one level at a time, never skip one:

```
H1  Lofoten ridge walk
  H2  Who it suits
    H3  Fitness
  H2  Day by day
```

- **Sections are H2.** A block that starts a new part of the page, such as "What's included" or "Questions", has an H2 heading.
- **Items inside a section are H3.** Cards in a grid, questions in a list, days in an itinerary.
- **Rich text follows the block it sits in.** Inside a section whose heading is an H2, editors use H3 in the text. In a text field directly under the H1, they use H2.

CMS Orbit checks the headings it can see: the main heading you declare, headings inside rich text, and a block's heading level when the block has a `level` field (see step 4). **It can't see headings your website's templates add on their own.** If a template prints a heading in front of editors' content, especially a hidden one, SEO health reports the content's headings as skipping a level, even though the page is fine. Either leave such headings out, or give the content the level that follows them.

## 3. Create the component

1. Open **Schema › Components** and select **New component**.
2. Enter a **Name** editors will recognise, such as `Text section`. The **Key** is made from it (`textSection`) and is what your website uses; it can't be changed later.
3. Choose where it may go:
   - **Allowed at the top of a page** for blocks that make up the page.
   - **Can sit inside another component** for blocks that live in a grid or a section.
4. Select **Create component**, then open it to add its fields. A typical section has:

   | Field | Type | Notes |
   | --- | --- | --- |
   | Heading | Text | Required, so a section never has an empty heading |
   | Heading level | Select, key `level` | Choices `H2` and `H3`, default `H2`; see step 4 |
   | Text | Rich text | |
   | Image | Asset | Editors add alt text in the media library |

   [Field types](https://docs.bifrostorbit.com/cms/developers/field-types/) lists every type and its settings.
5. If this block provides the page's main heading (step 1), choose its heading field under **Main heading of the page**. If it does so only when a switch is on, choose that yes/no field under **Only when**.

A component whose key contains `hero` is taken to provide the H1 when nothing else is set. Set **Main heading of the page** anyway, so the rule is written down and the right field is checked.

## 4. Let editors choose a heading's level, where it can vary

Some blocks sit at different depths: a card grid can be a section of its own (H2) or part of a larger section (H3). Give such blocks a **Select** field with the key `level` and the choices `H2`, `H3` and so on. The values are stored as `h2`, `h3`, which CMS Orbit reads as that block's heading level, so SEO health checks the order on every page. Set a default, usually `H2`, so editors only change it when they need to.

Blocks whose heading is always at the same level don't need the field. Then CMS Orbit doesn't count their heading, and only checks the headings it can see.

## 5. Allow it where it belongs

1. Open **Schema › Content types** and the content type, such as **Page**.
2. Under **Components at the top of the page**, add the new component.
3. For content types whose template prints the H1 (step 1), open the **Search** step and choose the field under **Main heading of the page**. Leave hero-style blocks out of their allowed components.

## 6. Render it on your website

Draw the block with the heading tag that matches what you set in CMS Orbit. Type it with `BlockProps` and register it as in [Render components](https://docs.bifrostorbit.com/cms/developers/render-blocks/).

```tsx
import type { BlockProps } from '@bifrost-orbit-cms/sdk/react'
import { RichText } from '@bifrost-orbit-cms/sdk/react'

export function TextSection({ block }: BlockProps<'textSection'>) {
  const Heading = block.level ?? 'h2'
  return (
    <section>
      <Heading className="section-heading">{block.heading}</Heading>
      <RichText value={block.text} />
    </section>
  )
}
```

A block with **Only when** renders its heading as the H1 only while the switch is on:

```tsx
export function Text({ block }: BlockProps<'text'>) {
  const Heading = block.asTitle ? 'h1' : 'h2'
  return <Heading>{block.heading}</Heading>
}
```

Choose the tag by meaning and the look by class. If an H2 should look smaller in one place, give it a class, not an H3.

## 7. Check it before editors start

1. Create a test page named `TEMP heading check`, and place every block you made on it, with a heading in each and an H2 and H3 in the rich text.
2. Publish it to Staging and open it on your website. In the browser's developer tools, check there is exactly one `<h1>` and the headings go down in order.
3. In CMS Orbit, open **SEO › Health**. Checks run on drafts as soon as they are saved, so the test page is already checked: under **Issues by impact**, none of the heading issues (**No main heading**, **More than one main heading**, **Headings that skip a level**, **Empty headings**) should list it.
4. If your Staging site has a website address, the live check in SEO health compares what CMS Orbit expects with the page as your website serves it. See [Improve SEO health](https://docs.bifrostorbit.com/cms/editors/seo-health/).
5. Delete the test page.

## Checklist

- [ ] Every routable content type has one source of the H1: a component or the content type's own field.
- [ ] Blocks that provide the H1 have **Main heading of the page** set, and **Only when** if it depends on a switch.
- [ ] The heading field of a block that provides the H1 is required.
- [ ] Section blocks render H2, items inside them H3, and blocks that move between levels have a `level` field.
- [ ] Templates don't add headings in front of editors' content, or the content follows them.
- [ ] Your website renders the same tags CMS Orbit expects, and a test page passes SEO health.

## Common problems

| SEO health says | Usually because | Fix |
| --- | --- | --- |
| No main heading | No block or field is marked as the H1 | Step 1 and step 3, item 5 |
| No main heading, on pages without a hero | The H1 comes from a switch the schema doesn't know about | Set **Only when** |
| More than one main heading | A hero on a page whose template already prints the title | Remove the hero from that content type's allowed components |
| Headings that skip a level | A heading the template adds, or rich text one level too deep | Step 2 |
| Empty headings | An optional heading field left empty on a block that renders it | Make the heading required, or render nothing when it is empty |
