Einblick SDK In-Page Editing
Register external sites, understand framework support, and integrate Einblick in-page editing without breaking SSR or host-site caching.
sdkapicmseditingexternal-sites
Einblick In-Page Editing
Open System Settings → API to register external sites, manage allowed origins, and copy the publishable site key used by the in-page editing runtime.
Framework support
@einblick/sdkis framework-agnostic. Use it for typed reads, schema generation, public command calls, and server-side CMS fetching in any JavaScript or TypeScript stack.@einblick/editorcontains framework-neutral bindings, inert DOM attributes, and the lazy browser runtime. It does not require the data SDK.@einblick/editor/reactprovides the editor provider and editable markers for Next.js, Remix, plain React, and Astro React islands.@einblick/editor/nextprovides the App Router boot.@einblick/sdk/reactremains the home of the data-renderingEinblickImagecomponent. SDK 0.x keeps its previous editor symbols only as deprecated compatibility copies.- The hosted editing routes use protocol 2 and retain protocol 1 for one rolling-deployment compatibility window. New integrations always use the proof-bound protocol 2 flow; versions outside the current/previous window are rejected.
Typed reads and response shapes
- Collection reads support
limit,cursor,sort,include,fields, andlocale. Use comma-separated field keys forsortand prefix a field with-for descending order. Without an explicitsort, CMS collections follow Einblick's manual record order, and each record exposes that order assortin record meta. - Use
fields: ['title', 'thumbnail']on collection, singleton, or record reads to fetch only the readable attributes that a page needs. Generated clients type the array to the resource's readable field keys; comma-separated strings also work. Unknown or unreadable fields return400. - For localized native resources,
localeaccepts one language tag or an ordered,Accept-Language-style string. Localized built-in and custom text fields fall back through Workspace and source languages; omittinglocalepreserves the stored source values. - Relation includes only work for readable relation fields and when the same API key also grants the related collection. Included records are returned in
included. - CMS
richtextvalues are returned as structured document JSON, not flattened HTML. - Asset payloads expose stable URLs plus optional
width,height,title,copyright,description,tags, andsort. When a CMS video has a generated web playback variant, the public asset URL resolves to that browser-compatible MP4 or WebM file. Browser-origin requests for non-transformed files are proxied with CORS headers and range support; server-side requests without anOriginheader can follow short-lived storage redirects. - Put shared fetch settings such as
cacheandnext.revalidatein the client'srequestInit. Per-requestfetch.nextsettings merge with those defaults, so adding resource-specificnext.tagsdoes not discard the shared revalidation interval.
Image asset transformations
Use getEinblickAssetUrl(asset, transform) to derive an image variant from the stable asset URL. You can also pass the name of a transform preset included in the asset payload.
import { getEinblickAssetUrl } from '@einblick/sdk'
const socialImage = getEinblickAssetUrl(asset, {
width: 1200,
height: 630,
fit: 'cover',
quality: 82,
format: 'webp',
})- A transform uses
width,height,fit,quality, andformat, which become thew,h,fit,q, andfmtparameters on the asset URL. Explicit dimensions and quality are not snapped to a fixed ladder. Delivery clamps each edge to12,000pixels, quality to1–100, and proportionally scales a width-height pair that would exceed100megapixels. - Supported fit modes are
scale-down,contain,cover,crop,pad,squeeze, andscale-up. The SDK also acceptsfillassqueeze, andinsideoroutsideascontain. - Supported formats are
auto,webp,avif,jpeg,baseline-jpeg,png, andpng-force; the SDK acceptsjpgasjpeg.pngis a preference and can preserve a JPEG source as JPEG, whilepng-forceguarantees PNG output. - Every distinct source-and-parameter combination can create another delivered variant. When dimensions come from arbitrary layout measurements, call
snapEinblickAssetTransform(...)explicitly to keep the variant set bounded. Withnext/image, a shortdeviceSizeslist usually provides the same control.
Resource writes
- Use
client.createResource(...),client.updateResource(...), andclient.deleteResource(...)for the standard resource API. CMS resources use/api/v1/cms/{slug}; native resources use/api/v1/{slug}. - Resource writes require a matching resource write grant on the API key plus writable-field grants for create and update payloads. CMS resources support create, update, and delete. Native resource writes currently support buildings, events, and festivals; native deletes archive the record.
- Pass an
idempotencyKeyfor retryable resource writes. Reusing the same key for the same route and payload returns the stored result; reusing it with a different payload returns a conflict.
Public commands
- Public commands are separate from resource reads and require explicit write-action grants on the API key.
- Use
client.getCommandSchema()to inspect the commands available to the current token. - Use
client.command(name, input)or typed helpers such asclient.jobs.submitApplication(...)for granted actions. Pass anidempotencyKeyfor retryable submit-style calls. - Newsletter signup is available through
client.newsletter.signup(...)when the key grantsnewsletters.signup. It creates or updates a contact, can apply published contact-group slugs, and returns eithersubscribedorpending_confirmationdepending on Workspace newsletter settings.
What the runtime does
- Anonymous visitors keep the normal SSR, caching, and performance profile of the external site.
- Signed-in editors get hover indicators, inline editing for simple fields, and a hosted drawer for more complex fields.
- The publishable site key only identifies the registered site and origin. After sign-in, navigator results and editor saves use the editor's normal Einblick Workspace role permissions; browser editing never uses a secret API key or API-key resource grants.
- By default, authenticated edit mode renders a fixed bottom Einblick control bar with the connected site, one general Collections button, and a More menu for opening Einblick or logging out. Use
chrome="badge"for the previous launcher style orchrome="none"when the host site provides its own entry point. - Use
accentColoronEinblickEditProviderwhen the default Einblick orange does not have enough contrast on a host site. The color replaces SDK-owned orange in hover outlines, quick edit buttons, badge chrome, and the bottom control bar.EinblickNextEditBootforwards the same prop and can readNEXT_PUBLIC_EINBLICK_ACCENT_COLOR. - The collections panel lets editors search all available resources for the external site session. CMS collections are grouped under an expandable CMS item, while supported native resources appear on the same level as that group. Editors can expand or collapse nested CMS collections, open the matching resource in Einblick, edit or delete existing records in the hosted drawer, and create records from the host site. Native drawer editing currently uses the normal Einblick forms for buildings, events, and festivals. Search expands matching branches so matching children stay reachable. Record labels use the configured display field when available, then fall back to
title,name,headline, another displayable field, the slug, or a generic record label. Singleton resources only show the create action until their first record exists. - Editors can reorder CMS records in the collections panel with the drag handle unless sorting is disabled for that collection.
- Wrap rendered list surfaces with
EditableCollectionand acreateEditableCollectionBinding({ resourceSlug })binding when a surface renders multiple records. SetshowCreateButtononEditableCollectionwhen that surface should render an edit-mode Add record button at the end of the list that opens the hosted drawer in create mode without leaving the host site. Keep item-levelEditableRegionbindings inside that wrapper so each record still has its own hover edit state. - In the hosted drawer, editors can reorder multi-value
filesandtagsfields. Savedfilesarrays keep that order in SDK and public API responses. - Relation and multi-relation fields in the hosted drawer load selectable records through the current edit session, scoped to the field's configured target collection. The picker uses the target collection's display field for labels, currently returns up to 100 records from the same Workspace, and lets editors create or edit related records without opening the CMS separately. When a related record is created and the target collection has a relation field back to the current collection, Einblick pre-fills that back-reference.
- Field-level saves patch currently visible text-like bindings immediately.
- Single-value image and file bindings can upload and replace media directly from the external editor. Multi-value
imagesandfilesfields open the hosted drawer instead, so editors can see the full list, add or remove files, and reorder where supported. CMS image fields accept images; CMSfileandfilesfields can restrict accepted MIME types and max size. Generic CMS file fields default to 500 MB and cannot exceed the normal 5 GB per-file limit for signed-in uploads. - The host site's
onSaverefresh/refetch path is still needed when the rendered page depends on resolved asset URLs, SSR output, or cached data.
Login and edit sessions
EinblickLoginButtonand the providerlogin()action open sign-in from the user's click. The?einblick-loginand?loginURL triggers first show a prompt on the host page, and only its Sign in button opens the popup. This keeps popup creation tied to an explicit browser gesture.- If the popup is blocked, closed, or the secure handshake does not finish within two minutes, edit mode stays off and the host receives a sign-in error that can be retried. A blocked popup no longer triggers an automatic full-page redirect.
- Protocol 2 uses a short-lived, proof-bound broker between the host, Einblick popup, and embedded bridge. The edit-session bearer is claimed inside the bridge and is never returned through the host site's URL or hash.
- After successful sign-in, the popup closes and the editor activates on the current page.
- The provider stores the edit session per site key on the current device, so editors can keep working on the same registered origin until the session expires or they leave edit mode.
- Edit sessions are origin-bound. If you use multiple allowed origins for one site, editors sign in separately on each origin.
What the host site still owns
- The host site still owns server-side reads, page rendering, and any long-lived cache layers. Keep secret API keys server-side for reads and code generation; the browser edit runtime uses only the publishable site key and edit session.
- The SDK can patch some currently visible DOM immediately, but it cannot automatically purge framework-specific SSR caches, CDN caches, or static generation outputs on every host.
- For the best experience, wire the provider
onSavecallback to the host site's cache invalidation plus the smallest refresh/refetch mechanism that re-renders fresh data. The callback also fires after hosted-drawer creates and deletes, with deleted records reported asvalue: null. - The default control bar reserves bottom body space and sets
--einblick-editor-bottom-offsetplusdata-einblick-editor-bar-activeon<html>. Move fixed bottom host UI with that variable, or setreserveBottomSpace={false}if the host site manages spacing itself.
Cache invalidation best practices
- If your site has no persistent SSR or CDN caching, text-like field edits and inline media replacements may already look correct immediately. Still refresh or refetch after save when the page renders resolved asset URLs or cached data.
- If your site uses tagged data caches, revalidate the smallest tag scope that covers the edited resource.
- If your site statically renders routes, also revalidate the affected route output. Tags refresh the data cache; paths refresh the prerendered page or layout cache.
- If your site uses a CDN or reverse proxy cache, purge the relevant cache key or path after a successful save.
Next.js
- Keep CMS fetching on the server.
- Tag resource fetches with
next.tags, preferably throughcreateEinblickCmsTags()from@einblick/sdk/next/cache. - Expose
POST /api/einblick/revalidatewithcreateEinblickRevalidateHandler({ tags, secret }). The secret is required, must stay server-only, and must match the token configured for the external site in Einblick. For route handlers and Einblick webhooks, use immediate expiration (revalidateTag(tag, { expire: 0 }));profile: "max"can serve stale content once before refreshing. - For statically rendered App Router pages, make sure the same route also invalidates affected paths. The SDK helper defaults to
revalidatePath('/', 'layout'), which is a solid small-site default. Larger apps can pass a narrowerpathsmap. - Mount
EinblickNextEditBootin the root layout. It callsrouter.refresh()after saves; the authenticated Einblick webhook performs server-side cache invalidation. - In the external site settings, keep the revalidation endpoint set to
/api/einblick/revalidateso direct edits made inside Einblick invalidate the same tags. Einblick posts{ resourceSlug }with the configured secret after CMS creates, updates, deletes, reorders, and external-editor file changes.
// lib/einblick-cache.ts
import { createEinblickCmsTags } from '@einblick/sdk/next/cache'
export const einblickTags = createEinblickCmsTags({
fanOut: { 'gedanken-sections': ['gedanken'] },
})// app/api/einblick/revalidate/route.ts
import { createEinblickRevalidateHandler } from '@einblick/sdk/next/cache'
import { einblickTags } from '@/lib/einblick-cache'
export const POST = createEinblickRevalidateHandler({
tags: einblickTags,
secret: process.env.EINBLICK_REVALIDATE_SECRET,
// Default: paths: [{ path: '/', type: 'layout' }]
})// app/layout.tsx
import { EinblickNextEditBoot } from '@einblick/editor/next'
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html>
<body>
<EinblickNextEditBoot>{children}</EinblickNextEditBoot>
</body>
</html>
)
}Remix and custom SSR
- Keep CMS fetching on the server or in loaders.
- After save, invalidate your loader data, route cache, or CDN cache in the smallest possible scope, then trigger the smallest refresh/refetch path that re-renders fresh server data.
- If you use client-side query caches, trigger a refetch after save when needed.
Recommended integration shape
- Fetch CMS or native resource data on the server with
@einblick/sdk. - Map raw records into view models and keep bindings from
@einblick/editoralongside them. - Wrap CMS-backed output with
EditableText,EditableImage, orEditableRegion. For repeated lists, wrap the full list inEditableCollectionand keep item-level regions inside it. For native records, setsourceType: 'native'on the binding and pass the native resource slug plus record ID. - Mount
EinblickEditProviderwith the publishable site key and choose the default control bar,chrome="badge", or custom host chrome. SetaccentColoronly when the default orange is hard to see against the host site. - Use
EinblickLoginButtonor?einblick-login/?loginas the editor entry point. - Use
onSaveto revalidate caches and trigger the host site's refresh/refetch path when the page reads resource data through SSR or other caches.
Content modeling tips
- Prefer first-class fields for editor-facing content:
stringfor labels,textormarkdownfor prose, media fields for images/files, and related collections for repeatable items. Avoid storing copy, CTA labels, or prose segments injson; it is difficult for non-technical editors and inline editing cannot target individual JSON properties. - Keep one complete source field in one field marker. Do not attach the same
binding to values produced by
split,map,slice,[0], excerpts, or fallbacks. Render raw multilinetextonce with preserved whitespace; render complete Markdown, rich text, semantic paragraphs, lists, and links under oneEditableRegion. - If a host site renders only a subset of Markdown, keep that rule in the host
mapper. For example, a site may treat
**bold**as highlighted text and flatten all other Markdown syntax to plain text. - When seeding or importing CMS media, put each file in the folder for the CMS
record that owns the image/file field, or in a collection folder allowed by
that field's existing-file-selection policy. Public API asset resolution is
scoped to the resource, field, and allowed folder roots; reusing a file ID
from a different record can make that media field resolve to
null.