Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

GitHub Issue Table

Updated: 09 Feb 2026

Renders GitHub issues and PRs as tables from search queries or GitHub URLs.

Setup

Add to your myst.yml:

project:
  plugins:
    - https://raw.githubusercontent.com/jupyter-book/myst-plugins/main/plugins/github-issue-table/dist/index.mjs

Set the GITHUB_TOKEN environment variable for authentication:

export GITHUB_TOKEN=your_token_here
Token scopes
  • Fine-grained PATs: Issues (read-only), Pull requests (read-only), and Projects (read-only) if you query project boards; Metadata is implied.

  • Classic PATs: repo (or public_repo for public data only), read:org (for org project views), and project (for project boards).

  • GitHub Actions: usually works for public data but may need permissions: contents: read, issues: read, pull-requests: read, projects: read if you query projects.

  • Fine-grained PATs must also be installed on every repository you query. A 401 with correct scopes usually means the token isn’t granted to one of the repos in your query.

Results are cached locally so the same query only fetches data once per build.

Quick Start

The directive takes a GitHub search query and renders matching issues as a table. Use :columns: to pick which columns to show (default: title, author, state, reactions) and :limit: to control how many rows (default 25). Results are sorted first, then trimmed to the limit.

MyST Demo
:::{issue-table} org:jupyter-book is:pr is:open sort:reactions-desc
:columns: title, author, reactions, updated
:limit: 5
:::


*Error: fetching GitHub data: GitHub API error: 401 Unauthorized*

Sorting

Use GitHub’s native sort: in your search query for single-column sorting (recommended). See GitHub’s sorting docs for supported fields. For multi-column or project-field sorting, use the :sort: option with comma-separated column-direction pairs:

MyST Demo
:::{issue-table} org:jupyter-book is:pr is:open sort:updated-desc
:columns: title, author, reactions, updated
:sort: reactions_thumbsup-desc,updated-desc
:limit: 10
:::


*Error: fetching GitHub data: GitHub API error: 401 Unauthorized*

Date Formatting

Use :date-format: to control how created, updated, and closed columns display — either relative (e.g., “2d ago”) or absolute (default, YYYY-MM-DD).

MyST Demo
:::{issue-table} org:jupyter-book is:pr is:open sort:updated-desc
:columns: title, author, created, updated
:date-format: relative
:limit: 10
:::


*Error: fetching GitHub data: GitHub API error: 401 Unauthorized*

Column Widths

Set column width percentages (one per column). Values are normalized proportionally if they sum to more than 100%.

MyST Demo
:::{issue-table} org:jupyter-book is:pr is:open sort:reactions-desc
:columns: title, author, reactions
:widths: 60,20,20
:limit: 5
:::


*Error: fetching GitHub data: GitHub API error: 401 Unauthorized*

Truncation

Limit body, description, and summary columns to approximately N characters of visible text. Truncated content appends a “More” link to the full issue. Truncation is applied after Markdown is parsed, so it never produces broken links or formatting.

Options: :body-truncate: controls body and description; :summary-truncate: controls summary (falls back to :body-truncate: when not set)

MyST Demo
:::{issue-table} https://github.com/orgs/jupyter-book/projects/1/views/7
:columns: title, body, summary
:limit: 5
:body-truncate: 200
:summary-truncate: 150
:::


*Error: No issues found matching this query*

Summary Column

The summary column extracts a section from the issue body by searching for headers matching keywords (case-insensitive). It returns that section’s content up to the next header. If no matching header is found, it falls back to everything before the first header or horizontal rule.

Options: :summary-header: — comma-separated keywords to match, default: summary,context,overview,description,background,user story

MyST Demo
:::{issue-table} repo:jupyter-book/jupyter-book is:issue is:open sort:updated-desc
:columns: title, author, summary
:summary-header: tldr,abstract,problem
:limit: 5
:::


*Error: fetching GitHub data: GitHub API error: 401 Unauthorized*

Sub-Issues

Show tracked sub-tasks using GitHub’s sub-issue feature. Use :append-sub-issues: to inline them at the bottom of a specific column, or add a dedicated sub_issues column.

MyST Demo
:::{issue-table} repo:jupyter-book/mystmd is:issue 1921
:columns: number, title, updated
:append-sub-issues: title
:limit: 5
:::


*Error: fetching GitHub data: GitHub API error: 401 Unauthorized*

Project Boards

Pass a GitHub project view URL instead of a search query. The board’s own filter acts as the query, and you can use project field names (e.g., Team Priority, Status) as columns and sort by them.

MyST Demo
:::{issue-table} https://github.com/orgs/jupyter-book/projects/1/views/7
:columns: title, Team Priority, linked_prs, closing_prs, reactions
:sort: Team Priority-asc, reactions_thumbsup-desc
:::


*Error: No issues found matching this query*

Templates

Add custom columns using {{field}} placeholders and include the template name in :columns:. Definitions are semicolon-separated name=template pairs.

Options: :templates: — e.g., link=[View]({{url}}); search=[Find](https://example.com?q={{title | urlencode}})

MyST Demo
:::{issue-table} repo:jupyter-book/jupyter-book is:issue is:open sort:updated-desc
:columns: title, repo, author, issue_link, repo_link, issue_cta
:templates: issue_link=[View issue]({{url}}); repo_link=[Repo home](https://github.com/{{repo}}); issue_cta={button}`Open issue <{{url}}>`
:::


*Error: fetching GitHub data: GitHub API error: 401 Unauthorized*

Filters

Use {{ field | filter }} to transform a value before insertion. This is useful when embedding values in URLs where special characters must be encoded.

Available filters: urlencode — percent-encodes the value for safe use in URLs

MyST Demo
:::{issue-table} repo:jupyter-book/jupyter-book is:issue is:open sort:updated-desc
:columns: title, author, search_link
:templates: search_link=[Search title](https://github.com/search?q={{ title | urlencode }})
:limit: 5
:::


*Error: fetching GitHub data: GitHub API error: 401 Unauthorized*

Label Subset Columns

By default the labels column shows all labels on an issue. Use :label-columns: to show only a subset of labels, or to split labels into separate named columns. Definitions use [column name]=[patterns] syntax, separated by semicolons.

For example the following defines one column called fooandbar that will show only the labels foo or bar (if matched) and another column called type that matches any label beginning with type::

:label-columns: fooandbar=foo,bar;type=type:*

You can then include any [column name] listed in this argument in your :columns: option.

Values are comma-separated glob patterns where * matches any characters. A label is included if it matches any pattern in the list for a column.

Named label columns

Use any name to create a new column. Include the name in :columns: and it renders as styled label badges:

MyST Demo
:::{issue-table} repo:jupyter-book/jupyter-book is:issue is:open sort:updated-desc
:columns: title, type, labels
:label-columns: type=type:*; labels=enhancement,bug
:limit: 5
:::


*Error: fetching GitHub data: GitHub API error: 401 Unauthorized*

Pattern matching

Patterns support * as a wildcard. Without a wildcard, the pattern must match the label name exactly.

PatternMatchesDoesn’t match
bugbugtype:bug
type:*type:bug, type:featurepriority:high
*-requestfeature-requestbug
type:*,bugtype:feature, bugpriority:high

Column Reference

Available columns:

:::{issue-table} repo:jupyter-book/jupyter-book is:issue is:closed sort:updated-desc
:columns: number, title, author, author_affiliation, state, labels, linked_prs, closing_prs, sub_issues, reactions, comments, created, closed, updated, repo, body, summary
:body-truncate: 100
:summary-truncate: 50
:limit: 3
:::

*Error: fetching GitHub data: GitHub API error: 401 Unauthorized*