Recent searches

in

Visually edit complex arrays and page building

Learn how to define Array, Array Item, and Component Editable Regions to add, delete, and reorder array items in a complex array.

On this page

We've covered how to use Array, Array Item, and Component Editable Regions in the previous steps of this guide. We can use these Editable Regions in conjunction to enable visual editing for a complex array.

Array items in complex arrays can have different structured data keys. Complex arrays are often used to create a page-building experience, where each array item represents a block of content on your page. To enable visual editing for a complex array, including adding, deleting, and reordering items in the Visual Editor, we need to add Array, Array Item, and Component Editable Regions.

Nested text or image values in an array

To avoid data-prop misconfiguration errors, it is best practice to define your Editable Regions from the root of your file first (i.e., from parent elements to child elements). If you want to edit text or image values in an array using the Visual Editor, you should define your Array and Array Item Editable Regions on the parent element before Text and Image.

Arrays with different array items#

Just like simple arrays, complex array content is defined using structured data keys in the front matter of hybrid files (e.g., .mdx⁠) or in data files (e.g., .yml⁠) and referenced by templating in a layout file. Unlike simple arrays, complex arrays can have different structured data keys for each array item.

Let's take a look at an example.

Our website has an "About" page, where we display information about our company. We use a complex array to build this page, allowing us to add several different kinds of content blocks. The data for these content blocks is stored in the front matter of a Markdown file using array items with different structured data keys.

Here is our content file, about.md, containing the contentBlocks array.

Markdown
Copied to clipboard
---
layout: ../layouts/AboutLayout.astro
contentBlocks:
  - _name: HeroBlock
    title: We're on a mission
    description: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce lobortis mi sed est dapibus, sit amet consectetur quam euismod. Cras pretium quam vitae malesuada placerat. Curabitur ac sagittis ligula. Nunc sed ultrices leo. Vestibulum malesuada lobortis nisl, a feugiat est viverra et.
  - _name: StatsBlock
    stats:
      - figure: $200m
        text: Venture capital raised
      - figure: "2016"
        text: Established in
      - figure: 40+
        text: Amazing team members
      - figure: 44325+
        text: Active users and growing
  - _name: ContactBlock
    text: Want to get in contact with us?
    button: Click here
    image: https://preview.astro.new/blog/_astro/blog-placeholder-about.BtEdEmGp_1cKsnD.webp
---

Content from each array item in the contentBlocks array populates a component specific to that block. Here are the component files we use for our three blocks: Hero, Stats, and Contact.

ASTRO
Copied to clipboard
---
const { title, description } = Astro.props;
---

<h2>{title}</h2>
<p>{description}</p>
ASTRO
Copied to clipboard
---
const { stats } = Astro.props;
---

<div>
  {
    stats.map(({ figure, text }) => {
      return (
        <div>
          <p>{figure}</p>
          <p>{text}</p>
        </div>
      );
    })
  }
</div>
ASTRO
Copied to clipboard
---
const { text, button, image } = Astro.props;
---

<img src={image} />
<p>{text}</p>
<button type="button">{button}</button>

Finally, our page layout file, AboutLayout.astro, loops over all the items in the contentBlocks array, finds the appropriate component for each item and populates that component using values from about.md. Here is an excerpt from our About layout file.

ASTRO
Copied to clipboard
---
const components = {};
const componentImports = import.meta.glob("../components/**/*.astro", {
  eager: true,
});
Object.entries(componentImports).forEach(([path, obj]) => {
  const name = path.replace("../components/", "").split(".")[0];
  components[name] = obj.default;
});

const { contentBlocks } = Astro.props.frontmatter;
---

<main>
  {
    contentBlocks.map((block) => {
      const Component = components[block._name];
      return (
        <section>
          <Component {...block} />
        </section>
      );
    })
  }
</main>

Together, these files output a webpage that looks like this:

A screenshot of the About page on our Astro website shows header content, some statistics, an image, and a button.

To enable visual editing for this complex array, we can add Array, Array Item, and Component Editable Regions.

Let's start by registering our content blocks as components.

Just like the previous step of this guide, we need to install the Editable Regions integration for our SSG. Next, we can create a registration script including each of our content blocks, informing CloudCannon that they should be bundled for client-side use in the Visual Editor.

JavaScript
Copied to clipboard
import { registerAstroComponent } from '@cloudcannon/editable-regions/astro';
import '@cloudcannon/editable-regions/astro-react-renderer'; 
import HeroBlock from '../components/HeroBlock.astro';
import StatsBlock from '../components/StatsBlock.astro';
import ContactBlock from '../components/ContactBlock.astro';

// Register your components
registerAstroComponent('HeroBlock', HeroBlock);
registerAstroComponent('StatsBlock', StatsBlock);
registerAstroComponent('ContactBlock', ContactBlock);

This import is only necessary if you use React components nested inside your Astro components.

Finally, we can add Editable Regions to our AboutLayout.astro file.

Just like the previous step, we need to import our component registration file with a <script> HTML element inside the <body> element of our layout. If your layouts share a base layout file, the <script> element may already be there.

The <main> element wraps the array in our layout file, with its contents looping over every array item stored under the contentBlocks key. Like a simple array, let's add the data-editable="array" and data-prop="contentBlocks" HTML attributes to the <main> DOM element. For a complex array, we also need to add the data-id-key and data-component-key HTML attributes. The data-id-key attribute defines which structured data key CloudCannon should look at for each array item's unique identifier. The data-component-key defines which structured data key CloudCannon should look at for the name of the registered component. For simplicity's sake, we registered our components using the same name as the unique identifier, so the value for both these attributes is _name.

The <section> element wraps each array item in our layout file. Like a simple array, let's add the data-editable="array-item" HTML attribute to the <section> DOM element. For a complex array, we also need to add the data-id and data-component HTML attributes. These attributes define the unique identifier of an array item and the name of a component, respectively. We need these fields to populate for each array item at build time, so the value for both these attributes will be the templating {block._name}.

Here's what our layout file should look like:

ASTRO
Copied to clipboard
---
const components = {};
const componentImports = import.meta.glob("../components/**/*.astro", {
  eager: true,
});
Object.entries(componentImports).forEach(([path, obj]) => {
  const name = path.replace("../components/", "").split(".")[0];
  components[name] = obj.default;
});

const { contentBlocks } = Astro.props.frontmatter;
---

<script> 
  if (window.inEditorMode) {
    import("../scripts/register-components.js").catch((error) => {
      console.warn("Failed to load CloudCannon component registration:", error);
    });
  }
</script>
<main data-editable="array" data-prop="contentBlocks" data-id-key="_name" data-component-key="_name">
  {
    contentBlocks.map((block) => {
      const Component = components[block._name];
      return (
        <section data-editable="array-item" data-id={block._name} data-component={block._name}>
          <Component {...block} />
        </section>
      );
    })
  }
</main>

The <script> element instructs your browser to import the register-components.js file if your webpage is open in the Visual Editor.

If we also want to visually edit the text and images inside each array item, we can define Text and Image Editable Regions in each of the layout files defining our content blocks: HeroBlock.astro, StatsBlock.astro, and ContactBlock.astro.

Once we save and rebuild our Site, CloudCannon will show a yellow Editable Region box around each array item, and around any Text or Image Editable Regions you added inside them.

A screenshot of the Visual Editor shows yellow Editable Region boxes around each of the three content blocks on the About webpage.

We can now build pages in the Visual Editor. We can add, duplicate, move, or delete content blocks using the options in the Context Menu, which opens when we click the drag handle icon on an array item, or reorder content blocks by dragging the drag handle icon.

Common errors#

If you accidentally misconfigure your Editable Regions, CloudCannon will replace them in the Visual Editor with a red Error Card headed Failed to render array editable, or Failed to render array item for a misconfigured Array Item Editable Region or a new array item CloudCannon cannot render, followed by a message describing the problem.

A screenshot of an Error Card in the Visual Editor shows a message headed Failed to render array editable, followed by the reason the region could not render.

Here are a few common errors you might encounter with Array Editable Regions:

  • You did not define the data-prop HTML attribute.
  • You did not define the data-id-key or data-id HTML attributes for a complex array.
  • CloudCannon cannot render a new array item, because your Array Editable Region has no <template> element, no data-component or data-component-key HTML attribute, and no existing array item it can copy.
  • The value at the data-prop path is not an array (e.g., it is a string, a number, or an object, or no value exists at that path).
  • You have one or more orphaned Array Item Editable Regions whose nearest parent Editable Region is not an Array Editable Region.

And that's it! Your Site is built, has output Collections, and you have learned how to apply every type of Editable Region to enable visual editing. In the next step of this guide, we'll direct you to resources for further configuration or user guides on how to edit your files in CloudCannon.

Set up Visual Editing (10/11)
Visually edit complex arrays and page building
Open in a new tab