Components - InnerBlocks

Inner blocks allow you to insert additional blocks to your blocks. Under the hood, Blockstudio is using the InnerBlocks component. Please note that it is only possible to use one InnerBlocks component per block, this is a WordPress limitation.

Basic usage

To use InnerBlocks, you need to add the InnerBlocks component to your block.

index.php
<InnerBlocks /> or
<InnerBlocks/>
Copy

The composition is up to you. You can nest the InnerBlocks component as deep as you want or just use it by itself.

Properties

allowedBlocks

The allowedBlocks prop allows you to define which blocks can be inserted into the InnerBlocks component.

index.php
index.twig
<InnerBlocks allowedBlocks="<?php echo esc_attr(wp_json_encode(['core/heading', 'core/paragraph'])); ?>" />
Copy
<InnerBlocks allowedBlocks="{{ ['core/heading', 'core/paragraph']|json_encode|escape('html_attr') }}" />
Copy

tag

By default, the InnerBlocks component is rendered as a div element. You can change this by using the tag prop.

index.php
<InnerBlocks tag="section" />
Copy

template

The template prop allows you to define a template for the InnerBlocks component.

index.php
index.twig
<InnerBlocks template="<?php echo esc_attr( wp_json_encode([['core/heading', ['placeholder' => 'Book Title']], ['core/paragraph', ['placeholder' => 'Summary']]])); ?>" />
Copy
<InnerBlocks template="{{ [['core/heading', { placeholder: 'Book Title' }], ['core/paragraph', { placeholder: 'Summary' }]]|json_encode|escape('html_attr') }}" />
Copy

templateLock

The templateLock prop allows you to define if the template can be modified or not.

  • contentOnly: prevents all operations. Additionally, the block types that don't have content are hidden from the list view and can't gain focus within the block list. Unlike the other lock types, this is not overrideable by children.
  • all: prevents all operations. It is not possible to insert new blocks. Move existing blocks or delete them.
  • insert: prevents inserting or removing blocks, but allows moving existing ones.
  • false: prevents locking from being applied to an InnerBlocks area even if a parent block contains locking.

renderAppender

The renderAppender prop allows you to define the block appender type.

  • default: display the default block appender, typically the paragraph style appender when the paragraph block is allowed.
  • button: display a + icon button as the appender.

useBlockProps

useBlockProps will remove the outer wrapper of the InnerBlocks component inside the editor. See useBlockProps for more information.

Context

Registering

Blockstudio supports context for the InnerBlocks component. This allows you to pass data from the parent block to the child blocks. While it uses the WordPress core context mechanism under the hood, Blockstudio provides a more convenient API.

Instead of having to define all attributes that should be passed in the context separately, you can simply subscribe to all attributes of a parent block.

Let's say you have a container block setup like so:

block.json
{
  "$schema": "https://app.blockstudio.dev/schema",
  "name": "blockstudio/container",
  "title": "Container",
  "category": "design",
  "icon": "star-filled",
  "description": "Container block.",
  "blockstudio": {
    "attributes": [
      {
        "id": "full-width",
        "type": "toggle",
        "label": "Makes section full width"
      }
    ]
  }
}
Copy

Inside your child block, simply use the usesContext property with the parent block name to gain access to all parent attributes in your block template.

block.json
{
  "$schema": "https://app.blockstudio.dev/schema",
  "name": "blockstudio/element",
  "title": "Element",
  "category": "design",
  "icon": "star-filled",
  "description": "Element block.",
  "usesContext": ["blockstudio/container"],
  "parent": ["blockstudio/container"],
  "blockstudio": true
}
Copy

Templates

The $context and $c variables are available inside your block templates to access all parent attributes. Child blocks will automatically reload in the editor if parent data changes.

index.php
index.twig
<?php $container = $context['blockstudio/container']; ?>

<h1>
  <?php echo $container['full-width']
      ? 'is full width'
      : 'no full width'; ?>
</h1>
Copy
{% set container = context['blockstudio/container'] %}

<h1>
  {{ container['full-width']
      ? 'is full width'
      : 'no full width' }}
</h1>
Copy

Frontend wrapper

InnerBlocks has to render a wrapper element around its children inside the editor, however, it can be removed from the frontend by using blockstudio/blocks/components/innerblocks/frontend/wrap filter.

