This project is not covered by Drupal’s security advisory policy.
Automatic table of contents for any Drupal content page
JumpLinks adds a field type that scans your rendered pages for anchor targets and lets editors pick which sections appear as navigation links. No manual ID entry, no JSON blobs, no code changes.
How it works: Add a JumpLinks field to any entity type. On the edit form, the widget renders the page using your site's default theme, finds every element matching the configured XPath selector, and presents them as checkboxes (or draggable rows). Editors check the sections they want. On the frontend, selected links render in page order. Reorder your content and the jump links follow automatically.
Two widget styles, one field type
Editors have different preferences. JumpLinks ships two widgets that work with the same underlying field storage:
| Widget | Best for | How it works |
|---|---|---|
| Checkboxes | Most editors | Auto-scans on form load. All available sections shown in page order with checkboxes. Check the ones you want. Done. |
| Drag and Drop | Editors who want manual control | Click "Scan", pick sections from a dropdown, reorder with tabledrag. Full manual control over selection and order. |
Switch between widgets on the Manage form display tab. No data migration needed.
Key features
- Auto-scan on load: the checkbox widget scans automatically when the edit form opens. No button click required.
- Stale anchor detection: if a selected section is removed from the page, it appears with a strikethrough and warning icon. Validation warns on save so editors can clean up.
- Configurable max links: set a per-widget cap (e.g., 6 for a hero component). Checkboxes disable and validation blocks when the limit is reached.
- Label overrides: each link can have a custom label. Leave it blank to use the auto-detected heading text.
- Configurable XPath: field instance setting controls which elements are matched. Target sections, divs, headings, elements with a specific class, or any combination.
- Page-order rendering: links always render in the order their targets appear on the page. Reorder your content and the jump links follow.
- Entity-agnostic: works on nodes, paragraphs, or any content entity. The extractor walks up paragraph chains to find the root entity.
XPath selector examples
| Goal | XPath |
|---|---|
| Sections and divs with IDs (default) | //section[@id] | //div[@id] |
Only <section> elements |
//section[@id] |
| Elements with a marker class | //*[contains(@class, "toc-target")][@id] |
| Heading elements | //h2[@id] | //h3[@id] |
Architecture
JumpLinks uses structured field storage. Each selected link is a field delta with anchor and label columns, not a serialized blob. This means:
- Standard Drupal field APIs work (Views, REST, JSON:API, migrations)
- No custom serialization or deserialization
- Widget switching is seamless: both widgets read and write the same schema
For developers: integration patterns
The simplest integration is to show the field with the included JumpLinks Navigation formatter. It renders an accessible <nav> with an ordered list of anchor links.
For deeper integration (e.g., injecting links into a hero component), hide the field in the view display and write a preprocess hook:
function mymodule_preprocess_paragraph__hero(array &$variables): void { $paragraph = $variables['paragraph']; $field = $paragraph->get('field_jump_links'); // Read $item->anchor and $item->label from each field item. // Call jumplinks.anchor_extractor to get page-order anchors. // Map to your component's variables. }
The jumplinks.anchor_extractor service accepts any ContentEntityInterface. It renders the entity using the site's default theme, then parses the HTML with the provided XPath query. A static re-entrance guard prevents infinite loops when rendering triggers hooks that call the extractor.
Requirements
- Drupal 10.3+ or 11.x
- PHP 8.1+
- No JavaScript dependencies. No external services.
Installation
composer require drupal/jumplinks drush en jumplinks
Similar projects
- TOC API: filter-based, scans body field HTML for headings. JumpLinks differs in that it works at the field level across any rendered entity (paragraphs, layout builder, etc.) and gives editors explicit control over which sections appear.
- Anchor Link: CKEditor plugin for inserting anchor links. Complementary to JumpLinks. Anchor Link helps create the targets, JumpLinks helps navigate to them.
Project information
- Project categories: Content display, Content editing experience
- Created by owenbush on , updated
This project is not covered by the security advisory policy.
Use at your own risk! It may have publicly disclosed vulnerabilities.

