createInterceptor(...) entries.
Tutorial source code: OrderlyNetwork/fast-place-order-plugin —
https://github.com/OrderlyNetwork/fast-place-order-plugin. Read src/index.tsx (registerFastPlaceOrderPlugin) alongside the steps below; the repo is the canonical runnable example for this tutorial.
Prerequisites: Tutorial 1 — plugin scaffold and registerPlugin basics.
Scope: Implementation inside the plugin package only (interceptors, shared state, dedupe, Hooks). Loading the plugin in a host app, provider wiring, and production QA are not covered on this page.
Step 1 — Understand why you need multiple targets
A single interceptor is enough for simple overlays. For product-grade UX you often need several insertion points — for example:- Body target — where the feature lives (panel, popup, extra controls).
- Menu target — where users discover and switch the feature (display control list, settings, and so on).
- Further targets — headers, mobile sheets, or secondary menus; same registration, more
createInterceptorrows.
useFastPlaceOrderVisibility shared between the menu interceptor and the widget.
Step 2 — Define the plugin factory and options
- Import
createInterceptorandOrderlySDKfrom@orderly.network/plugin-core. - Export a register function such as
registerFastPlaceOrderPlugin(options?)that returns(SDK: OrderlySDK) => { ... }and, inside that callback, callsSDK.registerPlugin({ ... })(the usual plugin factory shape from Tutorial 1). - Normalize options once (for example
const autoShowOnFullscreen = options?.autoShowOnFullscreen ?? true) so every interceptor sees the same values.
Step 3 — Call SDK.registerPlugin once with a stable id
Inside the returned function, call SDK.registerPlugin({ ... }) a single time with:
id,name,version,orderlyVersion— as required by your release process.interceptors: [ ... ]— onecreateInterceptor(...)per target (this guide covers two; add more entries for additional surfaces).- Optional
setupfor non-UI side effects (subscriptions, logging).
registerPlugin call, two interceptor slots. Placeholders omit widget/menu logic; Steps 4–6 fill those in.
items={nextItems} on the same <Original /> (Steps 5–6).
Many targets still mean one plugin registration and one shared lifecycle.
Step 4 — Interceptor A: mount the widget on the desktop body
Target:"TradingView.Desktop" (string must match runtime injector targets; paths are case-sensitive).
Pattern:
- Wrap with anything the widget needs globally (the reference uses
LocaleProviderfor i18n). - Render
<Original {...props} />first so the default trading view stays intact. - Render your widget after the original, passing through whatever props the injector supplies for that target (for example
symbol={props.symbol}) plus your plugin options (autoShowOnFullscreen).
interceptors array, then menus and chrome, so the file reads top-down like the user journey.
Step 5 — Interceptor B: inject a menu item on the display control list
Target:'TradingView.DisplayControl.DesktopMenuList'.
Pattern:
- Read/write shared visibility with the same hook the widget uses (in the reference,
useFastPlaceOrderVisibility(false)inside the interceptor callback). - Build the next
itemsarray fromprops.items. - Return
<Original {...props} items={nextItems} />so you only extend the list, not replace the whole menu implementation.
Step 6 — Dedupe menu items before append
List targets re-render often. If you always push a new object without filtering, you can get duplicate rows. Do this every time you buildnextItems:
- Choose a stable string
idfor your row (for example"fastPlaceOrderPopupToggle"). - Remove any existing item with that
idfromprops.items ?? []. - Append your
customItemonce.
useMemo so the array identity stays stable when dependencies (isWidgetVisible, props.items, setter) do not change:
Step 7 — Keep Hook usage valid in interceptors
In this Orderly pattern, the interceptorcomponent: (Original, props) => ... runs as a React function component, so Hooks are allowed there.
Rules of thumb:
- Call Hooks unconditionally and in a fixed order on every render.
- If the interceptor grows large, extract a small inner component and call Hooks there instead — same rules, easier to read.
react-hooks rules.
Step 8 — Verify from the plugin project
Stay inside the plugin repo / package:pnpm build(or your package script) completes with no TypeScript errors.pnpm lintpasses if your pipeline requires it.- Code review pass:
interceptorsarray lists every intended target once; menu branch uses stableid+ filter (Step 6) so duplicates cannot accumulate on re-render. - Hook audit: no conditional Hook calls in interceptor callbacks (Step 7).
Common failure modes
Skills and tooling
Next step
Extract target strings into shared constants and add module augmentation for typedprops on each target so createInterceptor(...) stays accurate as the SDK evolves.