functions.php
add_filter('blockstudio/blocks/components/innerblocks/frontend/wrap', function ($render, $block) {
  if ($block->name === 'blockstudio/my-block') {
    $render = false;
  }

  return $render;
}, 10, 2);
Copy

Return early

If you want to return early from the render method of your block, you can check if the $innerBlocks (it contains the <InnerBlocks/> content) variable is empty. This comes in handy if you don't want to render anything if there are no inner blocks.

index.php
<?php if (strip_tags($innerBlocks) === '') {
  return;
} ?>
<InnerBlocks tag="section" />
Copy

Since <InnerBlocks/> is will always render an empty paragraph tag, you can use the strip_tags function to check if the content is empty.

Dynamic templates

The Select and Radio field types support dynamically generating the InnerBlocks template depending on the field value. This is useful if you want to allow the user to select a different template for the InnerBlocks component.

index.php
<InnerBlocks tag="section" />
Copy

To do so, simply add a innerBlocks key to the options array with the template.

block.json
{
  "$schema": "https://app.blockstudio.dev/schema",
  "name": "blockstudio/type-select-innerblocks",
  "title": "Native Select InnerBlocks",
  "category": "blockstudio-test-native",
  "icon": "star-filled",
  "description": "Native Blockstudio Select InnerBlocks block.",
  "blockstudio": {
    "attributes": [
      {
        "id": "layout",
        "type": "select",
        "label": "Layout",
        "options": [
          {
            "value": "1",
            "label": "One",
            "innerBlocks": [
              {
                "name": "core/columns",
                "innerBlocks": [
                  {
                    "name": "core/column",
                    "innerBlocks": [
                      {
                        "name": "core/heading",
                        "attributes": {
                          "content": "This is the left Heading.",
                          "level": 1
                        }
                      },
                      {
                        "name": "core/paragraph",
                        "attributes": {
                          "content": "This is the left Paragraph."
                        }
                      }
                    ]
                  },
                  {
                    "name": "core/column",
                    "innerBlocks": [
                      {
                        "name": "core/heading",
                        "attributes": {
                          "content": "This is the right Heading.",
                          "level": 1
                        }
                      },
                      {
                        "name": "core/paragraph",
                        "attributes": {
                          "content": "This is the right Paragraph."
                        }
                      }
                    ]
                  }
                ]
              }
            ]
          },
          {
            "value": "2",
            "label": "Two",
            "innerBlocks": [
              {
                "name": "core/columns",
                "innerBlocks": [
                  {
                    "name": "core/column",
                    "innerBlocks": [
                      {
                        "name": "core/heading",
                        "attributes": {
                          "content": "This is the left Heading.",
                          "level": 2
                        }
                      },
                      {
                        "name": "core/paragraph",
                        "attributes": {
                          "content": "This is the left Paragraph."
                        }
                      }
                    ]
                  },
                  {
                    "name": "core/column",
                    "innerBlocks": [
                      {
                        "name": "core/heading",
                        "attributes": {
                          "content": "This is the right Heading.",
                          "level": 2
                        }
                      },
                      {
                        "name": "core/paragraph",
                        "attributes": {
                          "content": "This is the right Paragraph."
                        }
                      }
                    ]
                  }
                ]
              }
            ]
          }
        ],
        "allowNull": "Select an option"
      }
    ]
  }
}
Copy
× Close

Menu

× Close

