Skip to Content
Editor SDKClient API

Client API Reference

The client entry point (@eide/foir-editor-sdk) provides React components, hooks, and a GraphQL client for building editor iframes that communicate with the Foir admin.

EditorProvider

The root context provider that manages the postMessage lifecycle between your iframe and the Foir admin host. Wrap your entire application with this component.

import { EditorProvider } from '@eide/foir-editor-sdk'; function App() { return ( <EditorProvider parentOrigin="https://app.foir.dev" onInit={(init) => console.log('Editor initialized', init.modelKey)} onSaveResult={(success, error) => { if (!success) console.error('Save failed:', error); }} > <MyEditor /> </EditorProvider> ); }

Props

interface EditorProviderProps { children: ReactNode; parentOrigin?: string; apiUrl?: string; onInit?: (init: EditorInit) => void; onValuesChanged?: (values: Record<string, unknown>) => void; onVariantChanged?: (variantKey: string, values: Record<string, unknown>) => void; onSaveResult?: (success: boolean, error?: string, versionId?: string) => void; onPublishResult?: (success: boolean, error?: string) => void; onThemeChanged?: (theme: 'light' | 'dark') => void; onSelectionChanged?: ( keys: string[], fields: Array<{ key: string; label: string; type: string }> ) => void; onLocaleChanged?: (locale: LocaleContext) => void; }
PropTypeDefaultDescription
childrenReactNodeRequiredYour editor application.
parentOriginstringVITE_PARENT_ORIGIN or NEXT_PUBLIC_PARENT_ORIGIN env var, falling back to "*"The origin of the Foir admin host window, used for postMessage validation. Set this explicitly in production.
apiUrlstringVITE_API_URL or NEXT_PUBLIC_API_URL env var, falling back to "http://localhost:4001/graphql"Platform API GraphQL endpoint URL. Overridden by the apiUrl provided in the init message if present.
onInit(init: EditorInit) => voidCalled when the host sends init data. Use this for one-time setup logic.
onValuesChanged(values: Record<string, unknown>) => voidCalled when the host pushes updated values (e.g., from a remote save by another user).
onVariantChanged(variantKey: string, values: Record<string, unknown>) => voidCalled when the user switches to a different variant in the host.
onSaveResult(success: boolean, error?: string, versionId?: string) => voidCalled after a requestSave completes. Includes the new versionId on success.
onPublishResult(success: boolean, error?: string) => voidCalled after a requestPublish completes.
onThemeChanged(theme: 'light' | 'dark') => voidCalled when the host theme changes. The theme value from useEditor updates automatically; use this callback for side effects.
onSelectionChanged(keys: string[], fields: Array<...>) => voidCalled when the user selects different fields in the host content editor (sidebar mode).
onLocaleChanged(locale: LocaleContext) => voidCalled when the active locale changes in the host (sidebar mode).

useEditor

The primary hook for interacting with the Foir admin host. Must be called inside an EditorProvider.

