Skip to content

Formcentric/client-examples

Repository files navigation

Frontend Client Integration

Monorepo with two aligned Formcentric integration demos:

  • nuxt/ (@formcentric/client-vue)
  • react/ (@formcentric/client-react)

Both apps share the same route behavior, runtime config source, and mostly shared translation keys.

Workspace Quick Start

Install once at repository root:

pnpm install

Run either app in cloud mode:

pnpm -C nuxt dev-cloud
pnpm -C react dev-cloud

Run either app in headless mode:

pnpm -C nuxt dev-headless
pnpm -C react dev-headless

This repo is dev-server oriented for both apps (no app-level build/preview scripts are required for normal usage).

Shared Configuration

Both apps read runtime config from root config/:

  • config/cloud.config.json
  • config/headless.config.json

Example cloud.config.json:

{
  "fcForms": [
    { "id": "5a335e0a-d17b-4530-8934-79e5ebd18c39", "name": "Application" },
    { "id": "00000000-0000-0000-0000-000000000000", "name": "Contact" }
  ],
  "fcUrl": "https://form.formcentric.com/headless-server/"
}

Example headless.config.json:

{
  "fcDataUrl": "http://localhost:8080",
  "fcFormDefinition": "<encrypted form definition>"
}

Routes

Both apps expose:

  • /: landing page (no form mount)
  • /single-form: exactly one mounted form
  • /forms: all configured cloud forms, or one headless form in headless mode

If two cloud IDs are configured in cloud.config.json, /forms renders two forms.

Dev Proxy (Why It Exists)

In cloud mode, Formcentric requests need to hit the configured cloud backend path (usually /headless-server). The local dev proxy exists to keep those requests same-origin from the browser perspective.

What this solves:

  • avoids CORS/preflight friction during local development
  • keeps browser requests on your app origin while forwarding upstream to fcUrl
  • keeps cloud-mode behavior consistent between Nuxt and React

How each app applies it:

  • Nuxt: nitro.devProxy in nuxt/nuxt.config.ts
  • React: server.proxy in react/vite.config.ts

Proxy is enabled in cloud mode and disabled in headless mode.

Form Integration Pattern

Each app intentionally uses only two form components:

  • cloud form component (embed-id based)
  • headless form component (data-url + encrypted definition)

Safe SPA Contract (Universal)

This contract applies to both adapter packages:

  • @formcentric/client-react
  • @formcentric/client-vue

Usage Contract

  • Treat adapters as restart-based integration bridges, not fully controlled form widgets.
  • Use adapters only in client/browser runtime.
  • Provide mount config via top-level adapter props.
  • Built-in restart triggers are:
    • remountKey
    • embedId
    • formDefinition
  • Most other config values are init-time props; changing them does not live-update a running instance.
  • If init-time config changes must apply, bump remountKey.
  • Theme assets are not bundled by the adapters. Provide CSS/template/variables (or runtime URLs) explicitly.
  • onReady / @ready signals completed initialization/start, not guaranteed first visible render.
  • onError / @error is the runtime/init error channel.

Avoidables

  • Do not expect arbitrary prop updates to patch a running form.
  • Do not use framework key changes to force reloads; use remountKey.
  • Do not pass newly created config objects every render and assume they apply live.
  • Do not remount form components due to unrelated UI state if form continuity matters.
  • Do not mix adapter lifecycle with separate manual lifecycle control for the same container.
  • Avoid concurrent mounts with the same embedId unless intentionally designed and tested.

Best Practices

  • Keep config objects stable (useMemo, stable refs/computed patterns).
  • Use an explicit remount policy for language/theme/config changes.
  • Keep routing/lifecycle simple: mount once per view, remount intentionally.
  • Bundle/import theme assets once and keep theme source consistent per app.
  • Use ready/error callbacks/events to drive loading/error UI states.
  • Set explicit container sizing (style/class) to reduce layout jumps.

App-specific Docs

  • Nuxt details: nuxt/README.md
  • React details: react/README.md

About

Embed Formcentric forms in modern frontend SPAs

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages