How can I programmatically create a block type?
I had the same issue and I hope I have resolved it.
Your algorithm is next:

Create your own module
Create file &lt;your_module&gt;.install
There must be at least one hook hook_install (I have used two: hook_install and hook_uninstall)
Inside install hook:

create a new block type
create fieldStorageConfig
create fieldConfig
set component for form and view display



My code:
&lt;?php

use Drupal\block_content\Entity\BlockContentType;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;

function &lt;your_module_name&gt;_install() {
  $newBlockType = BlockContentType::create([
    'id' =&gt; &lt;your_id&gt;,
    'label' =&gt; 'Custom block type',
    'description' =&gt; &quot;A custom block type&quot;,
  ]);
  $newBlockType-&gt;save();

  $blockId = $newBlockType-&gt;id();

  block_content_add_field($blockId, 'Icon', 'custom_block_icon', 'image', 'image', 'image_image');
  block_content_add_field($blockId, 'Count', 'custom_block_count', 'integer', 'number_integer', 'number');
  block_content_add_field($blockId, 'Description', 'custom_block_description', 'text', 'text_default', 'text_textfield');

}
function block_content_add_field(
  $blockTypeId,
  $label,
  $componentName,
  $fieldStorageType,
  $viewDisplayType = 'text_default',
  $formDisplayType = 'text_textarea_with_summary') {
    // Add or remove the body field, as needed.
    $field = FieldConfig::loadByName('block_content', $blockTypeId, $componentName);
    if (empty($field)) {
      $fieldStorage = FieldStorageConfig::loadByName('block_content', $componentName);
      if (empty($fieldStorage)) {
        $fieldStorage = FieldStorageConfig::create([
          'field_name' =&gt; $componentName,
          'entity_type' =&gt; 'block_content',
          'type' =&gt; $fieldStorageType,
        ]);
        $fieldStorage-&gt;save();
      }

      $field = FieldConfig::create([
        'field_storage' =&gt; $fieldStorage,
        'bundle' =&gt; $blockTypeId,
        'label' =&gt; $label,
        'settings' =&gt; ['display_summary' =&gt; FALSE],
      ]);

      $field-&gt;save();

      /** @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface $display_repository */
      $display_repository = \Drupal::service('entity_display.repository');

      // Assign widget settings for the default form mode.
      $display_repository-&gt;getFormDisplay('block_content', $blockTypeId)
        -&gt;setComponent($componentName, [
          'type' =&gt; $formDisplayType,
        ])
        -&gt;save();

      // Assign display settings for default view mode.
      $display_repository-&gt;getViewDisplay('block_content', $blockTypeId)
        -&gt;setComponent($componentName, [
          'label' =&gt; 'hidden',
          'type' =&gt; $viewDisplayType,
        ])
        -&gt;save();
    }

    return $field;
}

Result: the new custom block type with 3 fields.