Search

  • /activating
  • /assets
    • code-field
        #basic-usage

        My block

        My block

        #scoped-selector
        #example

        My block

        #automatic-rendering
        #in-extensions
        #example-1
    • processing
        #minification
        #scss
        #import-paths
        #es-modules
        #setup
        #caveats
        #version
        #same-modules
        #css-loader
    • registering
        #inline
        #scoped
        ">

        Scope me!

        Scope me too!

        h1 { color: red; } The above will result in the following scoped style: <div class="bs-62df71e6cc9a"> <h1>Scope me!</h1> <p>Scope me too!</p> </div>

        #global
        #admin
        #block-editor
        #per-block
        my slider
        my slider

  • /attributes
    • block-attributes
        #rendering

        The block is aligned:

        The block is aligned:

        The block is aligned: {{ block.align }}

        The block is aligned: {{ b.align }}

        #defaults
    • conditional-logic
        #operators
        #global
        #custom
        #block
        #repeater
    • disabling
    • field-types
    • filtering
        #in-editor
        #on-frontend
    • html-utilities
        #data-attributes

        >

        {{ a.message }}

        The bs_render_attributes function will render the attributes as data attributes on the element.

        Hello

        The bs_render_attributes function accepts a second parameter to specify the attributes to render. If no attributes are specified, all attributes will be rendered.

        >

        {{ a.message }}

        The above example will render the following HTML:

        Hello

        If you don't want to render the output, simply use the bs_attributes function.

        #css-variables

        {{ a.message }}

        Hello

        Similarly, the bs_render_variables function accepts a second parameter to specify the attributes to render. If no attributes are specified, all attributes will be rendered.

        {{ a.message }}

        The above example will render the following HTML:

        Hello

        If you don't want to render the output, simply use the bs_variables function.

        #string-conversion
    • populating-options
        #query
        #arguments
        #return-format
        #fetch
        #function
        #arguments-1
        #return-format-1
        #mixing
        #custom-data
        #external-data
    • registering
    • rendering

      {{ attributes.message }}

      {{ a.message }}

      Attribute values render false if the field is empty or no option has been selected.

      {{ a.message }}

  • /code-snippets
    • #setup
    • #styles-and-scripts
    • #custom-directories
  • /components
    • innerblocks
        #basic-usage
        #allowedblocks
        #tag
        #template
        #templatelock
        #renderappender
        #useblockprops
        #registering
        #templates

        {{ container['full-width'] ? 'is full width' : 'no full width' }}

        #frontend-wrapper
        #return-early
        #dynamic-templates
    • mediaplaceholder
        #basic-usage
        #accept
        #allowedtypes
        #autoopenmediaupload
        #disabledropzone
        #dropzoneuionly
        #icon
        #isappender
        #disablemediabuttons
        #labels
    • richtext
        #basic-usage
        #allowedformats
        #placeholder
        #preservewhitespace
        #tag
        #withoutinteractiveformatting
    • useblockprops
        #usage
        Blockstudio will automatically combine classes and attributes from the editor (alignment classes etc.) along with whatever is defined in the block templates. get_block_wrapper_attributes is being used under the hood for that.

        #root-element
        First root element.
        Second root element. Similarly, useBlockProps can only be used once per block template.

        #-sign
  • /composer
    • #env
    • #composer-json
  • /context

    This is the data of current element inside the loop:

    This is the data the current post:

    This is the data of current element inside the loop: {{ block.context.postId }} {{ block.context.postType }}

    This is the data the current post: {{ block.postId }} {{ block.postType }}

  • /editor
    • examples
        #usage
        #select
        #images
    • general
        #architecture
        #safety
        #setup
        #preview-assets
        #outlook
    • gutenberg
        #limitations
    • tailwind
        #setup
        #how-does-it-work
        #recompiling
  • /environment
    • #editor
    • #preview
  • /extensions
    • #extension-types
    • #setup
    • #file-name
    • #schema
    • #name
    • #blockstudio-key
    • #position
    • #priority
    • #attributes
    • #getting-values
    • #arrays
    • #attributes-field
  • /general
    • #introduction
    • #core-focused
    • #server-side-first
    • #file-system-based
    • #no-setup
  • /hooks
    • javascript
        #refresh
        #loaded
        #rendered
        #update
        #example
        #with-key
    • php
        #path
        #global
        #instance
        #global-before
        #global-before-instance
        #render
        #meta
        #conditions
        #attributes
        #attributes-render
        #attributes-populate
        #components-useblockprops-render
        #components-innerblocks-render
        #components-innerblocks-frontend-wrap
        #components-richtext-render
        #path-1
        #enable
        #process-scss-importpaths
        #process-css-content
        #process-js-content
        #global-1
        #head
        #footer
  • /initialization
  • /library
  • /loading
    • #disabling
  • /overrides
    • #block-json
    • #attributes
    • #complex-structures
    • #assets
    • #rendering-template
    • #twig-blocks

      Images:

      Instead of replacing the whole template, we can use Twigs extends feature to override certain parts of the template while keeping the rest intact. {% block image %} <figure> <img src="{{ image.url }}" class="image" /> <figcaption>{{ image.caption }}</figcaption> </figure> {% endblock %}

  • /post-meta
    • #getting-post-meta
    • #acf
    • #metabox
    • #refreshing-blocks
  • /preview
    • #custom-data

      {{ a.title }}

    • #innerblocks
  • /registration
    • #block-json
    • #template

      My first native block.

    • #twig
    • #conditional-logic
    • #custom-icon
    • #html
      :-)
      " } }

    • #custom-paths
    • #filter
    • #instances
    • #filter-metadata
  • /rendering
    • #without-data
    • #with-data
    • #nesting

      echo bs_block([ 'id' => 'blockstudio/cta', 'data' => [ 'title' => 'My title', 'subtitle' => 'My subtitle', ], 'content' => bs_block([ 'id' => 'blockstudio/cta', 'data' => [ 'text' => 'Button Text', ], ]) ]); The button block will be rendered in place of the $content variable inside the block template.

    • #multiple-slots

      echo bs_block([ 'id' => 'blockstudio/cta', 'data' => [ 'title' => 'My title', 'subtitle' => 'My subtitle', ], 'content' => [ 'beforeContent' => bs_block([ 'id' => 'blockstudio/badge', 'data' => ['text' => 'Before Content'], ]), 'afterContent' => bs_block([ 'id' => 'blockstudio/cta', 'data' => ['text' => 'Button Text'], ]) ] ]);

  • /schema
    • #add-to-json
  • /settings
    • #json
    • #filters
    • #admin
  • /transforms
    • #block-to-block
    • #enter
    • #prefix
  • /variations
    • #hiding-attributes
