InnerBlocks, and a block that contains other blocks

A container block — a card, a column, a callout with arbitrary content — needs the editor to nest blocks inside it, and that is one component rather than a custom editing surface.

edit() {
  return (
    <div className="wp-block-turkerdev-card">
      <InnerBlocks
        allowedBlocks={['core/heading', 'core/paragraph', 'core/image']}
        template={[['core/heading', { level: 3 }], ['core/paragraph']]}
        templateLock={false}
      />
    </div>
  );
},

save() {
  return <div className="wp-block-turkerdev-card"><InnerBlocks.Content /></div>;
}

InnerBlocks.Content in save is what serialises the children into the post, and omitting it produces a block that edits correctly and saves nothing — a mistake that is only visible after a reload. allowedBlocks is worth setting because an unconstrained container accepts anything including another copy of itself. templateLock: 'all' makes the structure fixed, which suits a layout block and frustrates an editor who wants one more paragraph.