Creating a table with pagination in a Node Edit form I have a content type article that has a field for Category. In order to help our editors find related articles, I included a table on the article Edit page that pulls in other article entities that have the same category as the current one.

I did this by implementing a hook_form_alter hook and adding some markup to the $form object after querying for all related articles.

// Query for matching articles
$query = \Drupal::entityQuery('node');
$query-&gt;condition('type', 'article');
$query-&gt;condition('field_articles_category.entity:taxonomy_term.tid', $categoryId);
$query-&gt;sort('created', $sortDirection);
$query-&gt;range($offset, $limit);
$query-&gt;latestRevision();

/* Omitting logic to load the Node objects by revision ID's */
// $articles is an array of Node objects of type article

$articlesTable = []
foreach ($articles as $index =&gt; $article) {
   $articlesTable[$index] ['articleTitle'] = [
      '#type' =&gt; 'link', '#title' =&gt; $article-&gt;getTitle(), '#url' =&gt; $article-&gt;toUrl()
   ]
}

$form['related_articles'] = [
   '#type' =&gt; 'details',
   '#title' =&gt; 'Articles of the Same category',
   'articlesTable' =&gt; $articlesTable
];


The table displays just fine and shows the expected articles. But there are sometimes hundreds of articles in a particular category so I would like to paginate this table for performance reasons (and to make sure the page is not too long to scroll through). How can I paginate such a table?

I've seen some articles mention creating a pagination controller and somehow setting the AJAX callback for that controller, but haven't found a concrete example yet to follow.
You can use tableselect instead of details something like:

   $query = \Drupal::database()-&gt;select('node', 'n')
      -&gt;condition('type', 'article', '=');
    $query-&gt;fields('n', ['nid']);
    $query-&gt;innerJoin('node__field_articles_category', 'category', 'category.field_articles_category_target_id = ' . $categoryId);
    //For the pagination we need to extend the pagerselectextender and
    //limit in the query
    $pager = $query-&gt;extend('Drupal\Core\Database\Query\PagerSelectExtender')
      -&gt;limit($limit);
    $article_ids = $pager-&gt;execute()-&gt;fetchAll(\PDO::FETCH_OBJ);
    $rows = [];
    foreach ($article_ids as $index =&gt; $article_id) {
      $article = Node::load($article_id-&gt;nid);
      $rows[$index + 1] = [
        'title' =&gt; $article-&gt;getTitle(),
        'url'   =&gt; Drupal::l($article-&gt;getTitle(), $article-&gt;toUrl()),
      ];
    }

    $form['related_articles']['table'] = [
      '#type'    =&gt; 'tableselect',
      '#header'  =&gt; ['title' =&gt; t('title'), 'url' =&gt; t('Url')],
      '#options' =&gt; $rows,
      '#empty'   =&gt; t('No users found'),
    ];
    // Finally add the pager.
    $form['related_articles']['pager'] = array(
      '#type' =&gt; 'pager',
    );


Result will be like: