Frontend & Headless

Vue and Web Components Starter Walkthrough

Formie provides packages for Vue and Web Components alongside React. This walkthrough uses the official starters to show server-rendered and client-rendered forms, each loading data through REST or GraphQL.

Use the Vue package documentation (opens new window) or Web Components package documentation (opens new window) for the options supported by the frontend you choose.

Prerequisites

  • Formie on Craft with at least one published form
  • Node.js and npm (or pnpm/yarn)
  • Frontend Assets

Package Overview

PackagenpmUse when
@verbb/formie-vueVue 3 bindingsYour app is Vue or Nuxt
@verbb/formie-web-componentsCustom elementsYou want framework-agnostic embeds, or Vue/React coexist on one page
@verbb/formie-browserShared browser packageRequired peer for both packages
@verbb/formie-coreForm definition + stateRequired for client-rendered mode

Install pattern (Vue example):

npm install @verbb/formie-browser @verbb/formie-core @verbb/formie-vue vue

Import CSS once:

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

Vue — Server-Rendered Form

The <FormieForm /> component mirrors the React API. Formie loads the payload, mounts HTML, and owns browser behaviour.

<script setup>
import { FormieForm } from '@verbb/formie-vue';
import '@verbb/formie-browser/css/formie.css';
</script>

<template>
    <FormieForm
        transport="rest"
        endpoint="https://your-craft-site.test"
        form-handle="contactForm"
        theme="formie"
        @submit-success="(result) => console.log(result)"
    />
</template>

Props use kebab-case in templates (form-handle, site-id, static-cache). For GraphQL, set transport="graphql" and point endpoint at your GraphQL URL.

Pre-Fetched Payload

When your app already has the bootstrap payload:

<FormieForm :source="{ payload }" />

Vue — Client-Rendered Form

Use <FormieClientForm /> when you want Vue components for each field:

<script setup>
import { FormieClientForm } from '@verbb/formie-vue';
</script>

<template>
    <FormieClientForm
        transport="graphql"
        endpoint="https://your-craft-site.test/api"
        form-handle="contactForm"
        :components="{
            Field({ field, children, errors }) {
                return h('div', { class: 'field' }, [
                    h('label', field.label),
                    children,
                    ...errors.map((e) => h('p', { class: 'error' }, e)),
                ]);
            },
        }"
    />
</template>

Composable helpers (useFormie, useFormieField, useFormiePage) match the React hooks for custom layouts.

Nuxt

Start from the Nuxt starter (opens new window). It demonstrates:

  • Server-side payload fetching in a route loader
  • Client-only mounting for browser-only APIs (captchas, file uploads)
  • REST and GraphQL transport switching via environment variables

For Nuxt 3+, wrap <FormieForm /> in <ClientOnly> when the page is statically generated and token refresh must run in the browser.

Web Components — Drop-in Embed

Web Components work anywhere you can add a script tag — static HTML, WordPress, legacy PHP, or inside Vue/React without a framework binding layer.

<link rel="stylesheet" href="/path/to/formie.css">

<formie-form
    transport="rest"
    endpoint="https://your-craft-site.test"
    form-handle="contactForm"
></formie-form>

<script type="module">
    import '@verbb/formie-web-components';
</script>

Register the custom element once; then use <formie-form> or <formie-client-form> in markup.

Client-Rendered Custom Element

For app-owned rendering via slots:

<formie-client-form
    transport="graphql"
    endpoint="https://your-craft-site.test/api"
    form-handle="contactForm"
></formie-client-form>

Listen for events on the element:

const form = document.querySelector('formie-form');

form?.addEventListener('formie:submit:result', (event) => {
    console.log(event.detail);
});

Event names match the browser package DOM events (formie:submit:result, formie:page:navigate:after, and so on).

Choosing REST vs GraphQL

TransportProsConsiderations
RESTSimple endpoint, easy local devRequires Formie REST routes enabled
GraphQLTyped schema, single API surfaceNeeds public schema scopes configured

Both transports use the same session token model. Bootstrap via formieClientForm, submit via submitFormieClientForm. See GraphQL submission flow end-to-end.

Static Caching

Pass static-cache attributes on the custom element or props on Vue components:

<FormieForm
    transport="rest"
    endpoint="https://your-craft-site.test"
    form-handle="contactForm"
    :static-cache="true"
    :refresh-tokens="true"
/>
<formie-form
    transport="rest"
    endpoint="https://your-craft-site.test"
    form-handle="contactForm"
    static-cache
    refresh-tokens
></formie-form>

See Cached forms in production.

Starter Repos

Clone and run the official starters to try each package in a working application:

Each starter includes working examples for server-rendered forms, client-rendered forms, REST, and GraphQL. Use them as copy-paste references before building from the package README alone.