diff --git a/lms.routing.yml b/lms.routing.yml
index 22273b24b5925920eeeae9def965d943d2f98d5a..3642eb55adc459eb25da9484a7f21201c005581c 100644
--- a/lms.routing.yml
+++ b/lms.routing.yml
@@ -77,3 +77,11 @@ lms.answer.evaluate:
     parameters:
       lms_answer:
         type: entity:lms_answer
+
+lms.modal_subform_endpoint:
+  path: 'lms/modal-reference-form'
+  defaults:
+    _controller: 'Drupal\lms\Controller\LmsReferenceSubform::endpoint'
+    _title: 'LMS Reference form endpoint'
+  requirements:
+    _permission: 'administer lms'
diff --git a/src/Controller/LmsReferenceSubform.php b/src/Controller/LmsReferenceSubform.php
new file mode 100644
index 0000000000000000000000000000000000000000..6fa3a0e97b94c1472ddc0cc62f4d68c2dd580601
--- /dev/null
+++ b/src/Controller/LmsReferenceSubform.php
@@ -0,0 +1,69 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\lms\Controller;
+
+use Drupal\Core\Ajax\AjaxResponse;
+use Drupal\Core\Ajax\AlertCommand;
+use Drupal\Core\DependencyInjection\AutowireTrait;
+use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
+use Drupal\Core\Form\FormAjaxResponseBuilderInterface;
+use Drupal\Core\Form\FormBuilderInterface;
+use Drupal\Core\Form\FormState;
+use Drupal\lms\Form\LmsEntityCreationForm;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Add doc.
+ */
+final class LmsReferenceSubform implements ContainerInjectionInterface {
+
+  use AutowireTrait;
+
+  public function __construct(
+    private readonly FormBuilderInterface $formBuilder,
+    private readonly FormAjaxResponseBuilderInterface $ajaxResponseBuilder,
+  ) {}
+
+  /**
+   * Endpoint callback.
+   */
+  public function endpoint(Request $request): AjaxResponse {
+    $form = $this->formBuilder->getForm(LmsEntityCreationForm::class);
+
+    // Any additional code should not be executed because a FormException
+    // should be thrown.
+    $form_state = new FormState();
+    $form_id = $this->formBuilder->getFormId(LmsEntityCreationForm::class, $form_state);
+    $form_state->setRequestMethod('POST');
+    $input = $request->request->all();
+    $form_state->setUserInput($input);
+
+    $form = $this->formBuilder->getCache($input['form_build_id'], $form_state);
+    kdpm($form, 'FA');
+    // @todo Security - if form is not available, exit.
+    if (!\is_array($form)) {
+      $form = $this->formBuilder->retrieveForm($form_id, $form_state);
+      $this->formBuilder->prepareForm($form_id, $form, $form_state);
+      $cache_form_state = $form_state->getCacheableArray();
+      $cache_form_state['always_process'] = $form_state->getAlwaysProcess();
+      $cache_form_state['temporary'] = $form_state->getTemporary();
+      $form_state_before_retrieval = clone $form_state;
+      $form_state = $form_state_before_retrieval;
+      $form_state->setFormState($cache_form_state);
+    }
+
+    $form['#build_id_old'] = $request->request->get('form_build_id');
+
+    $form_state->disableRedirect();
+    $this->formBuilder->processForm($form_id, $form, $form_state);
+
+    $response = $this->ajaxResponseBuilder->buildResponse($request, $form, $form_state, []);
+    \assert($response instanceof AjaxResponse);
+    $response->setStatusCode(200);
+
+    return $response;
+  }
+
+}
diff --git a/src/Form/LmsEntityCreationForm.php b/src/Form/LmsEntityCreationForm.php
new file mode 100644
index 0000000000000000000000000000000000000000..8481eddfd7bfb2964ffdc634e7538be018197911
--- /dev/null
+++ b/src/Form/LmsEntityCreationForm.php
@@ -0,0 +1,161 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\lms\Form;
+
+use Drupal\Core\Ajax\AlertCommand;
+use Drupal\Core\Ajax\AjaxResponse;
+use Drupal\Core\Ajax\ReplaceCommand;
+use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
+use Drupal\Core\Form\FormBase;
+use Drupal\Core\Form\FormBuilderInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Modal LMS entity creation form.
+ */
+final class LmsEntityCreationForm extends FormBase {
+
+  private const FORM_SELECTOR = 'lms-entity-creation-form';
+
+  private array $bundleInfo;
+
+  /**
+   * The constructor.
+   */
+  public function __construct(
+    protected EntityTypeBundleInfoInterface $entityTypeBundleInfo,
+  ) {
+
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container) {
+    return new static(
+      $container->get('entity_type.bundle.info'),
+      $container->get('form_builder'),
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFormId() {
+    return 'lms_entity_creation_form';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    if ($form_state->get('lms_parent_form_data') === NULL) {
+      $form_state->set('lms_parent_form_data', $form_state->getBuildInfo()['args']);
+    }
+
+    $bundle_form = $this->getBundleForm($form, $form_state);
+    if ($bundle_form !== NULL) {
+      $form = $bundle_form;
+    }
+    else {
+      $form = $this->getEntityForm($form, $form_state);
+    }
+
+    $form['#attributes']['data-lms-selector'] = self::FORM_SELECTOR;
+
+    return $form;
+  }
+
+  private function getBundleForm(array $form, FormStateInterface $form_state): ?array {
+    $bundle = $form_state->get('lms_entity_form_bundle');
+    if ($bundle !== NULL) {
+      return NULL;
+    }
+
+    $bundle = $form_state->getValue('bundle');
+    if ($bundle !== NULL) {
+      $form_state->set('lms_entity_form_bundle', $bundle);
+      return NULL;
+    }
+
+    $entity_type_id = $this->getEntityTypeId($form_state);
+    $bundle_info = $this->entityTypeBundleInfo->getBundleInfo($entity_type_id);
+    if (\count($bundle_info) === 1) {
+      $form_state->set('lms_entity_form_bundle', \array_keys($bundle_info)[0]);
+      return NULL;
+    }
+
+    $bundle_options = [];
+    foreach ($bundle_info as $bundle_id => $data) {
+      $bundle_options[$bundle_id] = $data['label'];
+    }
+
+    $form['bundle'] = [
+      '#type' => 'radios',
+      '#title' => $this->t('Bundle'),
+      '#options' => $bundle_options,
+      '#ajax' => [
+        'callback' => [\get_class($this), 'bundleSelectionAjax'],
+        'url' => Url::fromRoute('lms.modal_subform_endpoint'),
+        'options' => [
+          'query' => [
+            FormBuilderInterface::AJAX_FORM_REQUEST => TRUE,
+          ],
+        ],
+      ],
+    ];
+
+    return $form;
+  }
+
+  public static function bundleSelectionAjax(array $form, FormStateInterface $form_state) {
+    $response = new AjaxResponse();
+    $form_selector = '[data-lms-selector="' . self::FORM_SELECTOR . '"]';
+    $response->addCommand(new ReplaceCommand($form_selector, $form));
+    return $response;
+  }
+
+  private function getEntityForm(array $form, FormStateInterface $form_state) {
+    $form['submit'] = [
+      '#type' => 'button',
+      '#value' => $this->t('Create and add'),
+      '#ajax' => [
+        'callback' => [\get_class($this), 'createEntityAjax'],
+        'url' => Url::fromRoute('lms.modal_subform_endpoint'),
+        'options' => [
+          'query' => [
+            FormBuilderInterface::AJAX_FORM_REQUEST => TRUE,
+          ],
+        ],
+      ],
+    ];
+    return $form;
+  }
+
+  public static function createEntityAjax(array $form, FormStateInterface $form_state) {
+    kdpm($form_state->get('lms_entity_form_bundle'), 'FA');
+    kdpm($form_state->getBuildInfo(), 'FA');
+    $response = new AjaxResponse();
+    $response->addCommand(new AlertCommand('ok.'));
+    return $response;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    // This is 
+  }
+
+  /**
+   * dev
+   */
+  private function getEntityTypeId(FormStateInterface $form_state) {
+    return $form_state->getBuildInfo()['args'][0] ?? 'lms_lesson';
+  }
+
+}
diff --git a/src/Plugin/Field/FieldWidget/LMSReferenceTable.php b/src/Plugin/Field/FieldWidget/LMSReferenceTable.php
new file mode 100644
index 0000000000000000000000000000000000000000..4a1a4bf7c8adf622b464a465f83d4259e41148da
--- /dev/null
+++ b/src/Plugin/Field/FieldWidget/LMSReferenceTable.php
@@ -0,0 +1,279 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Drupal\lms\Plugin\Field\FieldWidget;
+
+use Drupal\Core\Ajax\AjaxResponse;
+use Drupal\Core\Ajax\InvokeCommand;
+use Drupal\Core\Ajax\OpenModalDialogCommand;
+use Drupal\Core\Entity\Element\EntityAutocomplete;
+use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
+use Drupal\Core\Field\Attribute\FieldWidget;
+use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\Core\Field\FieldItemListInterface;
+use Drupal\Core\Field\WidgetBase;
+use Drupal\Core\Form\FormBuilderInterface;
+use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\StringTranslation\TranslatableMarkup;
+use Drupal\lms\Entity\Activity;
+use Drupal\lms\Entity\ActivityType;
+use Drupal\lms\Form\LmsEntityCreationForm;
+use Symfony\Component\DependencyInjection\ContainerInterface;
+
+/**
+ * Plugin implementation of the 'lms_reference_table' widget.
+ */
+#[FieldWidget(
+  id: 'lms_reference_table',
+  label: new TranslatableMarkup('LMS reference table'),
+  description: new TranslatableMarkup('Improved LMS entity reference widget.'),
+  field_types: ['lms_reference'],
+  multiple_values: TRUE,
+)]
+final class LMSReferenceTable extends WidgetBase {
+
+  private const TABLEDRAG_CLASS = 'lms-items-order-weight';
+
+  /**
+   * Constructor.
+   */
+  public function __construct(
+    $plugin_id,
+    $plugin_definition,
+    FieldDefinitionInterface $field_definition,
+    array $settings,
+    array $third_party_settings,
+    protected EntityTypeBundleInfoInterface $entityTypeBundleInfo,
+    protected FormBuilderInterface $formBuilder,
+  ) {
+    parent::__construct($plugin_id, $plugin_definition, $field_definition, $settings, $third_party_settings);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
+    return new static(
+      $plugin_id,
+      $plugin_definition,
+      $configuration['field_definition'],
+      $configuration['settings'],
+      $configuration['third_party_settings'],
+      $container->get('entity_type.bundle.info'),
+      $container->get('form_builder')
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsForm(array $form, FormStateInterface $form_state): array {
+    return [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function settingsSummary(): array {
+    return [];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function formElement(FieldItemListInterface $items, $delta, array $element, array &$form, FormStateInterface $form_state) {
+
+    $table = [
+      '#type' => 'table',
+      '#caption' => $this->t('References'),
+      '#header' => [
+        'weight' => $this->t('Weight'),
+        'title' => $this->t('Title'),
+        'type' => $this->t('Type'),
+        'parameters' => $this->t('Parameters'),
+        'remove' => $this->t('Remove'),
+      ],
+      '#tableselect' => FALSE,
+      '#tabledrag' => [
+        [
+          'action' => 'order',
+          'relationship' => 'sibling',
+          'group' => self::TABLEDRAG_CLASS,
+        ],
+      ],
+      '#empty' => $this->t('No references.'),
+      '#attributes' => ['data-lms-selector' => 'lms-reference-table'],
+    ];
+
+    $target_type = $this->fieldDefinition->getSetting('target_type');
+    $bundle_info = $this->entityTypeBundleInfo->getBundleInfo($target_type);
+
+    $references = $form_state->get('lms_references');
+    if ($references === NULL) {
+      $references = [];
+      foreach ($items as $item_delta => $item) {
+        $entity = $item->entity;
+        $references[$item_delta] = [
+          'weight' => $item_delta,
+          'entity_id' => $item->get('target_id')->getValue(),
+          'label' => $entity->label(),
+          'type' => $bundle_info[$entity->bundle()]['label'],
+        ];
+      }
+    }
+
+    foreach ($references as $reference) {
+      $table[$reference['entity_id']] = [
+        'weight' => [
+          '#type' => 'weight',
+          '#title' => $this->t('Weight for @title', [
+            '@title' => $reference['label'],
+          ]),
+          '#title_display' => 'invisible',
+          '#default_value' => $reference['weight'],
+          '#attributes' => ['class' => [self::TABLEDRAG_CLASS]],
+        ],
+        'title' => [
+          '#markup' => $reference['label'],
+        ],
+        'type' => [
+          '#markup' => $reference['type'],
+        ],
+        'parameters' => [],
+        'remove' => [
+          '#type' => 'button',
+          '#value' => $this->t('Remove'),
+          '#ajax' => [
+            'callback' => [\get_class($this), 'removeItem'],
+          ],
+        ],
+      ];
+
+    }
+
+    $element['table'] = $table;
+    $element['add_item'] = [
+      '#type' => 'button',
+      '#value' => $this->t('Add item'),
+      '#ajax' => [
+        'callback' => [$this, 'openAddModal'],
+      ],
+      '#limit_validation_errors' => [],
+    ];
+    //$element['#attached']['library'] = ['drupal.dialog.ajax'];
+
+    return $element;
+  }
+
+  public function openAddModal(array $form, FormStateInterface $form_state) {
+    $response = new AjaxResponse();
+    kdpm($form, 'FA');
+    $target_type = $this->fieldDefinition->getSetting('target_type');
+    $modal_form = $this->formBuilder->getForm(LmsEntityCreationForm::class, $target_type, $form_state);
+
+    $response = new AjaxResponse();
+    $response->addCommand(new OpenModalDialogCommand(
+      $this->t('Create entity'),
+      $modal_form,
+      ['width' => '80%']
+    ));
+    return $response;
+  }
+
+  /**
+   * LMS Activity data elements.
+   *
+   * @param mixed[] $element
+   *   Form element.
+   */
+  private function setActivityDataElements(array &$element): void {
+    $element['target_id']['#title'] = $this->t('Activity');
+    $element['data']['max_score'] = [
+      '#type' => 'number',
+      '#min' => 0,
+      '#max' => 100,
+      '#title' => $this->t('Maximum score'),
+      '#default_value' => 5,
+    ];
+    $element['target_id']['#ajax'] = [
+      'callback' => [static::class, 'updateMaxScore'],
+      'event' => 'autocompleteclose',
+    ];
+  }
+
+  /**
+   * LMS Lesson data elements.
+   *
+   * @param mixed[] $element
+   *   Form element.
+   */
+  private function setLessonDataElements(array &$element): void {
+    $element['target_id']['#title'] = $this->t('Lesson');
+    $element['data']['mandatory'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Mandatory'),
+      '#default_value' => TRUE,
+    ];
+    $element['data']['auto_repeat_failed'] = [
+      '#type' => 'checkbox',
+      '#title' => $this->t('Auto-repeat if failed'),
+      '#description' => $this->t("If a student didn't get the score required to pass this lesson, it will be restarted when trying to navigate to the next one."),
+      '#default_value' => FALSE,
+    ];
+    $element['data']['required_score'] = [
+      '#type' => 'number',
+      '#min' => 0,
+      '#max' => 100,
+      '#title' => $this->t('Required score [%]'),
+      '#default_value' => 50,
+    ];
+    $element['data']['time_limit'] = [
+      '#type' => 'number',
+      '#min' => 0,
+      '#title' => $this->t('Time limit [min]'),
+      '#description' => $this->t('After this much time has passed, the lesson will be finished with all remaining activities unanswered.'),
+      '#default_value' => 0,
+    ];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function massageFormValues(array $values, array $form, FormStateInterface $form_state) {
+    $values = parent::massageFormValues($values, $form, $form_state);
+    return $values;
+  }
+
+  /**
+   * Update max score value on new elements.
+   */
+  public static function updateMaxScore(array $form, FormStateInterface $form_state): ?AjaxResponse {
+    $trigger = $form_state->getTriggeringElement();
+    $entity_id = EntityAutocomplete::extractEntityIdFromAutocompleteInput($trigger['#value']);
+    if ($entity_id === NULL) {
+      return NULL;
+    }
+    $entity = Activity::load($entity_id);
+    if ($entity === NULL) {
+      return NULL;
+    }
+
+    // Value.
+    $activity_type = ActivityType::load($entity->bundle());
+    $default_max_score = $activity_type->getDefaultMaxScore();
+
+    // Selector.
+    $parents = $trigger['#parents'];
+    \array_pop($parents);
+    $parents[] = 'data';
+    $parents[] = 'max_score';
+    $selector = 'input[name="' . \array_shift($parents) . '[' . \implode('][', $parents) . ']"]';
+
+    // Command.
+    $response = new AjaxResponse();
+    $response->addCommand(new InvokeCommand($selector, 'val', [$default_max_score]));
+    return $response;
+  }
+
+}