Using JS in FM
JavaScript Widgets in FileMaker — Getting Started
A plain-English guide for FileMaker developers. You do not need to know JavaScript to follow this. Claude writes the code; this page explains the setup around it, so you know what's happening on your machine and in your FileMaker file.
Read it once. After that, most of it is a one-time setup you never repeat.
The big picture
A "widget" is a small web page — HTML, CSS, and JavaScript — that runs inside a FileMaker web viewer. Building one has three moving parts:
- A project folder on your Mac. This holds the widget's source code. Claude creates it for you by cloning a starter kit.
- A dev server. One terminal command (
npm start) serves the widget athttp://localhost:1234/. While it's running, every time the code changes the web viewer updates instantly. - A FileMaker file. The starter kit ships with
JsDev.fmp12— a small FileMaker file with a layout, a web viewer, and the scripts that make all of this work. You develop against it, then copy its scripts into your own file when you're ready to ship.
The workflow is: build and preview in JsDev.fmp12 → when it looks right, build the widget into a single self-contained HTML file → upload that file into your own FileMaker file, where it runs offline with no dev server needed.
Part 1 — One-time machine setup
You only ever do this once, no matter how many widgets you build.
Node.js
Node is what runs the dev server and the build. Download the LTS version from nodejs.org and install it.
To confirm it worked, open Terminal and run:
node -v
npm -v
Both should print a version number. If you get command not found, the install didn't finish — restart Terminal and try again.
Git
Git is how the starter kit gets copied to your machine. macOS usually has it already:
git --version
If it isn't installed, macOS will offer to install the developer tools for you. Say yes.
A code editor (optional but recommended)
You don't have to edit code by hand — Claude does that — but it helps to be able to look at it. VS Code is free and standard.
FileMaker Pro
You need FileMaker Pro on the same machine, and it needs to be able to open a local .fmp12 file. That's it.
Part 2 — Starting a widget project
Claude runs these steps for you. They're here so you know what happened to your machine.
Two starter kits exist, and Claude picks based on what you're building:
| Kit | Use it for | Repo |
|---|---|---|
| Vanilla JS | Charts, tables, formatted displays. Simple widgets, and practice projects. | js-dev-environment-new |
| React | Widgets that fetch live FileMaker data, manage state, and send events back. | js-dev-react |
Whichever kit is used, the setup is the same shape:
git clone <starter-kit-url> myWidget
rm -rf myWidget/.git
cd myWidget
npm install
git clonecopies the starter kit into a new folder named after your widget.rm -rf myWidget/.gitdisconnects it from the starter kit's history so your widget is its own project.npm installdownloads the libraries the kit needs. This takes a minute and creates a largenode_modulesfolder — that's normal, and you never need to touch it.
Tell the kit about your FileMaker file
Each kit has a small config file — widget.config.js in the React kit, widget.config.cjs in the vanilla kit. Four values:
{
widgetName: "myWidget", // short camelCase name for this widget
server: "$", // "$" means "the file open on this machine"
file: "JsDev", // the FileMaker file to talk to (no .fmp12)
uploadScript: "UploadToHTML",
}
While you're developing, leave file as JsDev. Change it to your own file's name when you're ready to deploy there.
Part 3 — Open the FileMaker dev file ⚠️ don't skip this
The starter kit includes a FileMaker file in the project folder you just created:
- React kit →
JsDev.fmp12 - Vanilla kit →
jsDev.fmp12
Open it in FileMaker Pro. It contains the layout, the web viewer, and every script the widget system depends on.
Then, with npm start running in Terminal, put the file into dev mode (the file has a button or script for this — allow it when prompted). The web viewer on the layout will load http://localhost:1234/ and render your widget live. Change the code, and the web viewer updates.
If the web viewer stays blank, see Troubleshooting.
Part 4 — Copy the scripts into your own file ⚠️ the step everyone forgets
JsDev.fmp12 is a development sandbox. Your widget will eventually live in your file — and your file needs the same scripts, or nothing will work.
Do this once per client file:
- Open
JsDev.fmp12and open Scripts → Script Workspace (⌘⇧S). - Select all the scripts in the list (click the first,
⇧-click the last). - Copy them (
⌘C). - Open your file, open its Script Workspace, and paste (
⌘V).
Copy them all, even the ones you think you won't need — they call each other.
Then check three things in your file:
- Web viewer name. The scripts talk to a web viewer by object name. In
JsDev, click the web viewer, open the Inspector, and note its object name — the callback scripts assumeweb. Give the web viewer in your file the same name. - Where the built HTML lands. The
UploadToHTMLscript writes the finished widget somewhere in the file — a field or a table. Open that script inJsDevand see what it sets, then create the matching field in your file and repoint the script at it. - The two scripts you'll fill in.
Fetch DataandWV Eventcome across as working shells. You add your own logic to them — see Part 6.
Once the scripts are in place, change file in widget.config from JsDev to your file's name.
Part 5 — The daily loop
| Command | What it does |
|---|---|
npm start | Starts the dev server at http://localhost:1234/. Leave it running while you work. |
npm run deploy-to-fm | Builds the widget into one self-contained HTML file and uploads it into FileMaker. |
npm run upload | Uploads the last build again, without rebuilding. |
npm run generate-script-steps | Copies FileMaker script steps to your clipboard, ready to paste (macOS only). |
Day to day it's just: npm start, work with Claude on the widget, watch the web viewer update, npm run deploy-to-fm when you're happy.
To stop the dev server, click in the Terminal window and press Ctrl-C.
What deploy-to-fm actually does
It bundles all the HTML, CSS, and JavaScript into a single dist/index.html with nothing external, then opens an fmp:// URL that runs your UploadToHTML script and hands it that file. FileMaker stores the HTML, and from then on the widget runs from inside your file — no server, no internet.
Part 6 — The two FileMaker scripts your widget talks to
Every widget uses the same two script names. They're a convention, not something you invent per widget.
Fetch Data — the widget reading from FileMaker
The widget calls this whenever it needs data. It always passes an object with two properties:
{ "type": "myWidget", "query": { "status": "active" } }
typeidentifies which widget is calling. OneFetch Datascript is shared across every widget in the file, so it branches ontypeto know what to return.queryis the search or filter criteria. Leave it out entirely to load everything.
One special case: a widget that needs settings from FileMaker on startup sends { "type": "load" }, and the script returns a config object instead of records.
What the script must return. Exit the script with a JSON script result that is a plain array of record objects:
[
{
"recordId": "1",
"fieldData": { "FieldName": "value" },
"portalData": {
"PortalName": [{ "Portal::Field": "value" }]
}
}
]
Not wrapped in { "data": ... } — just the array. If you wrap it, the widget will look empty.
WV Event — the widget sending back to FileMaker
Whenever a click in the widget needs to run a FileMaker script — a checkout, a form submission, a selection — the widget calls WV Event with:
{ "event": "itemSelected", "data": { "id": "123" } }
eventnames what happened. Your script branches on it.datais the payload. Pull values out withJSONGetElement.
In the script: read Get(ScriptParameter), branch on event, then act on data.
There are two flavors, and it's worth deciding up front which you want:
- Fire only — the widget sends the event and moves on immediately. Use this when FileMaker doesn't need to tell the widget anything back.
- Fire and wait — the widget waits for the script to finish and uses whatever it returns. Use this when the widget needs a result: a confirmation number FileMaker generated, refreshed data, a validation failure.
Claude will ask you which one, and will also ask what the widget should do after it sends — clear the form, show a success message, reload, close. Have an answer ready; it changes how the widget is built.
generate-script-steps
Running npm run generate-script-steps puts a block of FileMaker script steps on your clipboard, converted so you can paste them straight into the Script Workspace.
To be clear about what it gives you: it's the callback boilerplate — the Set Variable steps that pull callbackName, promiseID, and parameter out of the script parameter, a Your logic here comment in the middle, and the Perform JavaScript in Web Viewer step at the end that returns the result to the widget. That wrapper is what lets a widget await a FileMaker script.
It does not write your Fetch Data logic. You paste the wrapper, then put your finds and JSON-building in the middle where the comment is.
It uses macOS clipboard tools, so it's Mac-only.
Troubleshooting
The web viewer is blank. Is npm start still running in Terminal? The dev-mode web viewer loads from localhost:1234, so if the server stopped, there's nothing to show. Restart it and refresh the layout.
command not found: npm. Node didn't install, or Terminal was open before it did. Quit Terminal, reopen it, and run node -v again.
The widget works in JsDev.fmp12 but not in my file. Almost always Part 4. Check that all the scripts came across, that the web viewer object name matches, and that UploadToHTML points at a field that exists in your file.
The widget shows no data. Two usual causes. Either Fetch Data isn't returning a plain array (check for a { "data": ... } wrapper), or the script isn't branching on your widget's type yet — a new widget needs a new branch added.
npm run deploy-to-fm doesn't seem to do anything. It works by opening an fmp:// URL. FileMaker Pro has to be running with the target file open, and file in widget.config has to match that file's name exactly, without the .fmp12.
Something changed and now nothing works. From the project folder: rm -rf node_modules && npm install. This reinstalls the libraries cleanly and fixes a surprising share of problems.
Glossary
| Term | What it means |
|---|---|
| npm | The tool that installs JavaScript libraries and runs project commands. Ships with Node. |
| dev server | A local web server (npm start) that serves the widget while you build it. Not used in production. |
| build | Compressing all the source files into one self-contained HTML file for FileMaker. |
| repo / clone | A project stored on GitHub; cloning copies it to your machine. |
node_modules | The folder of downloaded libraries. Large, auto-generated, never edited. |
| FMGofer | The library that lets JavaScript call a FileMaker script and wait for the answer. |
| React | A JavaScript library for building interfaces that update themselves when data changes. |
| TanStack Query | Handles fetching, caching, and refreshing data in React widgets. |