Block Class

<?php

/**
 * @file
 * Contains \Drupal\my_module\Plugin\Block\MyBlock.
 */

namespace Drupal\my_module\Plugin\Block;

use Drupal\Core\Block\BlockBase;
use Drupal\Core\Form\FormStateInterface;


/**
 * Provides a 'My Block Description Here' block.
 *
 * @Block(
 *   id = "my_module_my_block_block",
 *   admin_label = @Translation("My short Block Description Here"),
 *   category = @Translation("MyBlockGroup")
 * )
 */

class MyBlock extends BlockBase {

  /**
   * {@inheritdoc}
   * Define your default configuration values here
   */
  public function defaultConfiguration() {
    return [
      'sampleVariable' => 'value',
    ];
  }


  /**
   * {@inheritdoc}
   * Use this to create a block configuration form
   */
  public function blockForm($form, FormStateInterface $form_state) {
    $config = $this->configuration;

    $form['display_options'] = array(
      '#type' => 'details',
      '#title' => $this->t('<i>My Block Configuration</i>'),
      // Open if not set to defaults.
      '#open' => true,
      '#process' => [[get_class(), 'processDisplayOptionsParents']],
    );

    $form['display_options']['sampleVariable'] = array(
      '#type' => 'select',
      '#multiple' => TRUE,
      '#title' => $this->t('Sample Selector'),
      '#default_value' => $config['sampleVariable'],
      '#options' => array('1' => 'One', '2' => 'Two', '3' => 'Three'),
      '#required' => TRUE,
    );

    return $form;
  }


  /**
   * Form API callback: Processes the display_options field element to adjust
   * the #parents of the details container to save its children at the top level.
   */
  public static function processDisplayOptionsParents(&$element, FormStateInterface $form_state, &$complete_form) {
    array_pop($element['#parents']);
    return $element;
  }


  /**
   * {@inheritdoc}
   * Stores the settings submitted through the block configuration form.
   * You can do fun data transformations here if needed
   */
  public function blockSubmit($form, FormStateInterface $form_state) {
    $this->configuration['sampleVariable'] = $form_state->getValue('sampleVariable');
  }


  /**
   * {@inheritdoc}
   * Called to generate the output used to display your custom block.
   * Must return a valid render array
   */
  public function build() {

    return array(
      '#markup' => "<p>My configuration value is " . $this->configuraiton['sampleVariable'] . "</p>",
      '#attached' => array(
        'library' => array('my_module/block'),
      ),
      '#cache' => array(
        'max-age' => 0,
      ),
    );

  }

}

Notes:

  • The @Block section in the comments at the top of the file is very important and must be formatted and configured correctly.  Otherwise, your file won't be loaded.

  • The filename must match the classname (e.g. "MyBlock.php" for the example above) and be placed in "src/Plugin/Block/" relative to your module's root directory..

  • Anything that starts with "my_" or "My_" should be adjusted to fit your module.  In particular, anything, any instance of "my_module" should be replaced with the machine name of your module.

  • A configuration form is purely optional.  If your block doesn't need to be configured, then you can leave out everything relating to the block form.

  • The #markup example in the sample render array is valid, but it is preferred that you use actual render objects whenever possible.  See XYZ for a list of possible render objects.  You can also create your own render objects by adding your own TWIG templates to your module