Squarespace
7.1 markup is observed, not documented: null-check lookups, and inspect rather than guess.
Page structure
#siteWrapper
header#header
main#page the stable root
article#sections > section.page-section
or article#page-regions > section.region > section.page-section
footer#footer-sections
- A
.page-sectioncarriesdata-section-theme(may be"", meaning the site default) anddata-section-id(a rendered id, not Squarespace's backend id). Fluid Engine sections havedata-sqsp-section="fluid-engine". - The mount is a plain
divinside a Code Block. Find its section withel.closest(".page-section"); sections below are itsnextElementSiblings that match.page-section. - Squarespace buttons aren't always links: match
a[href]andbutton[data-href].
The editor
A signed-in owner visiting their site lands in /config, with the site in an iframe
(window.self !== window.top). Squarespace disables scripts embedded in Code Blocks while the
owner is logged in and editing, so they may not see the component there; the accurate view is
logged out. A browser holding the seamble_preview cookie shows drafts in the editor too.
Clicking Edit adds sqs-edit-mode-active to <body> without a reload. If you hid, moved or
rewrote Squarespace's markup, undo it then, or the owner can't edit it:
function onEditMode(undo) {
if (window.self === window.top) return;
const on = () => document.body.classList.contains("sqs-edit-mode-active");
if (on()) return undo();
const obs = new MutationObserver(() => { if (on()) { obs.disconnect(); undo(); } });
obs.observe(document.body, { attributes: true, attributeFilter: ["class"] });
}
Taking over the page
- Sections into a new layout (slider, stack): mount in the first section. Insert your
container into the page first, then move the sections into it; anchoring on a section already
moved inside throws (
HierarchyRequestError). Give the container the first section'sdata-section-theme. Undo: move them back in reverse order. - Hiding sections (tabs):
hiddenalone doesn't hide a.page-section(Styling). - List sections (auto layouts: team, testimonials):
section.user-items-list-section[data-sqsp-section="auto-layout"]holds[data-controller^="UserItemsList"][data-current-context]: JSON withuserItems(title,descriptionHTML,button,image.assetUrl). Render from it beside.user-items-list; hide the original in the editor, remove it live if you like. - Popups: sections clip their content, so append the overlay to
#siteWrapper. Give it a theme: the mount'sel.closest("[data-section-theme]")value, or the first non-empty one in#page(the header and cookie banner often have""). The site's default theme is defined only on:root: set on an element inside a differently themed section, it doesn't apply.
Inserted markup doesn't start
Squarespace starts its blocks once, at load. Its markup inserted later (a fetched or cloned section) keeps its HTML, but carousels, lazy images and background video never run, and calling Squarespace's init functions doesn't start them. Build your own UI from the data. Moving sections that are already running is fine.
Reading content
Collections (blog, events, products): add ?format=json to the collection URL.
items[], for a blog:title,fullUrl,excerptandbody(HTML),assetUrl,categories,tags,author,publishOn(epoch ms). Blog-wide:collection.categories,.tags.- 20 items per page. Follow
pagination.nextPageUrlwhilepagination.nextPage, addingformat=jsonback (it's dropped). Never compute offsets; don't trustcollection.itemCount. - The response can be HTML with status 200: parse in try/catch, show an empty state.
- The mount's
data-source(e.g./blog) says where, if set. - Images:
assetUrl + "?format=750w", never the original. - A list section's data is already in
data-current-context: no fetch.
Another page: ?format=json doesn't include a normal page's sections. Fetch its HTML, parse
with DOMParser, take what you need from #page, and use it as static HTML.
Site context: window.Static?.SQUARESPACE_CONTEXT. Signed in, it holds account details
and a CSRF token: never send it anywhere.