Skip to content

Add server-side rendered preview for unselected query loop blocks#11

Open
roborourke wants to merge 11 commits into
mainfrom
claude/server-side-render-previews-1zFB5
Open

Add server-side rendered preview for unselected query loop blocks#11
roborourke wants to merge 11 commits into
mainfrom
claude/server-side-render-previews-1zFB5

Conversation

@roborourke

@roborourke roborourke commented Feb 12, 2026

Copy link
Copy Markdown
Collaborator

Pages with many query loops (e.g. magazine homepages) trigger a large
number of API requests as each block loads its content previews. This
replaces the full editor rendering with a server-side rendered preview
when the query loop block is not selected, switching to the full editor
when the user clicks on it.

Registers a hidden hm-query-loop/preview block whose render_callback
runs do_blocks() on the serialized block markup. The editor uses
ServerSideRender with httpMethod="POST" to send the full inner blocks
content to this endpoint, wrapped in the Disabled component to prevent
interaction until the block is selected.

https://claude.ai/code/session_01LSHcKsEAo3tV17kuSFHTaj

Open WordPress Playground Preview

Pages with many query loops (e.g. magazine homepages) trigger a large
number of API requests as each block loads its content previews. This
replaces the full editor rendering with a server-side rendered preview
when the query loop block is not selected, switching to the full editor
when the user clicks on it.

Registers a hidden hm-query-loop/preview block whose render_callback
runs do_blocks() on the serialized block markup. The editor uses
ServerSideRender with httpMethod="POST" to send the full inner blocks
content to this endpoint, wrapped in the Disabled component to prevent
interaction until the block is selected.

https://claude.ai/code/session_01LSHcKsEAo3tV17kuSFHTaj
@github-actions

github-actions Bot commented Feb 12, 2026

Copy link
Copy Markdown

Playwright test results

passed  18 passed

Details

stats  18 tests across 6 suites
duration  1 minute, 7 seconds
commit  0ee6e7c

The ServerSideRender component uses __experimentalSanitizeBlockAttributes
internally which requires the block to be registered in the JS block
registry. Add a minimal registerBlockType call for hm-query-loop/preview
with inserter: false so it resolves during attribute sanitization.

Remove the Disabled wrapper since ServerSideRender already renders
static HTML that doesn't need interaction prevention.

https://claude.ai/code/session_01LSHcKsEAo3tV17kuSFHTaj
Adds two GitHub Actions workflows adapted from hm-url-tabs:

- pr-playground-preview.yml: On PR open/sync, builds the plugin, pushes
  built assets to a pr-N-built branch, and appends a WordPress Playground
  preview button to the PR description using the official action.
- pr-cleanup.yml: Deletes the pr-N-built branch when the PR is closed.

https://claude.ai/code/session_01LSHcKsEAo3tV17kuSFHTaj
github-actions Bot added a commit that referenced this pull request Feb 28, 2026
In the site editor, getCurrentPostId() can return a non-numeric
template identifier. Sending post_id=0 or a string also fails the
block-renderer endpoint's integer validation. Only include the
parameter when we have a real numeric post ID.

https://claude.ai/code/session_01LSHcKsEAo3tV17kuSFHTaj
github-actions Bot added a commit that referenced this pull request Feb 28, 2026
The server-rendered HTML contains real anchor tags and other interactive
elements that would navigate the editor iframe when clicked. Wrapping
in the Disabled component prevents this.

https://claude.ai/code/session_01LSHcKsEAo3tV17kuSFHTaj
github-actions Bot added a commit that referenced this pull request Feb 28, 2026
Two problems with the Disabled component wrapper:
- It changes the DOM structure so the query loop block can't be selected
  via click (the Disabled wrapper intercepts the event)
- Reverting to SSR preview on deselection triggers unnecessary re-fetches

Fix both by:
- Adding the inert attribute to top-level elements in the rendered HTML
  server-side using WP_HTML_Tag_Processor, preserving the original DOM
  structure so block selection works naturally
- Using useState to latch blocks as "activated" once first selected,
  keeping them in full edit mode permanently for the session

https://claude.ai/code/session_01LSHcKsEAo3tV17kuSFHTaj
github-actions Bot added a commit that referenced this pull request Feb 28, 2026
roborourke and others added 2 commits May 15, 2026 11:39
…toggle

The previous approach auto-activated on first click and never reverted,
which caused a refetch storm: getBlock(clientId) is reference-unstable so
every editor keystroke triggered a new POST to the block renderer.

The new approach adds an explicit Preview/Edit toggle to the Query Loop
toolbar. Snapshot serialization happens once on entering preview mode
(gated in a useEffect keyed only on mode transitions), so unrelated
editor activity never fires additional renderer requests.

A per-session hm-query-loop/preview-mode Redux store holds per-clientId
mode state — intentionally not persisted to post content, matching the
collapsed-panel convention.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…essor

get_current_depth() only exists on WP_HTML_Processor. Calling it on
WP_HTML_Tag_Processor caused a PHP fatal, which produced an empty REST
API response and a blank preview.

create_fragment() wraps content in an implicit body context, so
top-level elements are at depth 1 rather than 0.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
github-actions Bot added a commit that referenced this pull request Jul 3, 2026
github-actions Bot added a commit that referenced this pull request Jul 6, 2026
Moving snapshot capture from a useEffect into the toggleMode handler
ensures React batches setSnapshot and setMode into a single render.
The first ServerSideRender POST then has real content rather than an
empty string, eliminating the spurious empty initial render.

Also removes leftover echo/die() debug code and fixes a blank line.

@mattheu mattheu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I couldn't get this working and had to make a few fixes. However... I'm still not really sure what value this brings. Yes the preview is rendered server side, but the value needs to be enabled and doesn't persist. Am I misunderstanding something?

github-actions Bot added a commit that referenced this pull request Jul 6, 2026
@roborourke

Copy link
Copy Markdown
Collaborator Author

The thinking here is that inside the editor each query loop, and each post template block triggers a ton of API requests, for instance fetching featured images, fetching post meta, fetching terms etc... Firing one API request and generating the resulting markup server side I thought might reduce how much the editor needed to process, and how much data needed to be fetched in order to render a query loop, especially given it's unlikely an editor will need to or want to modify every loop on the page each time.

It's a bit too brittle still though, I haven't got it working well across several iterations, so perhaps need to go back to the drawing board on this idea.

The best workaround for now is to recommend using synced patterns and editing those directly for speed.

@mattheu

mattheu commented Jul 7, 2026

Copy link
Copy Markdown
Member

I actually do like this idea a lot and has potential. For a lot of publishing sites - the layout of the actual query loop post template will remain fixed and they'll just tweak the query to include different posts. So locking that preview, rendering server side, and possibly preloading that query, will be a notable UX improvement IMO.

@mattheu

mattheu commented Jul 7, 2026

Copy link
Copy Markdown
Member

Another thing that this solves is... some blocks used within post template make their own REST requests - e.g. to display post terms. So locking the preview like this also solves that issue.

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