How to add InnerBlock multiple times in the same block

You don’t

That’s not how blocks work, and it’s not how HTML works. If you want multiple areas to insert blocks into, you need multiple blocks.

This is the same as saying that a box has multiple insides, or that there is more than one “inside a HTML tag”, it does not make sense as a concept.

Instead, do what the official core blocks do. E.g Columns.

  • Columns block
    • column
      • paragraph
      • paragraph
    • column
      • paragraph
      • paragraph

A columns block can only contain individual column blocks, and it’s the individual column blocks that contain the content.

enter image description here

Tabs would work the same:

  • Tabs
    • tab
      • paragraph
      • paragraph
    • tab
      • paragraph
      • paragraph

Notice as well that what you’re asking for is also not possible to represent in bullet points without accidentally coming up with the exact same solution.

It’s the same with HTML tags, if I want to have a <div> container with 2 internal spaces I have to put 2 tags inside it to act as the separate spaces. Why would blocks be any different.

Note that the block appender UI is just the default UI, and nothing prevents you passing a custom appender component to the inner blocks UI, or even building one into the block itself.

Here’s an example taken straight from the readme for the InnerBlocks components appender attribute doc:

// Utilise a predefined component
<InnerBlocks
    renderAppender={ InnerBlocks.ButtonBlockAppender }
/>

// Don't display an appender
<InnerBlocks
    renderAppender={ false }
/>

// Fully custom
<InnerBlocks
    renderAppender={ MyAmazingAppender }
/>

Here’s an appender component:

function render( { clientId } ) {
    return (
        <div>
            <p>Some rendered content here</p>
            <ButtonBlockAppender rootClientId={ clientId } />
        </div>
    );
}

https://github.com/WordPress/gutenberg/tree/trunk/packages/block-editor/src/components/button-block-appender

Here is how the tabs block from ultimate GB addons does it:

        const { insertBlock } = !wp.blockEditor ? dispatch( 'core/editor' ) : dispatch( 'core/block-editor' );
        const tabItemBlock = createBlock('uagb/tabs-child');

        insertBlock(tabItemBlock, attributes.tabHeaders.length, clientId);

Here they use a uaggb/tabs block to provide the UI and contain the tabs, and a uaggb/tabs-child block to contain the content of each tab and provide tab specific attributes

Useful links: