Skip to content

Give notebook cells stable deep-link anchors - #2975

Merged
agoose77 merged 9 commits into
jupyter-book:mainfrom
Darshan808:stable-cell-ids
Aug 27, 2026
Merged

Give notebook cells stable deep-link anchors#2975
agoose77 merged 9 commits into
jupyter-book:mainfrom
Darshan808:stable-cell-ids

Conversation

@Darshan808

Copy link
Copy Markdown
Contributor

Fixes #518

Description

Right now, deep-linking to notebook cells is unreliable. We were discarding the nbformat cell id and falling back to a random nanoid, so #<id> links changed on every build.

This PR promotes notebook cell ids to stable anchors:

  • notebook.ts preserves cell.id when reading .ipynb files.
  • blocks.ts maps it to identifier (normalized) and html_id (verbatim).
  • myst-to-html emits the anchor on block containers, so static HTML exports
    get stable links too.

@changeset-bot

changeset-bot Bot commented Jun 23, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: ea379f0

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 8 packages
Name Type
myst-transforms Patch
myst-to-html Patch
myst-cli Patch
myst-parser Patch
myst-roles Patch
myst-directives Patch
mystmd Patch
myst-migrate Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@Darshan808
Darshan808 marked this pull request as ready for review June 23, 2026 10:12
@agoose77 agoose77 added the bug Something isn't working label Jun 23, 2026
@agoose77

Copy link
Copy Markdown
Contributor

Thanks for this PR @Darshan808! I'll take a look this week unless someone gets in first!

@krassowski krassowski left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can it use #cell-id=my-cell-id schema to align with the format used by nbconvert and JupyterLab?

@Darshan808

Copy link
Copy Markdown
Contributor Author

Yes, ultimately we will support #cell-id=<id>. This PR enables mystmd to attach the bare nbformat cell id as the cell's stable anchor (it has to stay bare, because the same id is reused in JATS/XML where = is illegal). The cell-id= part is handled in myst-theme (this pr) with a small client-side hash handler that strips the prefix and scrolls.

@agoose77

agoose77 commented Jun 27, 2026

Copy link
Copy Markdown
Contributor

@Darshan808 can you clarify your relationship with Jupyter Book? Your wording here

Yes, ultimately we will support #cell-id=.

slightly implies that you have decision making authority for the project.

I think this is just a nuance of language, and I'm very appreciative of your contribution here — but I want to understand and clarify any confusion as the distinction is important.

@Darshan808

Copy link
Copy Markdown
Contributor Author

That was just an imprecise choice of words on my part. I don't have any decision-making authority for Jupyter Book.
By "we" I meant the broader Jupyter Book/MyST community and the direction this work is heading, not myself or the project's maintainers.
Sorry for the confusion.

@krassowski

krassowski commented Jun 28, 2026

Copy link
Copy Markdown

Disclosure: I work with @Darshan808

By "we" I meant the broader Jupyter Book/MyST community and the direction this work is heading, not myself or the project's maintainers.

I don't think one can speak for the broader community either, but certainly for some parts of the userbase as to what they seem desirable (i.e. having a stable way to link to different parts of a notebook across Jupyter interfaces). I think what would be fair to say in the original comment would be:

Yes, ultimately we will try contribute support for #cell-id=<id>.

I for one was reaching out to a few members of MyST and JupyterBook community for years, sadly this was previously blocked by complexity of implementing this in Sphinx and possibly lack of impetus:

In the process multiple community members engaged with the discussion, supporting the idea or welcoming the PRs as seen in the issues linked above (Chris Sewell, Chris Holdgraf and Rowan Cockett), which might give the impression that there is some level of agreement in the community. Of course, whatever was expressed by individuals years ago does not imply a project-level agreement at the time, nor that they would still support this given the time that has passed. @agoose77 apologies that this was misinterpreted and misrepresented above.

@agoose77

agoose77 commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

@Darshan808 @krassowski thanks both for clarifying things! I really do not want this to get overblown — I just wanted to clear up what was being said. I wasn't aware that @krassowski works with @Darshan808, this adds some helpful context.

On this PR and the goals — Jupyter Book is a Jupyter subproject and we're very keen to ensure that we provide a good integration with the conventions and tooling like Jupyter Lab. So, I wholeheartedly agree with the idea from @krassowski that Jupyter Book is keen to move this forward. ❤️

@krassowski

Copy link
Copy Markdown

Is there anything I could help with to move this forward?

@agoose77

agoose77 commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

@krassowski good timing — I was just circling back. Let me pick this up tomorrow (sets reminder).

agoose77 and others added 4 commits August 27, 2026 13:43
The `block.id` is a legacy "stash" that we can remove.
Prefer directly propagating cell IDs like this.
Co-authored-by: darshan <pranishpoudel10@gmail.com>
@agoose77

Copy link
Copy Markdown
Contributor

Thank you so much for this PR!

I've updated this PR to simplify the label propagation given my knowledge of our intention behind labels and how this state is managed. It was a good opportunity to clean something up!

children: children as any,
};
const cellId = (cell as { id?: unknown }).id;
if (typeof cellId === 'string' && cellId.length > 0) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now we only handle cell IDs during parsing of an ipynb file. The intention here is to avoid stashing something — we can directly set the ID and html_id.

if (block.identifier) {

// Did the user set a label? Choose to only propagate IDs if so.
const hasExplicitLabel = typeof block.label === 'string';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In choose to check the final block.label, because that feels slightly more explicit than the intermediate block.data

@krassowski

Copy link
Copy Markdown

Thank you!

@agoose77
agoose77 merged commit 0b0ba90 into jupyter-book:main Aug 27, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Linking to a cell by cell ID

3 participants