× Close

Navigation

× Close

Search

  • /activating
  • /assets
    • code-field
        #basic-usage

        My block

        My block

        #scoped-selector
        #example

        My block

        #automatic-rendering
        #in-extensions
        #example-1
    • processing
        #minification
        #scss
        #import-paths
        #es-modules
        #setup
        #caveats
        #version
        #same-modules
        #css-loader
    • registering
        #inline
        #scoped
        ">

        Scope me!

        Scope me too!

        h1 { color: red; } The above will result in the following scoped style: <div class="bs-62df71e6cc9a"> <h1>Scope me!</h1> <p>Scope me too!</p> </div>

        #global
        #admin
        #block-editor
        #per-block
        my slider
        my slider

  • /attributes
    • block-attributes
        #rendering

        The block is aligned:

        The block is aligned:

        The block is aligned: {{ block.align }}

        The block is aligned: {{ b.align }}

        #defaults
    • conditional-logic
        #operators
        #global
        #custom
        #block
        #repeater
    • disabling
    • field-types
    • filtering
        #in-editor
        #on-frontend
    • html-utilities
        #data-attributes

        >

        {{ a.message }}

        The bs_render_attributes function will render the attributes as data attributes on the element.

        Hello

        The bs_render_attributes function accepts a second parameter to specify the attributes to render. If no attributes are specified, all attributes will be rendered.

        >

        {{ a.message }}

        The above example will render the following HTML:

        Hello

        If you don't want to render the output, simply use the bs_attributes function.

        #css-variables

        {{ a.message }}

        Hello

        Similarly, the bs_render_variables function accepts a second parameter to specify the attributes to render. If no attributes are specified, all attributes will be rendered.

        {{ a.message }}

        The above example will render the following HTML:

        Hello

        If you don't want to render the output, simply use the bs_variables function.

        #string-conversion
    • populating-options
        #query
        #arguments
        #return-format
        #fetch
        #function
        #arguments-1
        #return-format-1
        #mixing
        #custom-data
        #external-data
    • registering
    • rendering

      {{ attributes.message }}

      {{ a.message }}

      Attribute values render false if the field is empty or no option has been selected.

      {{ a.message }}

  • /code-snippets
    • #setup
    • #styles-and-scripts
    • #custom-directories
  • /components
    • innerblocks
        #basic-usage
        #allowedblocks
        #tag
        #template
        #templatelock
        #renderappender
        #useblockprops
        #registering
        #templates

        {{ container['full-width'] ? 'is full width' : 'no full width' }}

        #frontend-wrapper
        #return-early
        #dynamic-templates
    • mediaplaceholder
        #basic-usage
        #accept
        #allowedtypes
        #autoopenmediaupload
        #disabledropzone
        #dropzoneuionly
        #icon
        #isappender
        #disablemediabuttons
        #labels
    • richtext
        #basic-usage
        #allowedformats
        #placeholder
        #preservewhitespace
        #tag
        #withoutinteractiveformatting
    • useblockprops
        #usage
        Blockstudio will automatically combine classes and attributes from the editor (alignment classes etc.) along with whatever is defined in the block templates. get_block_wrapper_attributes is being used under the hood for that.

        #root-element
        First root element.
        Second root element. Similarly, useBlockProps can only be used once per block template.

        #-sign
  • /composer
    • #env
    • #composer-json
  • /context

    This is the data of current element inside the loop:

    This is the data the current post:

    This is the data of current element inside the loop: {{ block.context.postId }} {{ block.context.postType }}

    This is the data the current post: {{ block.postId }} {{ block.postType }}

  • /editor
    • examples
        #usage
        #select
        #images
    • general
        #architecture
        #safety
        #setup
        #preview-assets
        #outlook
    • gutenberg
        #limitations
    • tailwind
        #setup
        #how-does-it-work
        #recompiling
  • /environment
    • #editor
    • #preview
  • /extensions
    • #extension-types
    • #setup
    • #file-name
    • #schema
    • #name
    • #blockstudio-key
    • #position
    • #priority
    • #attributes
    • #getting-values
    • #arrays
    • #attributes-field
  • /general
    • #introduction
    • #core-focused
    • #server-side-first
    • #file-system-based
    • #no-setup
  • /hooks
    • javascript
        #refresh
        #loaded
        #rendered
        #update
        #example
        #with-key
    • php
        #path
        #global
        #instance
        #global-before
        #global-before-instance
        #render
        #meta
        #conditions
        #attributes
        #attributes-render
        #attributes-populate
        #components-useblockprops-render
        #components-innerblocks-render
        #components-innerblocks-frontend-wrap
        #components-richtext-render
        #path-1
        #enable
        #process-scss-importpaths
        #process-css-content
        #process-js-content
        #global-1
        #head
        #footer
  • /initialization
  • /library
  • /loading
    • #disabling
  • /overrides
    • #block-json
    • #attributes
    • #complex-structures
    • #assets
    • #rendering-template
    • #twig-blocks

      Images:

      Instead of replacing the whole template, we can use Twigs extends feature to override certain parts of the template while keeping the rest intact. {% block image %} <figure> <img src="{{ image.url }}" class="image" /> <figcaption>{{ image.caption }}</figcaption> </figure> {% endblock %}

  • /post-meta
    • #getting-post-meta
    • #acf
    • #metabox
    • #refreshing-blocks
  • /preview
    • #custom-data

      {{ a.title }}

    • #innerblocks
  • /registration
    • #block-json
    • #template

      My first native block.

    • #twig
    • #conditional-logic
    • #custom-icon
    • #html
      :-)
      " } }

    • #custom-paths
    • #filter
    • #instances
    • #filter-metadata
  • /rendering
    • #without-data
    • #with-data
    • #nesting

      echo bs_block([ 'id' => 'blockstudio/cta', 'data' => [ 'title' => 'My title', 'subtitle' => 'My subtitle', ], 'content' => bs_block([ 'id' => 'blockstudio/cta', 'data' => [ 'text' => 'Button Text', ], ]) ]); The button block will be rendered in place of the $content variable inside the block template.

    • #multiple-slots

      echo bs_block([ 'id' => 'blockstudio/cta', 'data' => [ 'title' => 'My title', 'subtitle' => 'My subtitle', ], 'content' => [ 'beforeContent' => bs_block([ 'id' => 'blockstudio/badge', 'data' => ['text' => 'Before Content'], ]), 'afterContent' => bs_block([ 'id' => 'blockstudio/cta', 'data' => ['text' => 'Button Text'], ]) ] ]);

  • /schema
    • #add-to-json
  • /settings
    • #json
    • #filters
    • #admin
  • /transforms
    • #block-to-block
    • #enter
    • #prefix
  • /variations
    • #hiding-attributes