A toolkit for building Laserfiche Forms customizations with TypeScript, Vite, and reusable components.
Important
This toolkit is not officially supported by Laserfiche and may break with future updates to Laserfiche Forms. Do not open support tickets with Laserfiche for issues related to this toolkit.
This is a monorepo with npm workspaces:
lf-form-builder/
├── docs/ # VitePress guides, recipes, and generated API docs
├── packages/
│ ├── core/ # Published npm package (@lf/lf-form-builder)
│ │ └── src/ # Library source: components, utilities, API helpers, plugins
│ ├── types/ # Published npm package (@lf/lf-form-types)
│ │ └── src/ # LFForm type definitions and helpers
│ └── examples/ # Real-world example forms (not published)
├── scripts/ # Release and workspace helper scripts
├── template/ # Starter template — clone this to start a new project
Copy the starter template and start building:
npx degit laserfiche/lf-form-builder/template my-forms-project
cd my-forms-project
# To use locally built (see: Development section below) dependencies, update the new project package.json
# "dependencies": {
# "@lf/lf-form-builder": "file:../packages/core",
# "@lf/lf-form-types": "file:../packages/types"
# }
npm install
npm run devSee template/README.md for full documentation.
git clone https://github.com/Laserfiche/lf-form-builder.git
cd lf-form-builder
npm install
copy .env.example .env.local
npm run build:core # Build the core library
npm run build:examples # Build example forms
npm run build:dev:css # Dev build + compile emitted .less to .css for static serving
npm run dev # Watch examples and serve built dist locallyUse .env.example as the committed template for local configuration. Keep actual keys and per-machine overrides in the untracked .env.local.
After changing .env.local, rebuild the examples bundle or restart npm run dev. The VITE_* values are embedded into the generated JavaScript at build time, so updating the env file alone does not change an already-built packages/examples/dist/*.js output.
If you use the translation examples, set VITE_TRANSLATION_ENDPOINT to a server-side route that performs translation. Do not expose provider API keys in browser code.
For live rebuilds when editing the library or examples, use the repository watch script. It rebuilds core, examples, and re-compiles emitted .less files automatically on save:
npm install # first-time only
npm run watch # rebuilds on file changesUse the workflow that matches what you are testing:
npm run dev— runs the examples workspacewatch+servescripts. This watches the example build output and servespackages/examples/distonhttp://localhost:3000when that port is available.npm run build:dev:css— one-shot development build that emits bothEmpower2026.jsandEmpower2026.cssintopackages/examples/dist.npm run watch-serve— runs the core watcher and a Vite development server onhttp://localhost:3000with--strictPort. Use this when you want Vite-hosted development output on a fixed port.
If you want the built dist directory served manually from the examples workspace:
# build once (dev mode) and emit matching .css files into dist
npm run build:dev:css
# serve the built dist on port 3000
npm --workspace=packages/examples run serveNotes:
npm run devuses the examples package's staticservecommand. If port3000is already in use,servewill choose another port.npm run build:dev:cssis the easiest one-shot build when you want the static index page to list bothEmpower2026.jsandEmpower2026.css.npm run watch-serveuses Vite on--strictPort, so it will fail instead of switching ports if3000is already occupied.- If you update
.env.local, run a fresh build or restartnpm run devso the newVITE_*values are embedded into the built JavaScript. - If
Empower2026.jslooks truncated, stop the server, run thebuild:devorbuild:dev:cssstep to completion, then restart serving.
This is useful during development so you don't need to run npm run build:core manually after each change.
- Quick Start — Writing form scripts with the LFForm API
- LFForm API Navigation — Browse the main LFForm API pages in a linear order, then drill into auxiliary types
- Template & Toolchain Setup — Build with Vite and the npm packages
- Custom HTML & Sandbox — Custom HTML, third-party libraries, and iframes
- Recipes — Copy-paste patterns for common form tasks
- Payment Gateways — Setting up Stripe, Braintree, or Authorize.net checkout on a form
- API Reference — Generated TypeDoc for
@lf/lf-form-builderand@lf/lf-form-types
The core library published to npm. Import utilities in your form code:
import { findField, LFFormModal, fullFieldHtml } from '@lf/lf-form-builder';Import Vite plugins in your build config:
import { bundleLfless, disableSharedChunking } from '@lf/lf-form-builder/plugins';Included modules:
- Field utilities —
findField,findFieldByIdParam,findFieldOrNull - Field rules —
LFFormFieldRules(chainable show/hide/CSS class actions) - Safe field helpers —
showFieldSafe,hideFieldSafe,setFieldValueSafe - Templates —
lfjsx(reactive field content templates) - Async and general utilities —
waitWithTimeout,throttle,setCustomHtml - Messaging utilities —
PostMessageHelperand related message types - Table utilities —
fillTableWithGenericResults,setTableFieldValues,generateCSV,makeCSVDownloadButton,makeDownloadTriggerButton,updateTableRows - API helpers —
getRepositories,resolveEntryIdField,resolveDefaultRepositoryAPIOptions,searchAsync,fillTableWithSearchResults,defaultSearchOptions,mapEntryToForm,isFieldIdEqualField,patchEntryMetadata,AdobeSignApi - Components —
LFFormModal,generateFullFieldHtml,fullFieldHtml,fieldFormatter,registerStarHandler,registerAllStarComponents,handleVoteChange,onVoteChange,makeLoadingBar,registerFirstTimeLoad - Google Maps —
initGoogleMapsAutocomplete,destroyGoogleMapsAutocomplete,buildLFAddress,initAddressValidation,destroyAddressValidation,sendValidationFeedback,validateAddress,validateFeedback - Repository —
DocView,IframeView - Vite plugins —
bundleLfless,disableSharedChunking,generateDirectoryHtml - CSS — LESS variables, mixins, and theme files via
@lf/lf-form-builder/css/*
See packages/core/README.md for package-level usage and plugin details.
TypeScript definitions for the LFForm runtime API, including focused subpath exports for the main LFForm API and auxiliary helper types.
import type { LFForm, LFFormEventApi, LFFormGetterApi, LFFormMethodApi } from '@lf/lf-form-types/lfform';
import type { LFFormSupportedEvents } from '@lf/lf-form-types/events';
import { isLfFormId } from '@lf/lf-form-types/utils';See packages/types/README.md and docs/guide/lfform-api-navigation.md for the LFForm-first docs structure.
A minimal starter project configured with Vite, @lf/lf-form-builder, and @lf/lf-form-types. Copy this directory to start a new forms project.
Real-world form implementations demonstrating the library in action:
- Empower2025 — Multi-feature form: modals, star ratings, table HTML injection
- Empower2026 — lookup-driven loading and CSV export, Google Maps autocomplete with address validation, modal-based checkout via Stripe, Braintree, or Authorize.net, cross-iframe messaging
- TranslateForm — static form localization with optional translation draft generation
Empower2026 ships three interchangeable payment-gateway pages, each independently toggleable via a VITE_DISABLE_PAGE* flag in .env.local:
| Page | File | Gateway | Feature flag |
|---|---|---|---|
| Page 3 | page3Payment.ts | Stripe (embedded checkout) | VITE_DISABLE_PAGE3 |
| Page 4 | page4BraintreePayment.ts | Braintree (Drop-in UI) | VITE_DISABLE_PAGE4 |
| Page 5 | page5AuthorizeNetPayment.ts | Authorize.net (Accept.js) | VITE_DISABLE_PAGE5 |
See docs/recipes/payment-gateways.md for the full setup and field-mapping guide for all three gateways.
Before using the Empower2026 example in your own Forms account, import the bundled Laserfiche assets from packages/examples/src/Forms/Empower2026/Laserfiche Process:
- Import the process definition: AdvancedScripting_RG.xml
- For Stripe (page 3), import the web request rules: RGWebRequestToStripeCharge.bri and RGWebRequestToStripeVerification.bri
- For Braintree (page 4), import: RGWebRequestToBraintreePaymentIInit.bri (creates the client_token) and RGWebRequestToBraintreeCharge.bri (charges the nonce)
- For Authorize.net (page 5), import: RGWebRequestToAuthNetCharge.bri and RGWebRequestToAuthNetVerification.bri
You only need to import the web request rules for the gateway(s) you intend to use; disable the others via their VITE_DISABLE_PAGE* flag.
Those imports are required for the example's process actions and gateway-related web requests to work in your environment.
The bundled process assets are configured for local development and expect the example scripts to be served from http://localhost:3000. That is appropriate for getting the demo running quickly on a developer machine.
For the payment gateways specifically, do not copy only Empower2026.js. Each checkout flow injects an iframe pointed back at the currently-loaded page (with the built script re-delivered into that iframe over postMessage), so Empower2026.js must be reachable at the same URL the form iframe loads — there is no separate stripe.html/Stripe.js asset to copy alongside it. In local development that URL is served from packages/examples/dist, and in production it should be hosted on the same HTTPS location referenced by the form.
For production, do not leave the process pointed at localhost. Host the built JavaScript, CSS, and related assets on a secure HTTPS site that your Forms environment can reach, then update the imported process and form configuration to reference that hosted location.
Before preparing a release, validate the workspace builds:
npm run build:types
npm run build:core
npm run build:examplesThen run:
npm run releaseThis script bumps the published package versions, aligns internal dependency ranges used by the examples and template, creates a release commit, and tags the version.