Skip to content

Toggle DatoCMS block labels on preview mode#309

Merged
jbmoelker merged 75 commits intomainfrom
feat/show-blocks
Feb 10, 2026
Merged

Toggle DatoCMS block labels on preview mode#309
jbmoelker merged 75 commits intomainfrom
feat/show-blocks

Conversation

@anareyna
Copy link
Member

@anareyna anareyna commented Feb 5, 2026

Changes

Adds visual block labels in preview mode that allow users to quickly identify and edit blocks in DatoCMS.
SCR-20260205-knbe

What changed

  • Block labels: Block names appear as overlays in preview mode (toggleable via "show blocks" button)
  • Clickable labels: Clicking a block label opens the block's focus field in DatoCMS editor
  • Field path detection: Automatically detects the primary editable field for each block type
  • Nested block support: Correctly handles nested blocks (e.g., blocks inside GroupingBlock items)

Technical details

  • Added block-debug-utils.ts for field path building and API key conversion
  • Enhanced download-item-types.ts to detect focus fields (rich text, media, JSON, links, etc.)
  • Updated Blocks.astro to render debug labels with correct field paths
  • Updated PreviewMode.client.ts to handle label positioning, hover effects, and edit link generation
  • Added hideDebugLabels prop to skip labels in specific contexts (PagePartialBlock, TextBlock inline blocks)

Usage

Block labels appear automatically in preview mode when:

  1. Preview mode is active
  2. PreviewModeSubscription receives a record prop
  3. "show blocks" button is toggled on

Labels are hidden for:

  • PagePartialBlock children (wrong record context)
  • TextBlock inline blocks (embedded in rich text)

Documentation

Updated docs/preview-mode.md https://github.com/voorhoede/head-start/blob/feat/show-blocks/docs/preview-mode.md#block-field-path-detection

How to test

  • Click "show blocks" button in preview bar
  • Verify block labels appear as overlays
  • Hover over blocks to see highlight effect
  • Click a block label to verify it opens the correct field in DatoCMS
  • Test nested blocks (e.g., blocks inside GroupingBlock)
  • Verify labels are hidden for PagePartialBlock children and TextBlock inline blocks

Test in other head-start based projects:
Screenshot 2026-02-05 at 11 38 35

Checklist

  • I have performed a self-review of my own code
  • I have made sure that my PR is easy to review (not too big, includes comments)
  • I have made updated relevant documentation files (in project README, docs/, etc)
  • ~~ I have added a decision log entry if the change affects the architecture or changes a significant technology~~
  • I have notified a reviewer

@cloudflare-workers-and-pages
Copy link

cloudflare-workers-and-pages bot commented Feb 5, 2026

Deploying head-start with  Cloudflare Pages  Cloudflare Pages

Latest commit: d636d75
Status: ✅  Deploy successful!
Preview URL: https://a76bfebc.head-start.pages.dev
Branch Preview URL: https://feat-show-blocks.head-start.pages.dev

View logs

@anareyna anareyna marked this pull request as ready for review February 5, 2026 10:37
Copy link
Member

@jbmoelker jbmoelker left a comment

Choose a reason for hiding this comment

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

Really nice PR @anareyna! Thanks for the elaborate PR body (don't forget to check the boxes at the bottom ;)). Really great feature to be able to showcase during sales as well :). I made some suggestions. I would approve already except for one thing which is about possible nested anchors. I suggested a workaround there. An alternative could be not wrapping but using adjacent elements after the blocks that lay over their blocks.


When clicking on a block label in preview mode, the system automatically generates a field path to focus the correct field in DatoCMS. Block labels are only clickable when `PreviewModeSubscription` receives a `record` prop (see above). This is done by detecting the "focus field" for each block type.

### How it works
Copy link
Member

Choose a reason for hiding this comment

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

I love the effort you put into the docs ❤️. Maybe nice to reference why this works on the DatoCMS site by referencing their content link feature?

});
dotenv.config({ allowEmptyValues: Boolean(process.env.CI) });

const FILE_PATH = './src/lib/datocms/itemTypes.json';
Copy link
Member

Choose a reason for hiding this comment

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

We don't use all uppercase const values elsewhere in the code base (other than for env variables). So I wouldn't start here. Just filePath etc.

