An example of hosting a React app inside Aras Innovator (Release 35) as a form body. The worked
case replaces the Part form with four React fields; the integration layer (src/aras/) is
generic and reusable for any ItemType.
The app is injected as an iframe by an html field (or an onload Form Event Method) of a custom
Form, is served from the Innovator client origin (<Client>/customer/part-react/) and therefore
talks to Innovator through top.aras.IomInnovator / parent.aras with the logged-in user's
session – permissions, lock/edit state, validation and the standard Save/Done toolbar keep working
unchanged.
Start with WALKTHROUGH.md – how Innovator renders forms, what the bridge hooks into, step-by-step setup, dev loops and the gotchas. PLAN.md has the original investigation notes.
item_number, name, description, classification – bound by property name; labels,
required/readonly flags, data types and the class structure come from the Part ItemType metadata,
edit/view mode from the item window. Adding a field is one line in
src/features/part/PartForm.tsx (<TextField name="…" />, <TextAreaField …/>,
<ClassificationField …/>); adding a whole form for another ItemType is a component plus one
registry line – see WALKTHROUGH.md §7.
- New Form (e.g.
Part React); Body CSShtml, body { margin: 0; overflow: hidden; }; Formheight= desired height of the form area (stock Part form: 300). - An
htmlfield on the Form whose HTML is the contents ofaras-package/html-field-snippet.html– a full-size iframe hosting this app; itsposition:fixedstyle escapes the field wrapper, so it fills the form area cleanly. (Alternative: no fields, and a JavaScript Method with the code inaras-package/form-onload-method.jsattached as a Form Event with eventonload– same result.) - New View on the
PartItemType → that Form,type = default,client = js,display_priority = 0,role= pilot identity (later World). The stockPartview stays. - Deploy this app to
<Client>\customer\part-react\(below).
Quick check without any configuration: open a Part in Innovator, in DevTools pick the form frame
(instance) as the console context and paste the Method code – the React form appears over the
stock form and is fully wired (Edit/Save work).
src/aras/ bridge to Innovator
types.ts minimal typings for aras / Form window / IOM
resolve.ts find the Form window + aras (tabbed client and tear-off windows)
bridge.ts reads from document.item, writes via handleItemChange, change notifications
metadata.ts ItemType properties + class structure from the client metadata cache
dialogs.ts Innovator ClassStructureDialog wrapper
ArasProvider.tsx React context + hooks (useProperty, useItemSnapshot, useEditMode, …)
mock/ in-memory stand-in for standalone dev & tests
src/fields/ Field, TextField, TextAreaField, ClassificationField (+ useDraft)
src/features/part/PartForm.tsx the Part form layout
src/registry.ts itemTypeName → lazy form component (one app serves all registered ItemTypes)
dev/index.html Vite "backend integration" shell for HMR inside Innovator
scripts/deploy.ps1 copy build / dev shell into the Aras client folder (self-elevates)
aras-package/ snippets for the Aras-side configuration
| Command | What it does |
|---|---|
npm install |
once |
npm run dev |
Vite dev server on http://localhost:5173 (standalone = mock mode) |
npm run deploy:dev |
copies dev/index.html to <Client>\customer\part-react\index.html (UAC prompt) |
npm run build |
type-check + production build to dist/ |
npm run deploy |
build + copy dist/ to <Client>\customer\part-react\ (UAC prompt) |
npm test |
vitest (bridge / metadata / resolve) |
npm run typecheck |
tsc -b |
ARAS_CLIENT_ROOT (or -ClientRoot) overrides the default
C:\Program Files (x86)\Aras\Innovator\Innovator\Client.
Inside Innovator with HMR – npm run deploy:dev once, then npm run dev and open a Part.
The page served by Innovator is only a shell; modules, CSS and hot reload come from the Vite dev
server (the document stays same-origin with Innovator, so parent.aras works). If the dev server
is down the shell says so.
Standalone / mock – npm run dev and open http://localhost:5173/ (or any URL with ?mock=1).
A yellow toolbar simulates Edit/Done, Save, Reset; the mock implements the same contracts the real
bridge uses (document.item, handleItemChange, observersHash, onformpopulated,
registerEventHandler, the classification dialog via window.prompt). Good for layout/UI work and
it is what the unit tests run against.
Production – npm run deploy, then bump the version in the field/Method snippet (or
hard-refresh) so the browser re-fetches index.html; assets are content-hashed.
main window (top) aras, aras.IomInnovator, ArasModules, OAuth token
└ item tab window.item, setEditMode/setViewMode, toolbar (Edit/Save/Done/…)
└ Form iframe document.item, document.isEditMode, handleItemChange, observersHash, onformpopulated
└ this app reads document.item, writes via handleItemChange, re-renders on onformpopulated /
observer events / ItemLock|ItemSave|ItemVersion|ItemRefresh
- Reads –
aras.getItemProperty(document.item, name)(neutral format). - Writes –
validateFieldValueByPropEvent(ItemType property rules) thenhandleItemChange(name, value, dataType): setsisDirty,action="update", enables Save. Refused when not in edit mode. Saving is the standard toolbar → same server events, permissions, required-property checks as the stock form. - Permissions – enforced by the server on every call made with the user's session; the UI
additionally honours edit mode (lock), property
readonly/is_required, and can askaras.getPermissions('can_update' | …)where it needs to show/hide actions. - Classification – opens Innovator's own
ClassStructureDialog.htmlthroughArasModules.Dialog.show, exactly like the stock field.
- Fixed form height (Form
height) – the app scrolls internally if it needs more room. - v1 fields only; lists, dates, item pickers, thumbnails, class-specific fields are the next
components (the binding layer already exposes
dataType,dataSourceId,pattern). - Ctrl+S inside the iframe is not forwarded to the item window yet.