Inline Blocks


Inline Blocks combine the content modelling flexibility of regular Blocks with the improved editing experience of Inline Editing.

Please note: This is considered an unstable API. Any breaking changes will be shared in the weekly Release Notes. That said, we will do our best to keep this document up-to-date.

TinaCMS: Inline Blocks Gif

Creating a Block

A block is made of two parts: a component that renders in edit mode, and a template to configure fields, defaults and other required data.

interface Block {
  Component: React.FC<BlockComponentProps>
  template: BlockTemplate
}

Part 1: Block Component

import { BlocksControls, BlockTextArea } from 'react-tinacms-inline'

// Example 'Heading' Block
export function Heading(props) {
  return (
    <BlocksControls index={props.index}>
      <BlockTextarea name="text" />
    </BlocksControls>
  )
}

The Block Component is passed index, its position in the block order, and data, the source data.

interface BlockComponentProps {
  index: number
  data: any
}

Since it renders in 'edit mode,' this component should display BlocksControls and at least one Block Field. BlocksControls is the UI for editing, deleting, or moving blocks. A Block Field is, at its most basic, an inline input field stripped of styling to blend in with the site.

TinaCMS: Inline Block Controls

The image above shows the Textarea Block field in use.

Available Block Fields:

Part 2: Block Template

export const heading_template = {
  type: 'heading',
  label: 'Heading',
  defaultItem: {
    text: 'At vero eos et accusamus',
  },
  key: 'heading-block',
  fields: [],
}

The Inline Block Template configures the block with the CMS. It has a similar shape to a Regular Block Template definition.

KeyTypePurpose
typestringThis value connects source block data with the template.
labelstringA human readable label.
defaultItem?object \| (() => object)Populates new blocks with default data.
keystringA unique value to optimize the rendering of the list
fieldsArrayPopulates fields in the Settings Modal(link to settings modal).

Configuring Inline Blocks with Inline Form

The initial steps to configuring Inline Blocks involve setting up an Inline Form on the page or component where the blocks should render. Then, you should add controls to handle editing state. Finally, you can use a component called InlineBlocks that renders blocks in order based on the source data.

The Steps:

  1. Wrap your component with InlineForm, pass the form object.
  2. Set up Inline Controls.
  3. Configure InlineBlocks, pass the name and blocks values.
import { useJsonForm } from 'next-tinacms-json'
import { InlineForm, InlineBlocks } from 'react-tinacms-inline'
import {
  BodyCopy,
  body_copy_template,
  Heading,
  heading_template,
  Image,
  image_template,
} from './blocks'
import { EditToggle, DiscardButton, SaveButton } from './inline-ui'

/*
 ** Example 'PageBlocks' Component
 */

export default function PageBlocks({ jsonFile }) {
  // Creates the form
  const [, form] = useJsonForm(jsonFile)

  return (
    <InlineForm form={form}>
      <EditToggle />
      <SaveButton />
      <DiscardButton />
      <InlineBlocks name="blocks" blocks={PAGE_BLOCKS} />
    </InlineForm>
  )
}

/*
 ** Multiple blocks are grouped into a single object,
 ** with their associated Component and template values.
 ** This object is passed to InlineBlocks
 */

const PAGE_BLOCKS = {
  heading: {
    Component: Heading,
    template: heading_template,
  },
  body_copy: {
    Component: BodyCopy,
    template: body_copy_template,
  },
  image: {
    Component: Image,
    template: image_template,
  },
}

InlineBlocks Interface

To be configured properly, InlineBlocks requires name and blocks.

interface InlineBlocksProps {
  name: string
  blocks: {
    [key: string]: Block
  }
}
KeyTypePurpose
namestringThe path to the source data for the blocks.
blocksobjectAn object composed of individual Blocks.

Blocks Source Data

The source data for the blocks in the example above could look something like this:

// Example blocks JSON source file
{
  "blocks": [
    {
      "_template": "image",
      "src": "/img/bali-1.jpg",
      "alt": "bali-viaje"
    },
    {
      "_template": "heading",
      "text": "Ne quaesiveris extra."
    },
    {
      "_template": "body_copy",
      "text": "Sed ut perspiciatis unde omnis iste natus error."
    }
  ]
}

The key ("blocks" in this example) for the array of individual blocks must match the name value passed to InlineBlocks.

Each individual block object must have a _template value to connect its data with a block template. This value should match the type value defined in the associated block template.

Using the Settings Modal

There may be times when a Block needs more field inputs than a single inline field. For example, an image may need a field for the β€˜alt’ tag. For this metadata, you can use ModalProvider and add additional fields to the Block Template.

TinaCMS: Inline Blocks Settings Modal

1. Define Fields in the Block Template

Field definitions added to the fields property will render in the Settings Modal. These fields are defined exactly the same as regular sidebar fields.

const image_template = {
  type: 'image',
  label: 'Image',
  defaultItem: {
    src: '/img/bali-1.jpg',
    alt: '',
  },
  key: 'image-block',
  /*
   ** Define fields to render
   ** in a Settings Modal form
   */
  fields: [
    {
      name: 'alt',
      label: 'Alt Text',
      component: 'text',
    },
  ],
}

2. Use the field data in your Block Component

You can then use that field data in the Block Component. In this example, the data will populate the alt attribute for the image.

export function Image({ data, index }) {
  return (
    <>
      <div>
        <BlocksControls index={index}>
          <BlockImage
            name="src"
            parse={filename => `/img/${filename}`}
            uploadDir={() => '/public/img/'}
          >
            {/*
             ** The 'alt' data from the
             ** 'settings' is consumed
             */}
            <img src={data.src} alt={data.alt} />
          </BlockImage>
        </BlocksControls>
      </div>
    </>
  )
}

3. Add the ModalProvider

To use the ModalProvider, wrap it around the InlineForm that renders InlineBlocks. This will provide the Settings Modal view. The fields defined in the block template will populate the form in that modal.

import { useJsonForm } from 'next-tinacms-json'
import { ModalProvider } from 'tinacms'
import { InlineForm, InlineBlocks } from 'react-tinacms-inline'

export default function IndexBlocks({ jsonFile }) {
  const [, form] = useJsonForm(jsonFile)

  // Wrap InlineForm with the ModalProvider
  return (
    <ModalProvider>
      <InlineForm form={form}>
        {/*
         ** //...
         */}
      </InlineForm>
    </ModalProvider>
  )
}