.split(/[_-]/)
function convertApiKeyToTypename(apiKey: string): string {
const words = apiKey.split(/[_-]/);
const pascalCase = words
Copy link
Member

Choose a reason for hiding this comment

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

https://www.npmjs.com/package/change-case ? or does DatoCMS maybe expose a method for this in general? I imagine they do the same thing in their packages.

Copy link
Member

Choose a reason for hiding this comment

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

if not provided I'd go with https://github.com/unjs/scule/
unjs packages are well maintained in general

}

// Run async work with a small concurrency cap to avoid spiking the api
async function processItemsConcurrently<T>(
Copy link
Member

Choose a reason for hiding this comment

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

I'm okay with these methods, but always find them a bit hard to read. So maybe easier to use helper packages.
Use async parallelLimit? https://www.npmjs.com/package/async

import VideoBlock from './VideoBlock/VideoBlock.astro';
import VideoEmbedBlock from './VideoEmbedBlock/VideoEmbedBlock.astro';
import GroupingBlock from './GroupingBlock/GroupingBlock.astro';
import { getDebugPaths } from './block-debug-utils';
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure if Debug is the best word. Is it Preview or Edit(or)?

Copy link
Member Author

Choose a reason for hiding this comment

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

makes sense, renamed debug prefixes and attributes to editor

<div class="preview-mode-actions">
{/* eslint-disable-next-line astro/jsx-a11y/anchor-is-valid */}
<a data-edit-record target="_blank" rel="noreferrer noopener">edit in CMS</a>
<a data-debug-edit-record target="_blank" rel="noreferrer noopener">edit in CMS</a>
Copy link
Member

Choose a reason for hiding this comment

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

There are now so many options in the preview bar that it no longer fits on smaller screens. So I think we need some responsive layouting here:

image

}

a[data-edit-record]:not([href]) {
a[data-debug-edit-record]:not([href]) {
Copy link
Member

Choose a reason for hiding this comment

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

I'm wondering if we should hide the other labels when one has focus. Now I tried out some pages that have a nested or overlapping blocks and my highlighted block contents are still not entirely visible as other labels are over it.

}
:global([data-debug-blocks-visible="true"]) {
--block-label-z-index: 9999;
--block-label-color: #ff593d;
Copy link
Member

Choose a reason for hiding this comment

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

We've managed to only use primary colours up until now. So maybe use blue, similar to the Vercel content link?

}
}

#buildRecordEditUrl(itemTypeId: string, recordId: string) {
Copy link
Member

Choose a reason for hiding this comment

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

Does this belong here or in lib/datocms or block-debug-utils?

Copy link
Member Author

Choose a reason for hiding this comment

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

I'd keep it here since it uses these other instances (#datocmsProject, #datocmsEnvironment) and is only called in this class. We can extract it if it's ever needed elsewhere.

return `https://${this.#datocmsProject}.admin.datocms.com/environments/${this.#datocmsEnvironment}/editor/item_types/${itemTypeId}/items/${recordId}`;
}

#applyBlockEditLinks(itemTypeId: string) {
Copy link
Member

Choose a reason for hiding this comment

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

If we use clickable divs instead of anchors, this only needs to be executed on click.

Copy link
Member

Choose a reason for hiding this comment

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

I would also consider adding examples of these paths to a comment on this functions to make it easier to understand what goes in and out.

@anareyna
Copy link
Member Author

anareyna commented Feb 6, 2026

Really nice PR @anareyna! Thanks for the elaborate PR body (don't forget to check the boxes at the bottom ;)). Really great feature to be able to showcase during sales as well :). I made some suggestions. I would approve already except for one thing which is about possible nested anchors. I suggested a workaround there. An alternative could be not wrapping but using adjacent elements after the blocks that lay over their blocks.

thanks!, not sure if I'm missing something about nesting anchors but currently the <a class="block-name-label"> is a sibling of the <Block>, not a wrapper. They're both inside a <> fragment. So there's no nesting of anchors here. @jbmoelker

Copy link
Member

@jbmoelker jbmoelker left a comment

Choose a reason for hiding this comment

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

sorry, no nested anchors. so approved. the rest are just suggestions for minor improvements.

@anareyna anareyna changed the title Feat/show blocks Toggle DatoCMS block labels on preview mode Feb 10, 2026
@jbmoelker jbmoelker self-requested a review February 10, 2026 13:04
@jbmoelker jbmoelker merged commit de6c5e1 into main Feb 10, 2026
5 checks passed
@jbmoelker jbmoelker deleted the feat/show-blocks branch February 10, 2026 13:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

Comments