Have you ever wanted to compose ScriptUI with JSX, like so:
<dialog text="Neat!" properties={{ closeButton: true }}>
<button text="Click me!" size={[100, 200]} onClick={() => alert("Doink!")} />
</dialog>Well, now you can! Plus, TypeScript will guide you through each prop with auto completions!
You can even create functional components:
const MyHeader = ({ text }: { text: string }) => (
<group orientation="row" alignChildren="fill">
<static-text text={text}></static-text>
</group>
);
const MyUI = (
<dialog text="Could it be?!" properties={{ resizeable: true }}>
<MyHeader text="Neat!" />
</dialog>
);And what would JSX be without "hooks":
import { onWindow, uniqueId, type ScriptUIElements } from "extendscript-ui";
const MyButton = ({ text, onClick }: ScriptUIElements["button"]) => {
const name = uniqueId("my_button");
onWindow((window) => {
const el = window.findElement(name);
el.addEventListener("mouseover", () => (el.text = "Hello mouse!"));
el.addEventListener("mouseout", () => (el.text = text));
});
return <button text={text} onClick={onClick} properties={{ name }}></button>;
};That's not enough, you say? What if you could render SVGs? (You can!)
const MySVG = ({ fill, stroke }: { fill: string, stroke: string }) => (
<svg>
<circle cx="75" cy="75" r="70" fill={fill} stroke={stroke} stroke-width="10" />
</svg>
));Tip
Want to skip all this setup? Jump right into an /examples project!
You'll need TypeScript and a bundler for your code. Here are some ExtendScript starters with TypeScript support to check out:
@motiondeveloper/adobe-script-starter@fartinmartin/adobe-lib-starter@Klustre/extender(see note on TypeScript)
npm i extendscript-uiUpdate your tsconfig.json:
Be sure to use .tsx files for JSX syntax highlighting. Import jsx to satisfy TypeScript and for code completion:
// index.tsx
import { jsx } from "extendscript-ui";
export const ExampleUI = (
<dialog text="Neat!" properties={{ closeButton: true }}>
<button text="Click me!" onClick={() => alert("Doink!")} />
</dialog>
);Then, use createWindow to render your template. This will create a Window, wire up your event callbacks, and return the Window.
import { createWindow } from "extendscript-ui";
const window = createWindow(ExampleUI); // ExampleUI from previous code block
window.show();To define behavior that requires the elements to be rendered (e.g. element.addEventListener) use the onWindow "hook". The callback function passed to onWindow will run after the Window object has been created and receives a reference to it.
onWindow((window) => {
window.addEventListener("mouseover", () => alert("Hello!"));
});Tip
ScriptUI has a helpful findElement method to use inside this hook! extendscript-ui also exports a uniqueId helper to ensure element properties.name values are valid.
import { onWindow, uniqueId } from "extendscript-ui";
const MyText = ({ text, properties }) => {
const name = properties?.name ?? uniqueId("my_text");
onWindow((window) => {
const el = window.findElement(name);
el.addEventListener("mouseover", () => (el.text = "Hello mouse!"));
el.addEventListener("mouseout", () => (el.text = text));
});
return <static-text text={text} properties={{ name }}></static-text>;
};extendscript-ui uses a custom jsxFactory to transform JSX into a ScriptUI Resource Specifications-compliant string. When you pass your UI to createWindow it creates a new Window object using your compiled specification string. Then, it attaches any event handlers (e.g. onClick, etc.) to their respective UI elements and runs all of the side effect functions you passed to the onWindow "hook".
- Set element's
sizebased on<svg width="100" height="100">? - Move
propertiesintoInstancePropsprops? - Default text nodes to
<static-text/>? e.g<button>hello!</button> === <button text="hello!"/> - Figure out
TreeView | ListBox | DropDownListrendering - Remove
typeattribute from native types since it's defined by tag? - ProgressBar helpers?
- Turn these TODOs into issues...?

{ "compilerOptions": { // ...your other config options // tell TypeScript how to find extendscript-ui's jsx.d.ts declarations: "typeRoots": ["./node_modules/extendscript-ui/dist"], "types": ["types/jsx.d.ts"], // tell TypeScript how to transform your JSX code and the name of the jsxFactory fn to use when doing so: "jsx": "react", "jsxFactory": "jsx" // this is the fn that extendscript-ui exports! } // ...any other options }