Back to skills

Agent Skill

HubSpot Email Editing

hubspot-email-editing

Reliable patterns for editing HubSpot email templates. Uses click_and_insert for ALL fields (body modules and sidebar metadata) to prevent stale DOM indices and TinyMCE focus traps.

O-mega.aiBrowser AutomationHubspotEmailTinymceEditorIframeNewsletterMarketingContent-editingEmail-editingBrowser-automation

0 installs

o-mega.ai/internal

by o-mega.ai

Score

9.0

/ 10

Installs

0

Repo Stars

Last Updated

0d ago

Fresh

Quality Ratio

95%

Description

Verified

First Published

Apr 2026

Platforms

1

Summary

Use click_and_insert for every field in HubSpot email editor. Body modules: prevents text landing in wrong module. Sidebar fields (subject/preview): prevents stale DOM index failures. Edit body first, subject/preview last.

Skill Definition

HubSpot Email Editor: Reliable Editing Guide

Overview

HubSpot's email editor has two types of editable fields: body content modules (TinyMCE rich text) and sidebar metadata fields (Subject Line, Preview Text). Both suffer from stale DOM indices due to React re-renders. Use click_and_insert for ALL fields to avoid this.


Use click_and_insert for Everything

click_and_insert performs an atomic CDP click + clear + text insertion in a single operation. No LLM roundtrip between focus and typing. This prevents two problems:

  1. Body modules: TinyMCE ignores JS .focus(), so input_all_at_once sends text to the wrong module. click_and_insert uses a real CDP click that TinyMCE processes.
  2. Sidebar fields: React re-renders the sidebar between steps, invalidating DOM indices. click_and_insert resolves the element and types into it atomically, before the DOM can refresh.

Pattern for every field:

click_and_insert(index=N, text="...", clear_existing=True)

Then screenshot to verify. That's it.

Example (newsletter with 3 body modules + subject + preview):

Step 6:  click_and_insert(282, "Lead editorial text...")
Step 7:  screenshot -> verify
Step 8:  click_and_insert(391, "Table of contents...")
Step 9:  screenshot -> verify
Step 10: click_and_insert(517, "Breaking news...")
Step 11: screenshot -> verify
Step 12: click_and_insert(9790, "Subject line text")
Step 13: screenshot -> verify
Step 14: click_and_insert(9853, "Preview text here")
Step 15: screenshot -> verify

Finding Element Indices

Indices change between sessions. Two methods:

  1. Standard browser state: Look for contenteditable="true" elements or input fields.
  2. get_raw_dom_discovery: Use when the standard state hides elements. Act on the returned indices in the SAME step (call click_and_insert immediately, do not wait for another step).

Subject Line and Preview Text: Use Commentable Area Divs

HubSpot has TWO sets of elements for Subject Line and Preview Text:

  1. Sidebar React inputs (inside the "Edit inbox content" panel): These are <input> elements controlled by React. click_and_insert physically types text into them but React does NOT register the change. The text disappears on the next re-render. DO NOT USE THESE.

  2. Commentable area divs (in the main editor frame): These are <div id="commentable-area-subject-line-default"> and <div id="commentable-area-preview-text-default">. They are contentEditable divs that work reliably with click_and_insert, just like body modules.

Always target the commentable-area-* divs for metadata. Find them via get_raw_dom_discovery and look for elements with id containing commentable-area-subject or commentable-area-preview. If you cannot find them, scroll up to the top of the email preview and re-run discovery.


Recommended Editing Order

  1. Body content modules (top to bottom in the template)
  2. Subject line (via commentable-area-subject-line-default div)
  3. Preview text (via commentable-area-preview-text-default div)
  4. CTA button text (click the button module to open settings, then edit)

What NOT to Do

  • Do NOT use separate click + clear_input_field + input_all_at_once for body modules (causes text in wrong module)
  • Do NOT use input(clear=True) for sidebar fields across steps (indices go stale between steps)
  • Do NOT call get_raw_dom_discovery and then use the indices in a LATER step (they will be stale)
  • Do NOT call get_raw_dom_discovery more than twice without acting
  • Do NOT target sidebar <input> fields for Subject/Preview. Use commentable-area-* divs instead.


Saving the Draft (CRITICAL)

HubSpot does NOT reliably auto-save new content. If the browser session crashes, gets paused, or times out, unsaved work is lost. You MUST click the "Save" button proactively.

Save after every major content insertion:

  • After inserting body content: click Save
  • After inserting metadata (subject/preview): click Save
  • After editing CTA button text: click Save
  • BEFORE calling done: click Save (mandatory, non-negotiable)

How to save:

  1. The "Save" button is in the top-right toolbar (near "Preview and test" and "Review and send")
  2. If you have scrolled down, scroll back up first or use the toolbar
  3. Click "Save"
  4. Wait 2 seconds
  5. Verify "Saved" or "Autosaved" appears in the toolbar

If you skip Save and call done, the entire email draft is empty. All work wasted. Save early, save often.


Escalation

If click_and_insert fails:

  1. Screenshot to check current state
  2. Use get_raw_dom_discovery to get fresh indices
  3. Call click_and_insert with the new index in the SAME step
  4. If still failing, try targeting a DIFFERENT element (e.g. switch from sidebar inputs to commentable-area divs, or vice versa)
  5. If still failing, use editor_set_content action as the final fallback:
    • For body modules (TinyMCE): editor_set_content(text="<p>Your content</p>", editor_type="tinymce") This calls TinyMCE's own JavaScript API (setContent) which bypasses DOM indices entirely.
    • For React input fields (Subject, Preview): editor_set_content(text="Subject text", selector="input [placeholder*='Subject']", editor_type="react_input") This uses React's native value setter to bypass React's internal value tracking.
    • For contenteditable divs: editor_set_content(text="Content", selector="div [id*='commentable-area-subject']", editor_type="contenteditable") editor_set_content does not need element indices. It uses CSS selectors or the editor's JS API directly, so it works even when React re-renders invalidate all DOM indices.

How to Use