import { useEditor } from '@eide/foir-editor-sdk'; function MyEditor() { const { isReady, init, theme, client, updateField, updateValues, requestSave, requestPublish, requestOperation, resize, setDirty, requestNavigate, configMode, selectedFieldKeys, selectableFields, localeContext, } = useEditor(); if (!isReady) return null; // ... }

Return Value

interface EditorContextValue { isReady: boolean; init: EditorInit | null; theme: 'light' | 'dark'; client: PlatformClient; updateValues: (values: Record<string, unknown>) => void; updateField: (path: string, value: unknown) => void; requestSave: () => void; requestPublish: () => void; requestOperation: ( operationKey: string, input?: Record<string, unknown>, options?: { mode?: 'sync' | 'async' } ) => Promise<{ success: boolean; result?: unknown; error?: { code: string; message: string }; }>; resize: (height: number) => void; setDirty: (isDirty: boolean) => void; requestNavigate: (path: string) => void; configMode: 'editor' | 'sidebar'; selectedFieldKeys: string[]; selectableFields: Array<{ key: string; label: string; type: string }>; localeContext: LocaleContext | null; }
PropertyTypeDescription
isReadybooleantrue once the host has sent the init message. Always check this before accessing other values.
initEditorInit | nullThe full initialization payload from the host. null until isReady is true.
theme'light' | 'dark'Current host theme. Updates automatically when the user toggles themes in the admin.
clientPlatformClientA GraphQL client pre-configured with the API URL and key from the host. Throws if called before init.
updateValues(values) => voidSend a complete replacement of all field values to the host. Use updateField for granular changes instead when possible.
updateField(path, value) => voidSend a single field update to the host. The path supports dot notation (e.g., 'content.title').
requestSave() => voidAsk the host to save the current values. Listen for the result via the onSaveResult callback on EditorProvider.
requestPublish() => voidAsk the host to publish the current version. Listen for the result via onPublishResult.
requestOperation(operationKey, input?, options?) => Promise<...>Ask the host to execute a platform operation. Pass { mode: 'sync' } (default) for inline results — times out after 60 seconds. Pass { mode: 'async' } to dispatch and receive an { executionId } immediately, then subscribe to jobs:{executionId} for progress + terminal events.
resize(height) => voidReport the iframe content height to the host for resizing. Prefer useAutoResize over calling this directly.
setDirty(isDirty) => voidReport whether the editor has unsaved changes. The host uses this to show save prompts on navigation.
requestNavigate(path) => voidAsk the host to navigate to an admin path (e.g., '/records/my-model'). The path must start with /.
configMode'editor' | 'sidebar'How this editor is rendered: 'editor' for main content area, 'sidebar' for the sidebar panel.
selectedFieldKeysstring[]Currently selected field keys in the host content editor. Only relevant in sidebar mode.
selectableFieldsArray<{ key, label, type }>All fields available for selection in the host. Only relevant in sidebar mode.
localeContextLocaleContext | nullLocale information when the host is in a translation workflow. null outside of translation contexts.

useAutoResize

Automatically reports your iframe content height to the host using a ResizeObserver. Returns a callback ref to attach to your root container element.

import { useAutoResize } from '@eide/foir-editor-sdk'; function MyEditor() { const containerRef = useAutoResize({ minHeight: 600 }); return <div ref={containerRef}>Your editor content here</div>; }

Options

interface UseAutoResizeOptions { /** Minimum height to report. @default 400 */ minHeight?: number; /** Debounce delay in milliseconds. @default 100 */ debounce?: number; /** Whether auto-resize is enabled. @default true */ enabled?: boolean; }
OptionTypeDefaultDescription
minHeightnumber400Minimum height in pixels. Prevents the iframe from collapsing to a very small size.
debouncenumber100Delay in milliseconds before reporting height changes. Prevents excessive postMessage traffic during rapid layout changes.
enabledbooleantrueSet to false to disable auto-resize (e.g., when using height: 'fill' placement).

Return Value

Returns a React.RefCallback<HTMLDivElement> — attach it to the root div of your editor. The hook observes the element’s scrollHeight and sends resize messages to the host whenever it changes.

createPlatformClient

Creates a standalone GraphQL client for the Foir platform API. You typically do not need to call this directly because EditorProvider creates one automatically (accessible via useEditor().client). Use this if you need a client outside of React context.

import { createPlatformClient } from '@eide/foir-editor-sdk'; const client = createPlatformClient({ apiUrl: 'https://api.foir.io/graphql', apiKey: 'sk_test_...', }); const { data } = await client.query<{ product: { title: string } }>( `query GetProduct($id: ID!) { product(id: $id) { title } }`, { id: '123' } );

Options

interface PlatformClientOptions { /** GraphQL endpoint URL. */ apiUrl: string; /** API key for authentication. */ apiKey: string; }

PlatformClient Interface

interface PlatformClient { query<T = Record<string, unknown>>( query: string, variables?: Record<string, unknown> ): Promise<GraphQLResponse<T>>; }

GraphQLResponse

interface GraphQLResponse<T = Record<string, unknown>> { data: T | null; errors?: GraphQLError[]; } interface GraphQLError { message: string; locations?: Array<{ line: number; column: number }>; path?: Array<string | number>; extensions?: Record<string, unknown>; }

PlatformClientError

Thrown when a GraphQL request fails (network error or GraphQL errors in the response).

class PlatformClientError extends Error { status?: number; response?: unknown; }

EditorInit

The initialization payload sent by the host when your editor loads. Access it via useEditor().init.

interface EditorInit { /** Current record field values. */ values: Record<string, unknown>; /** Record metadata (e.g., timestamps, status). */ metadata?: Record<string, unknown>; /** The model key for this record (e.g., 'product', 'redirect'). */ modelKey: string; /** The record ID. Empty string for new records. */ recordId: string; /** Human-readable natural key (slug, handle, etc.). */ naturalKey?: string; /** Current version ID, or null for unversioned records. */ versionId: string | null; /** Current variant key, or null if not using variants. */ variantKey: string | null; /** Current locale code, or null. */ locale: string | null; /** The model's field schema definition. */ schema: unknown; /** Whether the record is editable or view-only. */ mode: 'edit' | 'readonly'; /** Current admin theme. */ theme: 'light' | 'dark'; /** API key for platform GraphQL requests. */ apiKey: string; /** Platform API GraphQL endpoint URL. */ apiUrl: string; /** Config connection domain (e.g., my-store.myshopify.com). */ connectionDomain?: string; /** How this editor is rendered: 'editor' (main area) or 'sidebar'. */ configMode?: 'editor' | 'sidebar'; /** Currently selected field keys (sidebar mode). */ selectedFieldKeys?: string[]; /** Available fields for selection (sidebar mode). */ selectableFields?: Array<{ key: string; label: string; type: string }>; /** Locale context for translation workflows (sidebar mode). */ localeContext?: LocaleContext | null; }

LocaleContext

Locale information provided to sidebar editors during translation workflows.

interface LocaleContext { /** The locale currently being edited. */ currentLocale: string; /** The source/default locale to translate from. */ sourceLocale: string; /** Whether the user is actively in a translation workflow. */ isTranslating: boolean; /** All available locales for this project. */ locales: Array<{ code: string; displayName: string; isDefault: boolean; }>; }

PostMessage Protocol

The SDK handles the postMessage protocol automatically. This section documents the message types for reference. You do not need to construct these messages yourself.

Editor to Host Messages

Messages your editor sends to the Foir admin host (handled by the SDK).

TypePayloadDescription
ready{}Sent automatically when EditorProvider mounts. Signals the host to send init.
update-values{ values: Record<string, unknown> }Full replacement of all field values.
update-field{ path: string, value: unknown }Single field update. Path supports dot notation.
request-save{}Request the host to save.
request-publish{}Request the host to publish.
resize{ height: number }Report iframe content height.
dirty{ isDirty: boolean }Report unsaved changes state.
request-navigate{ path: string }Request navigation to an admin path.
request-operation{ operationKey: string, input?: Record<string, unknown>, mode?: 'sync' | 'async', requestId: string }Request execution of a platform operation.

Host to Editor Messages

Messages the Foir admin host sends to your editor (handled by EditorProvider).

TypePayloadDescription
initEditorInitInitialization data. Sent once after the editor sends ready.
values-changed{ values: Record<string, unknown>, source: 'remote' }Values updated externally (e.g., by another user).
variant-changed{ variantKey: string, values: Record<string, unknown> }User switched to a different variant.
save-result{ success: boolean, error?: string, versionId?: string }Result of a request-save.
publish-result{ success: boolean, error?: string }Result of a request-publish.
theme-changed{ theme: 'light' | 'dark' }Admin theme changed.
selection-changed{ selectedFieldKeys: string[], fields: Array<...> }Field selection changed in host (sidebar mode).
locale-changedLocaleContextActive locale changed (sidebar mode).
operation-result{ requestId: string, success: boolean, result?: unknown, error?: { code, message } }Result of a request-operation.

Protocol Utilities

The SDK exports two validation functions and a protocol version constant, primarily useful if you need to handle raw postMessage events outside of EditorProvider:

import { isValidEditorMessage, isValidHostMessage, PROTOCOL_VERSION, // '1.0' } from '@eide/foir-editor-sdk';
  • isValidEditorMessage(data) — type guard for EditorToHostMessage
  • isValidHostMessage(data) — type guard for HostToEditorMessage
Last updated on