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:

  1. A project folder on your Mac. This holds the widget's source code. Claude creates it for you by cloning a starter kit.
  2. A dev server. One terminal command (npm start) serves the widget at http://localhost:1234/. While it's running, every time the code changes the web viewer updates instantly.
  3. 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.

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:

KitUse it forRepo
Vanilla JSCharts, tables, formatted displays. Simple widgets, and practice projects.js-dev-environment-new
ReactWidgets 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 clone copies the starter kit into a new folder named after your widget.
  • rm -rf myWidget/.git disconnects it from the starter kit's history so your widget is its own project.
  • npm install downloads the libraries the kit needs. This takes a minute and creates a large node_modules folder — 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:

  1. Open JsDev.fmp12 and open Scripts → Script Workspace (⌘⇧S).
  2. Select all the scripts in the list (click the first, -click the last).
  3. Copy them (⌘C).
  4. 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 assume web. Give the web viewer in your file the same name.
  • Where the built HTML lands. The UploadToHTML script writes the finished widget somewhere in the file — a field or a table. Open that script in JsDev and 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 Data and WV Event come 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

CommandWhat it does
npm startStarts the dev server at http://localhost:1234/. Leave it running while you work.
npm run deploy-to-fmBuilds the widget into one self-contained HTML file and uploads it into FileMaker.
npm run uploadUploads the last build again, without rebuilding.
npm run generate-script-stepsCopies 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" } }
  • type identifies which widget is calling. One Fetch Data script is shared across every widget in the file, so it branches on type to know what to return.
  • query is 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" } }
  • event names what happened. Your script branches on it.
  • data is the payload. Pull values out with JSONGetElement.

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

TermWhat it means
npmThe tool that installs JavaScript libraries and runs project commands. Ships with Node.
dev serverA local web server (npm start) that serves the widget while you build it. Not used in production.
buildCompressing all the source files into one self-contained HTML file for FileMaker.
repo / cloneA project stored on GitHub; cloning copies it to your machine.
node_modulesThe folder of downloaded libraries. Large, auto-generated, never edited.
FMGoferThe library that lets JavaScript call a FileMaker script and wait for the answer.
ReactA JavaScript library for building interfaces that update themselves when data changes.
TanStack QueryHandles fetching, caching, and refreshing data in React widgets.
Previous
Introduction