Getting Started

Browser ​

The Browser docs cover the browser JavaScript and CSS used by Formie’s rendered forms.

If you are coming from the Formie plugin docs, this is where to look when you want to understand or customise what happens in the browser: JavaScript events, submit handling, validation, field modules, rendered markup, CSS variables, and the default theme CSS.

There are two common paths:

  • Server-rendered forms - Craft renders the form HTML and Formie’s normal browser assets enhance it. Use these docs to find events, CSS hooks, module behaviour, and markup contracts.
  • Your own browser bundle - You import @verbb/formie-browser, decide when forms mount, and choose whether to load Formie’s CSS or provide your own. Use this path when you disable Formie’s automatic JavaScript, load forms from an endpoint, or need lower-level client control.

If you want to load and display a form through a React, Vue or Web Components component, start with that package’s docs. Those packages provide components for both server-rendered and client-rendered forms.

Installation ​

You only need to install @verbb/formie-browser when your own build imports the package. Server-rendered forms can use the assets emitted by Formie without installing the npm package in your project.

bash
npm install @verbb/formie-browser

Import the shipped CSS if you want the default Formie UI:

ts
import '@verbb/formie-browser/css/formie.css';

If Formie Already Renders the Form ​

For most Craft-rendered forms, Formie outputs the markup, startup script, translations, modules, and CSS links needed in the browser. You do not need to recreate that setup from npm just to listen for events or adjust styling.

Start here when you want to customise what the plugin already provides:

  • JavaScript events for lifecycle, page navigation, validation, and submit events.
  • Submission handling for Ajax results and submit-flow behaviour.
  • UI reference for rendered markup, data attributes, and class hooks.
  • CSS variables for changing the default theme without replacing templates.
  • Modules for field, captcha, address, and payment behaviour.

If you are moving old event listeners or custom scripts from older server-rendered forms, see Migrating from Formie Plugin.

If Your Bundle Initializes Forms ​

Use @verbb/formie-browser when Formie still owns the rendered HTML and browser behaviour, but your browser bundle owns mounting.

Start with formie({ element }). It wraps the lower-level browser client with DOM-ready handling, mounts the matched form elements, and gives you simple success and error hooks.

ts
import { formie } from '@verbb/formie-browser';
import '@verbb/formie-browser/css/formie.css';

await formie({
  element: '#contact-form',
  onReady(instance) {
    console.log('Mounted:', instance.id);
  },
  onSuccess(result) {
    console.log('Submit ok:', result);
  },
  onError(result) {
    console.log('Submit failed:', result);
  },
});

element can be:

  • a CSS selector such as '#contact-form'
  • one DOM element
  • any iterable collection of elements

When you pass a selector, Formie waits until the DOM is ready before it gives up on finding the target.

If Craft still renders the form HTML but you want to control startup timing, use Manual initialisation. If you need custom module registration, explicit mount/unmount control, or endpoint-driven loading, use Custom client.

Options ​

formie() supports these options:

NameTypeRequiredDescription
elementstring | Element | Iterable<Element>YesDOM target or selector to mount.
transport'rest' | 'graphql'NoUse when Formie should fetch HTML payloads itself instead of only enhancing existing rendered markup.
endpointstringNoCraft site base URL for REST, or GraphQL endpoint for GraphQL.
formHandlestringNoForm handle to load when you are not mounting an already rendered form.
payloadFormEndpointPayloadNoPreloaded HTML payload for explicit mounting.
refreshTokensbooleanNoRefresh CSRF, request, render, and captcha tokens as needed.
localestringNoAdvanced locale override.
siteIdnumberNoRequest a specific site.
autoVisiblebooleanNoMount eagerly instead of waiting for visibility-driven initialisation.
theme'formie' | 'none'NoUse the shipped Formie theme or skip it.
themeConfigRecord<string, unknown>NoAdditional browser-theme configuration.
observebooleanNoStart low-level DOM observation after the initial mount.
onReadyfunctionNoCalled after each matched form instance mounts.
onResultfunctionNoCalled for every submit result.
onSuccessfunctionNoCalled when a submit result succeeds.
onErrorfunctionNoCalled when a submit result fails.
onEventfunctionNoCalled for browser lifecycle events exposed by the mounted instance.

Return Value ​

formie() resolves to a small app handle:

NameTypeDescription
clientFormieClientThe underlying advanced browser client.
instancesFormieFormInstance[]Mounted instances currently tracked by the helper.
get(target)functionReturns one mounted instance by selector or element.
rescan()functionRe-runs the element lookup and mounts any new matches.
destroy()functionUnsubscribes listeners, stops observation, and unmounts tracked forms.

Existing Rendered Forms ​

If Craft already rendered the final form HTML into the page and you are initializing from your own bundle, formie() is usually all you need:

html
<form id="contact-form" data-formie-form>
  ...
</form>
ts
await formie({
  element: '#contact-form',
});

On server-rendered pages, Craft can also output Formie's startup script and an inline JSON translations seed for you. formie() is for the cases where you want to take over mounting timing from your own bundle, not for re-creating the whole plugin startup layer.

When You Need the Advanced Client ​

Use the lower-level browser client only when you need a real story for it, such as:

  • custom module registration
  • explicit mount() / unmount() / update() control
  • manual scan() / observe() coordination in a larger app shell
  • migration code that already depends on the full browser client surface

That path lives on Custom client.

If Formie is still outputting its browser script for you and you only need to take over boot timing, use Manual initialisation instead.

Rendering contracts ​

See Rendering Products and Request Profiles to choose how forms load and submit, including when your application and Craft use different domains.

Last updated: Oct 8, 2026, 10:37 AM