Skip to content

[Admin] Add the metaobject details form extension contract - #4630

Draft
kjbrum wants to merge 2 commits into
2026-10-rcfrom
kjbrum/metaobject-custom-form-target-types
Draft

[Admin] Add the metaobject details form extension contract#4630
kjbrum wants to merge 2 commits into
2026-10-rcfrom
kjbrum/metaobject-custom-form-target-types

Conversation

@kjbrum

@kjbrum kjbrum commented Aug 16, 2026

Copy link
Copy Markdown

Background

Admin Web is adding an internal target that lets an app replace the visible form area on an app-owned Metaobject details page. Admin still owns route state, validation, Save, Discard, and persistence.

This PR adds the app-facing authoring and testing contract for admin.metaobject-details.form.render on the active 2026-10-rc line. Core accepts the target from 2025-10; new SDK features land on the active release-candidate line.

Related work:

Solution

Add a narrow MetaobjectFormApi and map it to the new Admin render target.

The supported API contains only:

  • extension, intents, and navigation from the baseline Admin runtime;
  • snapshot, a read-only signal with value and subscribe;
  • async setFieldValue({key, value}); and
  • setSaveHandler(handler | null) for pending writes that Admin must await before saving.

The snapshot contains the complete trusted form definition whenever the target launches:

interface MetaobjectFormSnapshotField {
  key: string;
  type: string;
  value: string;
  editable: boolean;
  errors: string[];
}

Admin launches the target only for definitions with at most 40 trusted fields. Larger definitions remain entirely native instead of receiving a truncated snapshot.

setFieldValue returns SUCCESS or one of these structural error codes:

  • UNKNOWN_FIELD
  • READ_ONLY
  • COMPUTED
  • UNSUPPORTED_FIELD_TYPE
  • INVALID_VALUE
  • VALUE_TOO_LARGE
  • NOT_ACTIVE

The target intentionally does not expose raw form state, submit, reset, mutation helpers, a GraphQL client, tools, pickers, or caller-supplied app identity. A generic Host can expose an incidental empty data: {}, but data is not part of this supported contract.

Component architecture

The target allows seven Polaris components:

  • Grid
  • GridItem
  • Heading
  • Section
  • Text
  • TextField
  • contextual MetaobjectField

<s-metaobject-field fieldKey="..." /> asks Admin to render its native editor for a field in the current route form. This preserves native behavior for references and other complex field types. The extension can combine these host-rendered fields with custom Remote DOM editors.

The Preact example demonstrates the expected lifecycle:

snapshot.value
  -> render current host values
  -> queue async setFieldValue writes
  -> register setSaveHandler for the newest queued write
  -> clear the handler with setSaveHandler(null)
  -> unmount with render(null, document.body)

It also restores the host value after structural rejection and reports transport failures separately.

Tester and package behavior

  • Add a target-specific tester factory with the same narrow API.
  • Initialize intents as {launchUrl: undefined} without a fake invoke method.
  • Initialize snapshot with no fields and no unsupported peek method.
  • Omit the unsupported incidental data object.
  • Export the new API types and both named and default MetaobjectFormComponents types.
  • Add a minor changeset for @shopify/ui-extensions and @shopify/ui-extensions-tester.

🎩

This prototype intentionally omits new automated test coverage. Validate the package contract with build and static checks:

yarn build
yarn type-check
yarn lint

Then verify a consumer project:

  1. Import @shopify/ui-extensions/preact.
  2. Declare admin.metaobject-details.form.render as the target.
  3. Read shopify.snapshot.value and subscribe when reactive updates are needed.
  4. Await shopify.setFieldValue({key, value}) and handle each error code.
  5. Register the newest pending write with shopify.setSaveHandler.
  6. Clear the handler with shopify.setSaveHandler(null) during cleanup.
  7. Render a native field with <s-metaobject-field fieldKey="..." />.
  8. Confirm that TypeScript rejects unsupported APIs such as data, submit, reset, and snapshot.peek().

A full browser tophat also needs the matching Core registration and Admin Web branch. Follow tooling/metaobject-form-extension-tophat/README.md in the Admin Web change after Core deploys.

Validation completed

  • Package build
  • Type-check
  • Lint
  • git diff --check

Checklist

  • I have 🎩'd these changes. The real app-dev tophat waits for deployed Core registration and a human-linked development shop.
  • I have updated relevant documentation, the target example, and tester guidance.

kjbrum added 2 commits August 16, 2026 15:20
Assisted-By: devx/f69a4e6e-0eb7-4653-b3e3-7097ac70f437
Assisted-By: devx/f69a4e6e-0eb7-4653-b3e3-7097ac70f437
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.

1 participant