Issues arising from the migration to Observable 2.0

Howdy,

I had a vague sense that Observable was working on a new notebook format, but now that the automatic migration process has begun, I realize it’s time to embrace the change and get up to speed.

I should now bring to your attention all the problems and pitfalls I’ve encountered.


Some of my 1.0 notebooks rely on named HTML and Markdown cells for lightweight metaprogramming, allowing me to leverage built-in syntax highlighting and reference cell values, as in:

(boilerplate [md]=)

<script defer src="data:,">
onmessage = async (e) => {
  let $_ = await ({{this}}).apply(null, e.data);
  if (Symbol.iterator in Object($_) && Object.prototype.toString.call($_) !== '[object String]') {
    $_ = [...$_];
  }
  postMessage($_);
}
</script>

or

(root [md]=)

<script defer src="data:,">
:root {
  --width: ${boxWidth}px;
  --slope-angle: ${alpha};
  --slope-angle-modulus: max(var(--slope-angle), -1 * var(--slope-angle));
  --compute-tan-of-angle: ${expand('var(--slope-angle-modulus)').replace(/((var\(--slope-angle)-modulus\)) \* \1/g, "$2) * $2)")};
  --magic-number: (var(--compute-tan-of-angle)) / 2;
  --slope-padding: var(--magic-number) * var(--width);
}

@supports (opacity: cos(0)) {
  :root {
    --compute-tan-of-angle: tan(var(--slope-angle-modulus));
  }
}
</script>

While this was permitted in the Observable 1.0 format, it appears that this functionality is no longer supported in the 2.0 release.

Here’s yet another example:

RuntimeError: megaphone is not defined

${[
  Object.assign($(megaphone), {
    style: "position: absolute;"
  }),
  md`> It was suggested to stop the corrections to UTC and publish the increasing difference between UTC and UT1. The relatively small community that needs UT1 could then use the published values as correction.`
]}

(megaphone [html]=)

<span aria-hidden="true">📣</span> ` 

Would you consider reintroducing the ability to name these cell types? At the very least, would it be possible to maintain support for pre-2.0 notebooks when they are rendered in the new format?


Now when a cell doesn’t display anything, and the cell isn’t pinned (or focused), we show a small gray <empty> instead of showing the cell’s source code.

It seems like <style>:only-child HTML cells used to be pinned by default, which felt a bit unnecessary since most people aren‘t CSS experts. Now, the behavior has shifted and the cells don‘t render at all, meaning you can‘t even click into them to edit. That‘s a bit of a bummer. Could we go back to how it worked in Observable 1.0?

import { runAsWorker } from "@galopin/work-work"

The above cell yields <empty>!?

Therefore you cannot see at a glance what’s being imported and from which notebook until you toggle the edit mode. You cannot even go to the imported notebook. IMHO, you should restore the Observable 1.0 functionality with full working hyperlinks.


viewof pass = Inputs.input(probe())

The above cell is rendered as viewof$pass = …!?


I previously noticed that most notebooks were loading in an incomplete state, where HTML and Markdown cells failed to render initially. A manual page reload was required to achieve the expected result. As I am no longer able to reproduce this behavior, I assume this specific issue has been resolved.


md doesn’t seem to work the same depending on which version of Standard Library is picked (2026 or 2018). The old md function would wrap the HTML output in a <span> or <div> if I remember correctly. And syntax highlighting for code snippets in Markdown cells is not supported if Standard Library 2018 is used :upside_down_face:


What you might be noticing is that none of the editing affordances show when you’re not an author (or when you’re signed out). No toggle in left gutter, no cell inserters between cells. You have to click the pencil icon in the bottom right to edit or tinker. That’s a deliberate new approach to present a cleaner published artifact and decrease false-positive edits, at the expense of making tinkering — which is very important! — one click harder.

  1. On my Android handheld device where the screen estate in portrait/landscape mode is limited, I don’t see any of the controls on the far right (attachments, dependencies, comments, edit mode, etc.) which makes it pretty hard to actually tinker with things on mobile :confused:

    Additionally, when using Google Chrome for Android, the notebook loads in a zoomed-in state rather than fitting the screen. I have to manually zoom out to view the right edge of the page. Any idea why that’s happening?

  2. On my iPad, where the screen estate is obviously much bigger, the controls are available as expected.

You cannot toggle between edit and read-only mode.

Likewise, the floating palette in the bottom-left corner does not display the name of the selected cell. How can you rename all instances of a specific cell in an Observable 2.0 notebook?

I have some concerns about the new orange hue used for language keywords. To me, orange often signals a warning or an error, which makes the keywords feel cautionary. I found the pink used in Observable 1.0 to be more neutral; perhaps a purple hue would be a viable alternative?

The minimap is gone but I suppose you’re working on it :wink:

Also the license the notebook was published with is nowhere to be found. (There is even a 404 error when you try to access the license from old.observablehq.com.)

I noticed that you reveal the number of forks a notebook has, even if those forks are unlisted or private (only you). I think that this information should not be divulged unless the forks are public.

Finally, could you clarify the current process for locking comments on a 2.0 notebook? I can’t find the ‘comments locked’ mention anymore in the notebook header.

Yes… the main reason we don’t do this currently is I didn’t want the cell toolbar appearing on Markdown, HTML, and other non-JavaScript cell modes. There is a cell toolbar for SQL cells (and data loaders in Desktop, which will come to the web eventually), and this cell toolbar allows you to declare an output which is equivalent to the cell name in Notebooks 1.0.

Not sure when you wrote your feedback, but we changed this yesterday in response to similar feedback. These cells should now show <empty>. Are you seeing something different?

As for imports displaying <empty>, we’re planning on adding an automatic inspector for imports, and for all other top-level variables too. We just haven’t had a chance to implement it yet. We’re going as fast as we can! I also want to link to the imported notebook, but I haven’t figured out exactly how that should work yet (because I think imports should let you inspect the imported values, not just be redundant with the corresponding source code). Probably the module specifier should be a clickable link in the cell editor.

Haha yes, that’s exactly the idea! Notebooks 1.0 (2018 standard library) uses an ancient version of Marked. Notebooks 2.0 (2026 standard library) uses the latest version of markdown-it and is CommonMark compliant. The purpose of the standard library is to let you choose which behavior you want, and to avoid breaking existing notebooks… or to reduce the number of ways they break, I should say.

Yes, we’re working on improving the mobile/narrow window layout; see this thread for related feedback. I’m not sure why the notebook would load zoomed-in, sorry.

I did this because I thought you’d want to decide whether to discard your tinkered edits or automatically save them, and I wasn’t sure which to do. I suppose your tinkered edits should be saved automatically? Or perhaps you want two different buttons to either save or discard?

Cells don’t have names in Notebooks 2.0, though some cell modes (the non-JavaScript ones) can have an output as discussed above. I do want to implement something like F2 in Zed where you can rename the variable at the current caret, and it should work with both local variables and cross-cell top-level variables.

Oops, that’s a CORS error related to the new origin. We can fix that; thank you for the report. We’d also like to have this feature on New Observable but there’s a long list of other stuff that we’ll likely need to do first. You’ll have to manually declare your desired license in the notebook body for now.

We did this because many (most!) forks are private, and yet the existence and magnitude of those forks at an aggregate level is a useful quality signal for the notebook.

We haven’t implemented comment locking yet, but it’s on the (long) list. If someone is behaving badly, please let us know so we can enforce etiquette.

Thanks for all the excellent, detailed, thoughtful feedback!

The CORS error loading licenses has been fixed. Thank you again.

I’d prefer that everything not be saved automatically, as in Observable 1.0’s Tinker mode, and that I have the choice between saving the changes made or discarding everything.

So yes, having two distinct buttons would be a welcome option.